Moved tests over to Kotlin. (#1428)

This commit is contained in:
Paul Hawke 2018-04-08 02:59:20 -05:00 committed by Josephine Lim
parent 6b2dd8c1df
commit 22772c851e
29 changed files with 1453 additions and 1679 deletions

View file

@ -0,0 +1,33 @@
package fr.free.nrw.commons
import fr.free.nrw.commons.upload.FileUtils
import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.*
class FileUtilsTest {
@Test
fun copiedFileIsIdenticalToSource() {
val source = File.createTempFile("temp", "")
val dest = File.createTempFile("temp", "")
writeToFile(source, "Hello, World")
FileUtils.copy(FileInputStream(source), FileOutputStream(dest))
assertEquals(getString(source), getString(dest))
}
private fun writeToFile(file: File, s: String) {
val buf = BufferedOutputStream(FileOutputStream(file))
buf.write(s.toByteArray())
buf.close()
}
private fun getString(file: File): String {
val bytes = ByteArray(file.length().toInt())
val buf = BufferedInputStream(FileInputStream(file))
buf.read(bytes, 0, bytes.size)
buf.close()
return String(bytes)
}
}

View file

@ -0,0 +1,64 @@
package fr.free.nrw.commons
import fr.free.nrw.commons.location.LatLng
import org.junit.Assert.assertEquals
import org.junit.Test
class LatLngTests {
@Test
fun testZeroZero() {
val place = LatLng(0.0, 0.0, 0f)
assertPrettyCoordinateString("0.0 N, 0.0 E", place)
}
@Test
fun testAntipode() {
val place = LatLng(0.0, 180.0, 0f)
assertPrettyCoordinateString("0.0 N, 180.0 W", place)
}
@Test
fun testNorthPole() {
val place = LatLng(90.0, 0.0, 0f)
assertPrettyCoordinateString("90.0 N, 0.0 E", place)
}
@Test
fun testSouthPole() {
val place = LatLng(-90.0, 0.0, 0f)
assertPrettyCoordinateString("90.0 S, 0.0 E", place)
}
@Test
fun testLargerNumbers() {
val place = LatLng(120.0, 380.0, 0f)
assertPrettyCoordinateString("90.0 N, 20.0 E", place)
}
@Test
fun testNegativeNumbers() {
val place = LatLng(-120.0, -30.0, 0f)
assertPrettyCoordinateString("90.0 S, 30.0 W", place)
}
@Test
fun testTooBigWestValue() {
val place = LatLng(20.0, -190.0, 0f)
assertPrettyCoordinateString("20.0 N, 170.0 E", place)
}
@Test
fun testRounding() {
val place = LatLng(0.1234567, -0.33333333, 0f)
assertPrettyCoordinateString("0.1235 N, 0.3333 W", place)
}
@Test
fun testRoundingAgain() {
val place = LatLng(-0.000001, -0.999999, 0f)
assertPrettyCoordinateString("0.0 S, 1.0 W", place)
}
private fun assertPrettyCoordinateString(expected: String, place: LatLng) =
assertEquals(expected, place.prettyCoordinateString)
}

View file

@ -0,0 +1,46 @@
package fr.free.nrw.commons
import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.utils.LengthUtils
import org.junit.Assert.assertEquals
import org.junit.Test
class LengthUtilsTest {
@Test
fun testZeroDistance() {
val pointA = LatLng(0.0, 0.0, 0f)
val pointB = LatLng(0.0, 0.0, 0f)
assertDistanceBetween("0m", pointA, pointB)
}
@Test
fun testOneDegreeOnEquator() {
val pointA = LatLng(0.0, 0.0, 0f)
val pointB = LatLng(0.0, 1.0, 0f)
assertDistanceBetween("111.2km", pointA, pointB)
}
@Test
fun testOneDegreeFortyFiveDegrees() {
val pointA = LatLng(45.0, 0.0, 0f)
val pointB = LatLng(45.0, 1.0, 0f)
assertDistanceBetween("78.6km", pointA, pointB)
}
@Test
fun testOneDegreeSouthPole() {
val pointA = LatLng(-90.0, 0.0, 0f)
val pointB = LatLng(-90.0, 1.0, 0f)
assertDistanceBetween("0m", pointA, pointB)
}
@Test
fun testPoleToPole() {
val pointA = LatLng(90.0, 0.0, 0f)
val pointB = LatLng(-90.0, 0.0, 0f)
assertDistanceBetween("20,015.1km", pointA, pointB)
}
private fun assertDistanceBetween(expected: String, pointA: LatLng, pointB: LatLng) =
assertEquals(expected, LengthUtils.formatDistanceBetween(pointA, pointB))
}

View file

@ -0,0 +1,23 @@
package fr.free.nrw.commons
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class)
class MediaTest {
@Test
fun displayTitleShouldStripExtension() {
val m = Media("File:Example.jpg")
assertEquals("Example", m.displayTitle)
}
@Test
fun displayTitleShouldUseSpaceForUnderscore() {
val m = Media("File:Example 1_2.jpg")
assertEquals("Example 1 2", m.displayTitle)
}
}

View file

@ -0,0 +1,35 @@
package fr.free.nrw.commons
import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.nearby.NearbyController.loadAttractionsFromLocationToBaseMarkerOptions
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class)
class NearbyControllerTest {
@Test
fun testNullAttractions() {
val location = LatLng(0.0, 0.0, 0f)
val options = loadAttractionsFromLocationToBaseMarkerOptions(
location, null, RuntimeEnvironment.application)
assertEquals(0, options.size.toLong())
}
@Test
fun testEmptyList() {
val location = LatLng(0.0, 0.0, 0f)
val options = loadAttractionsFromLocationToBaseMarkerOptions(
location, emptyList(), RuntimeEnvironment.application)
assertEquals(0, options.size.toLong())
}
}

View file

@ -0,0 +1,67 @@
package fr.free.nrw.commons
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.net.URLEncoder
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class)
class PageTitleTest {
@Test
fun displayTextShouldNotBeUnderscored() {
val pageTitle = PageTitle("Ex_1 ")
assertEquals("Ex 1", pageTitle.displayText)
}
@Test
fun moreThanTwoColons() {
val pageTitle = PageTitle("File:sample:a.jpg")
assertEquals("File:Sample:a.jpg", pageTitle.prefixedText)
}
@Test
fun getTextShouldReturnWithoutNamespace() {
val pageTitle = PageTitle("File:sample.jpg")
assertEquals("Sample.jpg", pageTitle.text)
}
@Test
fun capitalizeNameAfterNamespace() {
val pageTitle = PageTitle("File:sample.jpg")
assertEquals("File:Sample.jpg", pageTitle.prefixedText)
}
@Test
fun prefixedTextShouldBeUnderscored() {
val pageTitle = PageTitle("Ex 1 ")
assertEquals("Ex_1", pageTitle.prefixedText)
}
@Test
fun getMobileUriForTest() {
val pageTitle = PageTitle("Test")
assertEquals(BuildConfig.MOBILE_HOME_URL + "Test", pageTitle.mobileUri.toString())
}
@Test
fun spaceBecomesUnderscoreInUri() {
val pageTitle = PageTitle("File:Ex 1.jpg")
assertEquals(BuildConfig.HOME_URL + "File:Ex_1.jpg", pageTitle.canonicalUri.toString())
}
@Test
fun leaveSubpageNamesUncapitalizedInUri() {
val pageTitle = PageTitle("User:Ex/subpage")
assertEquals(BuildConfig.HOME_URL + "User:Ex/subpage", pageTitle.canonicalUri.toString())
}
@Test
fun unicodeUri() {
val pageTitle = PageTitle("User:例")
assertEquals(BuildConfig.HOME_URL + "User:" + URLEncoder.encode("", "utf-8"), pageTitle.canonicalUri.toString())
}
}

View file

@ -0,0 +1,72 @@
package fr.free.nrw.commons
import android.content.Context
import android.content.SharedPreferences
import android.support.v4.util.LruCache
import com.nhaarman.mockito_kotlin.mock
import com.squareup.leakcanary.RefWatcher
import fr.free.nrw.commons.auth.AccountUtil
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.caching.CacheController
import fr.free.nrw.commons.data.DBOpenHelper
import fr.free.nrw.commons.di.CommonsApplicationComponent
import fr.free.nrw.commons.di.CommonsApplicationModule
import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent
import fr.free.nrw.commons.location.LocationServiceManager
import fr.free.nrw.commons.mwapi.MediaWikiApi
import fr.free.nrw.commons.nearby.NearbyPlaces
import fr.free.nrw.commons.upload.UploadController
class TestCommonsApplication : CommonsApplication() {
private var mockApplicationComponent: CommonsApplicationComponent? = null
override fun onCreate() {
if (mockApplicationComponent == null) {
mockApplicationComponent = DaggerCommonsApplicationComponent.builder()
.appModule(MockCommonsApplicationModule(this)).build()
}
super.onCreate()
}
// No leakcanary in unit tests.
override fun setupLeakCanary(): RefWatcher = RefWatcher.DISABLED
}
class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModule(appContext) {
val accountUtil: AccountUtil = mock()
val appSharedPreferences: SharedPreferences = mock()
val defaultSharedPreferences: SharedPreferences = mock()
val otherSharedPreferences: SharedPreferences = mock()
val uploadController: UploadController = mock()
val mockSessionManager: SessionManager = mock()
val mediaWikiApi: MediaWikiApi = mock()
val locationServiceManager: LocationServiceManager = mock()
val cacheController: CacheController = mock()
val mockDbOpenHelper: DBOpenHelper = mock()
val nearbyPlaces: NearbyPlaces = mock()
val lruCache: LruCache<String, String> = mock()
override fun providesAccountUtil(context: Context): AccountUtil = accountUtil
override fun providesApplicationSharedPreferences(context: Context): SharedPreferences = appSharedPreferences
override fun providesDefaultSharedPreferences(context: Context): SharedPreferences = defaultSharedPreferences
override fun providesOtherSharedPreferences(context: Context): SharedPreferences = otherSharedPreferences
override fun providesUploadController(sessionManager: SessionManager, sharedPreferences: SharedPreferences, context: Context): UploadController = uploadController
override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager
override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences): MediaWikiApi = mediaWikiApi
override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager
override fun provideCacheController(): CacheController = cacheController
override fun provideDBOpenHelper(context: Context): DBOpenHelper = mockDbOpenHelper
override fun provideNearbyPlaces(): NearbyPlaces = nearbyPlaces
override fun provideLruCache(): LruCache<String, String> = lruCache
}

View file

@ -0,0 +1,68 @@
package fr.free.nrw.commons
import fr.free.nrw.commons.Utils.fixExtension
import org.junit.Assert.assertEquals
import org.junit.Test
class UtilsFixExtensionTest {
@Test
fun jpegResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpeg", "jpeg"))
}
@Test
fun capitalJpegWithNoHintResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.JPEG", null))
}
@Test
fun jpegWithBogusHintResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpeg", null))
}
@Test
fun jpegToCapitalJpegResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpeg", "JPEG"))
}
@Test
fun jpgToJpegResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpg", "jpeg"))
}
@Test
fun jpegToJpgResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpeg", "jpg"))
}
@Test
fun jpgRemainsJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpg", "jpg"))
}
@Test
fun pngRemainsPng() {
assertEquals("SampleFile.png", fixExtension("SampleFile.png", "png"))
}
@Test
fun jpgHintResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile", "jpg"))
}
@Test
fun jpegHintResultsInJpg() {
assertEquals("SampleFile.jpg", fixExtension("SampleFile", "jpeg"))
}
@Test
fun dotLessJpgToJpgResultsInJpg() {
assertEquals("SAMPLEjpg.jpg", fixExtension("SAMPLEjpg", "jpg"))
}
@Test
fun inWordJpegToJpgResultsInJpg() {
assertEquals("X.jpeg.SAMPLE.jpg", fixExtension("X.jpeg.SAMPLE", "jpg"))
}
}

View file

@ -0,0 +1,257 @@
package fr.free.nrw.commons.category
import android.content.ContentProviderClient
import android.content.ContentValues
import android.database.Cursor
import android.database.MatrixCursor
import android.database.sqlite.SQLiteDatabase
import android.os.RemoteException
import com.nhaarman.mockito_kotlin.*
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.category.CategoryContentProvider.BASE_URI
import fr.free.nrw.commons.category.CategoryContentProvider.uriForId
import fr.free.nrw.commons.category.CategoryDao.Table.*
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.util.*
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class)
class CategoryDaoTest {
private val columns = arrayOf(COLUMN_ID, COLUMN_NAME, COLUMN_LAST_USED, COLUMN_TIMES_USED)
private val client: ContentProviderClient = mock()
private val database: SQLiteDatabase = mock()
private val captor = argumentCaptor<ContentValues>()
private val queryCaptor = argumentCaptor<Array<String>>()
private lateinit var testObject: CategoryDao
@Before
fun setUp() {
testObject = CategoryDao { client }
}
@Test
fun createTable() {
onCreate(database)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun deleteTable() {
onDelete(database)
inOrder(database) {
verify(database).execSQL(DROP_TABLE_STATEMENT)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
}
@Test
fun migrateTableVersionFrom_v1_to_v2() {
onUpdate(database, 1, 2)
// Table didnt exist before v5
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v2_to_v3() {
onUpdate(database, 2, 3)
// Table didnt exist before v5
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v3_to_v4() {
onUpdate(database, 3, 4)
// Table didnt exist before v5
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v4_to_v5() {
onUpdate(database, 4, 5)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun migrateTableVersionFrom_v5_to_v6() {
onUpdate(database, 5, 6)
// Table didnt change in version 6
verifyZeroInteractions(database)
}
@Test
fun createFromCursor() {
createCursor(1).let { cursor ->
cursor.moveToFirst()
testObject.fromCursor(cursor).let {
assertEquals(uriForId(1), it.contentUri)
assertEquals("foo", it.name)
assertEquals(123, it.lastUsed.time)
assertEquals(2, it.timesUsed)
}
}
}
@Test
fun saveExistingCategory() {
createCursor(1).let {
val category = testObject.fromCursor(it.apply { moveToFirst() })
testObject.save(category)
verify(client).update(eq(category.contentUri), captor.capture(), isNull(), isNull())
captor.firstValue.let { cv ->
assertEquals(3, cv.size())
assertEquals(category.name, cv.getAsString(COLUMN_NAME))
assertEquals(category.lastUsed.time, cv.getAsLong(COLUMN_LAST_USED))
assertEquals(category.timesUsed, cv.getAsInteger(COLUMN_TIMES_USED))
}
}
}
@Test
fun saveNewCategory() {
val contentUri = CategoryContentProvider.uriForId(111)
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val category = Category(null, "foo", Date(234L), 1)
testObject.save(category)
verify(client).insert(eq(BASE_URI), captor.capture())
captor.firstValue.let { cv ->
assertEquals(3, cv.size())
assertEquals(category.name, cv.getAsString(COLUMN_NAME))
assertEquals(category.lastUsed.time, cv.getAsLong(COLUMN_LAST_USED))
assertEquals(category.timesUsed, cv.getAsInteger(COLUMN_TIMES_USED))
assertEquals(contentUri, category.contentUri)
}
}
@Test(expected = RuntimeException::class)
fun testSaveTranslatesRemoteExceptions() {
whenever(client.insert(isA(), isA())).thenThrow(RemoteException(""))
testObject.save(Category())
}
@Test
fun whenTheresNoDataFindReturnsNull_nullCursor() {
whenever(client.query(any(), any(), any(), any(), any())).thenReturn(null)
assertNull(testObject.find("foo"))
}
@Test
fun whenTheresNoDataFindReturnsNull_emptyCursor() {
whenever(client.query(any(), any(), any(), any(), any())).thenReturn(createCursor(0))
assertNull(testObject.find("foo"))
}
@Test
fun cursorsAreClosedAfterUse() {
val mockCursor: Cursor = mock()
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(mockCursor)
whenever(mockCursor.moveToFirst()).thenReturn(false)
testObject.find("foo")
verify(mockCursor).close()
}
@Test
fun findCategory() {
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1))
val category = testObject.find("foo")
assertNotNull(category)
assertEquals(uriForId(1), category?.contentUri)
assertEquals("foo", category?.name)
assertEquals(123L, category?.lastUsed?.time)
assertEquals(2, category?.timesUsed)
verify(client).query(
eq(BASE_URI),
eq(ALL_FIELDS),
eq("$COLUMN_NAME=?"),
queryCaptor.capture(),
isNull()
)
assertEquals("foo", queryCaptor.firstValue[0])
}
@Test(expected = RuntimeException::class)
fun findCategoryTranslatesExceptions() {
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenThrow(RemoteException(""))
testObject.find("foo")
}
@Test(expected = RuntimeException::class)
fun recentCategoriesTranslatesExceptions() {
whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenThrow(RemoteException(""))
testObject.recentCategories(1)
}
@Test
fun recentCategoriesReturnsEmptyList_nullCursor() {
whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(null)
assertTrue(testObject.recentCategories(1).isEmpty())
}
@Test
fun recentCategoriesReturnsEmptyList_emptyCursor() {
whenever(client.query(any(), any(), any(), any(), any())).thenReturn(createCursor(0))
assertTrue(testObject.recentCategories(1).isEmpty())
}
@Test
fun cursorsAreClosedAfterRecentCategoriesQuery() {
val mockCursor: Cursor = mock()
whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(mockCursor)
whenever(mockCursor.moveToFirst()).thenReturn(false)
testObject.recentCategories(1)
verify(mockCursor).close()
}
@Test
fun recentCategoriesReturnsLessThanLimit() {
whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(1))
val result = testObject.recentCategories(10)
assertEquals(1, result.size)
assertEquals("foo", result[0])
verify(client).query(
eq(BASE_URI),
eq(ALL_FIELDS),
isNull(),
queryCaptor.capture(),
eq("$COLUMN_LAST_USED DESC")
)
assertEquals(0, queryCaptor.firstValue.size)
}
@Test
fun recentCategoriesHonorsLimit() {
whenever(client.query(any(), any(), anyOrNull(), any(), any())).thenReturn(createCursor(10))
val result = testObject.recentCategories(5)
assertEquals(5, result.size)
}
private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply {
for (i in 0 until rowCount) {
addRow(listOf("1", "foo", "123", "2"))
}
}
}

View file

@ -0,0 +1,335 @@
package fr.free.nrw.commons.contributions
import android.content.ContentProviderClient
import android.content.ContentValues
import android.database.MatrixCursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uri
import android.os.RemoteException
import com.nhaarman.mockito_kotlin.*
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.contributions.Contribution.*
import fr.free.nrw.commons.contributions.ContributionDao.Table
import fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI
import fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.util.*
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class)
class ContributionDaoTest {
private val localUri = "http://example.com/"
private val client: ContentProviderClient = mock()
private val database: SQLiteDatabase = mock()
private val captor = argumentCaptor<ContentValues>()
private lateinit var contentUri: Uri
private lateinit var testObject: ContributionDao
@Before
fun setUp() {
contentUri = uriForId(111)
testObject = ContributionDao { client }
}
@Test
fun createTable() {
Table.onCreate(database)
verify(database).execSQL(Table.CREATE_TABLE_STATEMENT)
}
@Test
fun deleteTable() {
Table.onDelete(database)
inOrder(database) {
verify(database).execSQL(Table.DROP_TABLE_STATEMENT)
verify(database).execSQL(Table.CREATE_TABLE_STATEMENT)
}
}
@Test
fun upgradeDatabase_v1_to_v2() {
Table.onUpdate(database, 1, 2)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(Table.ADD_DESCRIPTION_FIELD)
verify<SQLiteDatabase>(database).execSQL(Table.ADD_CREATOR_FIELD)
}
}
@Test
fun upgradeDatabase_v2_to_v3() {
Table.onUpdate(database, 2, 3)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(Table.ADD_MULTIPLE_FIELD)
verify<SQLiteDatabase>(database).execSQL(Table.SET_DEFAULT_MULTIPLE)
}
}
@Test
fun upgradeDatabase_v3_to_v4() {
Table.onUpdate(database, 3, 4)
// No changes
verifyZeroInteractions(database)
}
@Test
fun upgradeDatabase_v4_to_v5() {
Table.onUpdate(database, 4, 5)
// No changes
verifyZeroInteractions(database)
}
@Test
fun upgradeDatabase_v5_to_v6() {
Table.onUpdate(database, 5, 6)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(Table.ADD_WIDTH_FIELD)
verify<SQLiteDatabase>(database).execSQL(Table.SET_DEFAULT_WIDTH)
verify<SQLiteDatabase>(database).execSQL(Table.ADD_HEIGHT_FIELD)
verify<SQLiteDatabase>(database).execSQL(Table.SET_DEFAULT_HEIGHT)
verify<SQLiteDatabase>(database).execSQL(Table.ADD_LICENSE_FIELD)
verify<SQLiteDatabase>(database).execSQL(Table.SET_DEFAULT_LICENSE)
}
}
@Test
fun saveNewContribution_nonNullFields() {
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val contribution = createContribution(true, null, null, null, null)
testObject.save(contribution)
assertEquals(contentUri, contribution.contentUri)
verify(client).insert(eq(BASE_URI), captor.capture())
captor.firstValue.let {
// Long fields
assertEquals(222L, it.getAsLong(Table.COLUMN_LENGTH))
assertEquals(321L, it.getAsLong(Table.COLUMN_TIMESTAMP))
assertEquals(333L, it.getAsLong(Table.COLUMN_TRANSFERRED))
// Integer fields
assertEquals(STATE_COMPLETED, it.getAsInteger(Table.COLUMN_STATE))
assertEquals(640, it.getAsInteger(Table.COLUMN_WIDTH))
assertEquals(480, it.getAsInteger(Table.COLUMN_HEIGHT))
// String fields
assertEquals(SOURCE_CAMERA, it.getAsString(Table.COLUMN_SOURCE))
assertEquals("desc", it.getAsString(Table.COLUMN_DESCRIPTION))
assertEquals("create", it.getAsString(Table.COLUMN_CREATOR))
assertEquals("007", it.getAsString(Table.COLUMN_LICENSE))
}
}
@Test
fun saveNewContribution_nullableFieldsAreNull() {
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val contribution = createContribution(true, null, null, null, null)
testObject.save(contribution)
assertEquals(contentUri, contribution.contentUri)
verify(client).insert(eq(BASE_URI), captor.capture())
captor.firstValue.let {
// Nullable fields are absent if null
assertFalse(it.containsKey(Table.COLUMN_LOCAL_URI))
assertFalse(it.containsKey(Table.COLUMN_IMAGE_URL))
assertFalse(it.containsKey(Table.COLUMN_UPLOADED))
}
}
@Test
fun saveNewContribution_nullableImageUrlUsesFileAsBackup() {
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val contribution = createContribution(true, null, null, null, "file")
testObject.save(contribution)
assertEquals(contentUri, contribution.contentUri)
verify(client).insert(eq(BASE_URI), captor.capture())
captor.firstValue.let {
// Nullable fields are absent if null
assertFalse(it.containsKey(Table.COLUMN_LOCAL_URI))
assertFalse(it.containsKey(Table.COLUMN_UPLOADED))
assertEquals(Utils.makeThumbBaseUrl("file"), it.getAsString(Table.COLUMN_IMAGE_URL))
}
}
@Test
fun saveNewContribution_nullableFieldsAreNonNull() {
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val contribution = createContribution(true, Uri.parse(localUri),
"image", Date(456L), null)
testObject.save(contribution)
assertEquals(contentUri, contribution.contentUri)
verify(client).insert(eq(BASE_URI), captor.capture())
captor.firstValue.let {
assertEquals(localUri, it.getAsString(Table.COLUMN_LOCAL_URI))
assertEquals("image", it.getAsString(Table.COLUMN_IMAGE_URL))
assertEquals(456L, it.getAsLong(Table.COLUMN_UPLOADED))
}
}
@Test
fun saveNewContribution_booleanEncodesTrue() {
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val contribution = createContribution(true, null, null, null, null)
testObject.save(contribution)
assertEquals(contentUri, contribution.contentUri)
verify(client).insert(eq(BASE_URI), captor.capture())
// Boolean true --> 1 for ths encoding scheme
assertEquals("Boolean true should be encoded as 1", 1,
captor.firstValue.getAsInteger(Table.COLUMN_MULTIPLE))
}
@Test
fun saveNewContribution_booleanEncodesFalse() {
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
val contribution = createContribution(false, null, null, null, null)
testObject.save(contribution)
assertEquals(contentUri, contribution.contentUri)
verify(client).insert(eq(BASE_URI), captor.capture())
// Boolean true --> 1 for ths encoding scheme
assertEquals("Boolean false should be encoded as 0", 0,
captor.firstValue.getAsInteger(Table.COLUMN_MULTIPLE))
}
@Test
fun saveExistingContribution() {
val contribution = createContribution(false, null, null, null, null)
contribution.contentUri = contentUri
testObject.save(contribution)
verify(client).update(eq(contentUri), isA(), isNull(), isNull())
}
@Test(expected = RuntimeException::class)
fun saveTranslatesExceptions() {
whenever(client.insert(isA(), isA())).thenThrow(RemoteException(""))
testObject.save(createContribution(false, null, null, null, null))
}
@Test(expected = RuntimeException::class)
fun deleteTranslatesExceptions() {
whenever(client.delete(anyOrNull(), anyOrNull(), anyOrNull())).thenThrow(RemoteException(""))
val contribution = createContribution(false, null, null, null, null)
contribution.contentUri = contentUri
testObject.delete(contribution)
}
@Test(expected = RuntimeException::class)
fun exceptionThrownWhenAttemptingToDeleteUnsavedContribution() {
testObject.delete(createContribution(false, null, null, null, null))
}
@Test
fun deleteExistingContribution() {
val contribution = createContribution(false, null, null, null, null)
contribution.contentUri = contentUri
testObject.delete(contribution)
verify(client).delete(eq(contentUri), isNull(), isNull())
}
@Test
fun createFromCursor() {
val created = 321L
val uploaded = 456L
createCursor(created, uploaded, false, localUri).let { mc ->
testObject.fromCursor(mc).let {
assertEquals(uriForId(111), it.contentUri)
assertEquals("file", it.filename)
assertEquals(localUri, it.localUri.toString())
assertEquals("image", it.imageUrl)
assertEquals(created, it.timestamp.time)
assertEquals(created, it.dateCreated.time)
assertEquals(STATE_QUEUED, it.state)
assertEquals(222L, it.dataLength)
assertEquals(uploaded, it.dateUploaded?.time)
assertEquals(88L, it.transferred)
assertEquals(SOURCE_GALLERY, it.source)
assertEquals("desc", it.description)
assertEquals("create", it.creator)
assertEquals(640, it.width)
assertEquals(480, it.height)
assertEquals("007", it.license)
}
}
}
@Test
fun createFromCursor_nullableTimestamps() {
createCursor(0L, 0L, false, localUri).let { mc ->
testObject.fromCursor(mc).let {
assertNull(it.timestamp)
assertNull(it.dateCreated)
assertNull(it.dateUploaded)
}
}
}
@Test
fun createFromCursor_nullableLocalUri() {
createCursor(0L, 0L, false, "").let { mc ->
testObject.fromCursor(mc).let {
assertNull(it.localUri)
assertNull(it.dateCreated)
assertNull(it.dateUploaded)
}
}
}
@Test
fun createFromCursor_booleanEncoding() {
val mcFalse = createCursor(0L, 0L, false, localUri)
assertFalse(testObject.fromCursor(mcFalse).multiple)
val mcHammer = createCursor(0L, 0L, true, localUri)
assertTrue(testObject.fromCursor(mcHammer).multiple)
}
private fun createCursor(created: Long, uploaded: Long, multiple: Boolean, localUri: String) =
MatrixCursor(Table.ALL_FIELDS, 1).apply {
addRow(listOf("111", "file", localUri, "image",
created, STATE_QUEUED, 222L, uploaded, 88L, SOURCE_GALLERY, "desc",
"create", if (multiple) 1 else 0, 640, 480, "007"))
moveToFirst()
}
private fun createContribution(isMultiple: Boolean, localUri: Uri?, imageUrl: String?, dateUploaded: Date?, filename: String?) =
Contribution(localUri, imageUrl, filename, "desc", 222L, Date(321L), dateUploaded,
"create", "edit", "coords").apply {
state = STATE_COMPLETED
transferred = 333L
source = SOURCE_CAMERA
license = "007"
multiple = isMultiple
timestamp = Date(321L)
width = 640
height = 480 // VGA should be enough for anyone, right?
}
}

View file

@ -0,0 +1,156 @@
package fr.free.nrw.commons.modifications
import android.content.ContentProviderClient
import android.content.ContentValues
import android.database.MatrixCursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uri
import android.os.RemoteException
import com.nhaarman.mockito_kotlin.*
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.modifications.ModificationsContentProvider.BASE_URI
import fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class)
class ModifierSequenceDaoTest {
private val mediaUrl = "http://example.com/"
private val columns = arrayOf(COLUMN_ID, COLUMN_MEDIA_URI, COLUMN_DATA)
private val client: ContentProviderClient = mock()
private val database: SQLiteDatabase = mock()
private val contentValuesCaptor = argumentCaptor<ContentValues>()
private lateinit var testObject: ModifierSequenceDao
@Before
fun setUp() {
testObject = ModifierSequenceDao { client }
}
@Test
fun createFromCursorWithEmptyModifiers() {
testObject.fromCursor(createCursor("")).let {
assertEquals(mediaUrl, it.mediaUri.toString())
assertEquals(BASE_URI.buildUpon().appendPath("1").toString(), it.contentUri.toString())
assertTrue(it.modifiers.isEmpty())
}
}
@Test
fun createFromCursorWtihCategoryModifier() {
val cursor = createCursor("{\"name\": \"CategoriesModifier\", \"data\": {}}")
val seq = testObject.fromCursor(cursor)
assertEquals(1, seq.modifiers.size)
assertTrue(seq.modifiers[0] is CategoryModifier)
}
@Test
fun createFromCursorWithRemoveModifier() {
val cursor = createCursor("{\"name\": \"TemplateRemoverModifier\", \"data\": {}}")
val seq = testObject.fromCursor(cursor)
assertEquals(1, seq.modifiers.size)
assertTrue(seq.modifiers[0] is TemplateRemoveModifier)
}
@Test
fun deleteSequence() {
whenever(client.delete(isA(), isNull(), isNull())).thenReturn(1)
val seq = testObject.fromCursor(createCursor(""))
testObject.delete(seq)
verify(client).delete(eq(seq.contentUri), isNull(), isNull())
}
@Test(expected = RuntimeException::class)
fun deleteTranslatesRemoteExceptions() {
whenever(client.delete(isA(), isNull(), isNull())).thenThrow(RemoteException(""))
val seq = testObject.fromCursor(createCursor(""))
testObject.delete(seq)
}
@Test
fun saveExistingSequence() {
val modifierJson = "{\"name\":\"CategoriesModifier\",\"data\":{}}"
val expectedData = "{\"modifiers\":[$modifierJson]}"
val cursor = createCursor(modifierJson)
val seq = testObject.fromCursor(cursor)
testObject.save(seq)
verify(client).update(eq(seq.contentUri), contentValuesCaptor.capture(), isNull(), isNull())
contentValuesCaptor.firstValue.let {
assertEquals(2, it.size())
assertEquals(mediaUrl, it.get(COLUMN_MEDIA_URI))
assertEquals(expectedData, it.get(COLUMN_DATA))
}
}
@Test
fun saveNewSequence() {
val expectedContentUri = BASE_URI.buildUpon().appendPath("1").build()
whenever(client.insert(isA(), isA())).thenReturn(expectedContentUri)
val seq = ModifierSequence(Uri.parse(mediaUrl))
testObject.save(seq)
assertEquals(expectedContentUri.toString(), seq.contentUri.toString())
verify(client).insert(eq(ModificationsContentProvider.BASE_URI), contentValuesCaptor.capture())
contentValuesCaptor.firstValue.let {
assertEquals(2, it.size())
assertEquals(mediaUrl, it.get(COLUMN_MEDIA_URI))
assertEquals("{\"modifiers\":[]}", it.get(COLUMN_DATA))
}
}
@Test(expected = RuntimeException::class)
fun saveTranslatesRemoteExceptions() {
whenever(client.insert(isA(), isA())).thenThrow(RemoteException(""))
testObject.save(ModifierSequence(Uri.parse(mediaUrl)))
}
@Test
fun createTable() {
onCreate(database)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun updateTable() {
onUpdate(database, 1, 2)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(DROP_TABLE_STATEMENT)
verify<SQLiteDatabase>(database).execSQL(CREATE_TABLE_STATEMENT)
}
}
@Test
fun deleteTable() {
onDelete(database)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(DROP_TABLE_STATEMENT)
verify<SQLiteDatabase>(database).execSQL(CREATE_TABLE_STATEMENT)
}
}
private fun createCursor(modifierJson: String) = MatrixCursor(columns, 1).apply {
addRow(listOf("1", mediaUrl, "{\"modifiers\": [$modifierJson]}"))
moveToFirst()
}
}

View file

@ -0,0 +1,246 @@
package fr.free.nrw.commons.mwapi
import android.content.SharedPreferences
import android.os.Build
import android.preference.PreferenceManager
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.TestCommonsApplication
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import java.net.URLDecoder
import java.util.*
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class)
class ApacheHttpClientMediaWikiApiTest {
private lateinit var testObject: ApacheHttpClientMediaWikiApi
private lateinit var server: MockWebServer
private lateinit var sharedPreferences: SharedPreferences
@Before
fun setUp() {
server = MockWebServer()
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application)
testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences)
testObject.setWikiMediaToolforgeUrl("http://" + server.hostName + ":" + server.port + "/")
}
@After
fun teardown() {
server.shutdown()
}
@Test
fun authCookiesAreHandled() {
assertEquals("", testObject.authCookie)
testObject.authCookie = "cookie=chocolate-chip"
assertEquals("cookie=chocolate-chip", testObject.authCookie)
}
@Test
fun simpleLoginWithWrongPassword() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api batchcomplete=\"\"><query><tokens logintoken=\"baz\" /></query></api>"))
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><clientlogin status=\"FAIL\" message=\"Incorrect password entered.&#10;Please try again.\" messagecode=\"wrongpassword\" /></api>"))
val result = testObject.login("foo", "bar")
assertBasicRequestParameters(server, "POST").let { loginTokenRequest ->
parseBody(loginTokenRequest.body.readUtf8()).let { body ->
assertEquals("xml", body["format"])
assertEquals("query", body["action"])
assertEquals("login", body["type"])
assertEquals("tokens", body["meta"])
}
}
assertBasicRequestParameters(server, "POST").let { loginRequest ->
parseBody(loginRequest.body.readUtf8()).let { body ->
assertEquals("1", body["rememberMe"])
assertEquals("foo", body["username"])
assertEquals("bar", body["password"])
assertEquals("baz", body["logintoken"])
assertEquals("https://commons.wikimedia.org", body["loginreturnurl"])
assertEquals("xml", body["format"])
}
}
assertEquals("wrongpassword", result)
}
@Test
fun simpleLogin() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api batchcomplete=\"\"><query><tokens logintoken=\"baz\" /></query></api>"))
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><clientlogin status=\"PASS\" username=\"foo\" /></api>"))
val result = testObject.login("foo", "bar")
assertBasicRequestParameters(server, "POST").let { loginTokenRequest ->
parseBody(loginTokenRequest.body.readUtf8()).let { body ->
assertEquals("xml", body["format"])
assertEquals("query", body["action"])
assertEquals("login", body["type"])
assertEquals("tokens", body["meta"])
}
}
assertBasicRequestParameters(server, "POST").let { loginRequest ->
parseBody(loginRequest.body.readUtf8()).let { body ->
assertEquals("1", body["rememberMe"])
assertEquals("foo", body["username"])
assertEquals("bar", body["password"])
assertEquals("baz", body["logintoken"])
assertEquals("https://commons.wikimedia.org", body["loginreturnurl"])
assertEquals("xml", body["format"])
}
}
assertEquals("PASS", result)
}
@Test
fun twoFactorLogin() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api batchcomplete=\"\"><query><tokens logintoken=\"baz\" /></query></api>"))
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><clientlogin status=\"PASS\" username=\"foo\" /></api>"))
val result = testObject.login("foo", "bar", "2fa")
assertBasicRequestParameters(server, "POST").let { loginTokenRequest ->
parseBody(loginTokenRequest.body.readUtf8()).let { body ->
assertEquals("xml", body["format"])
assertEquals("query", body["action"])
assertEquals("login", body["type"])
assertEquals("tokens", body["meta"])
}
}
assertBasicRequestParameters(server, "POST").let { loginRequest ->
parseBody(loginRequest.body.readUtf8()).let { body ->
assertEquals("true", body["rememberMe"])
assertEquals("foo", body["username"])
assertEquals("bar", body["password"])
assertEquals("baz", body["logintoken"])
assertEquals("true", body["logincontinue"])
assertEquals("2fa", body["OATHToken"])
assertEquals("xml", body["format"])
}
}
assertEquals("PASS", result)
}
@Test
fun validateLoginForLoggedInUser() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"10\" name=\"foo\"/></query></api>"))
val result = testObject.validateLogin()
assertBasicRequestParameters(server, "GET").let { loginTokenRequest ->
parseQueryParams(loginTokenRequest).let { body ->
assertEquals("xml", body["format"])
assertEquals("query", body["action"])
assertEquals("userinfo", body["meta"])
}
}
assertTrue(result)
}
@Test
fun validateLoginForLoggedOutUser() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"0\" name=\"foo\"/></query></api>"))
val result = testObject.validateLogin()
assertBasicRequestParameters(server, "GET").let { loginTokenRequest ->
parseQueryParams(loginTokenRequest).let { params ->
assertEquals("xml", params["format"])
assertEquals("query", params["action"])
assertEquals("userinfo", params["meta"])
}
}
assertFalse(result)
}
@Test
fun editToken() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><tokens edittoken=\"baz\" /></api>"))
val result = testObject.editToken
assertBasicRequestParameters(server, "GET").let { loginTokenRequest ->
parseQueryParams(loginTokenRequest).let { params ->
assertEquals("xml", params["format"])
assertEquals("tokens", params["action"])
assertEquals("edit", params["type"])
}
}
assertEquals("baz", result)
}
@Test
fun fileExistsWithName_FileNotFound() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api batchcomplete=\"\"><query> <normalized><n from=\"File:foo\" to=\"File:Foo\" /></normalized><pages><page _idx=\"-1\" ns=\"6\" title=\"File:Foo\" missing=\"\" imagerepository=\"\" /></pages></query></api>"))
val result = testObject.fileExistsWithName("foo")
assertBasicRequestParameters(server, "GET").let { request ->
parseQueryParams(request).let { params ->
assertEquals("xml", params["format"])
assertEquals("query", params["action"])
assertEquals("imageinfo", params["prop"])
assertEquals("File:foo", params["titles"])
}
}
assertFalse(result)
}
@Test
fun getUploadCount() {
server.enqueue(MockResponse().setBody("23\n"))
val testObserver = testObject.getUploadCount("testUsername").test()
assertEquals("testUsername", parseQueryParams(server.takeRequest())["user"])
assertEquals(1, testObserver.valueCount())
assertEquals(23, testObserver.values()[0])
}
private fun assertBasicRequestParameters(server: MockWebServer, method: String): RecordedRequest = server.takeRequest().let {
assertEquals("/", it.requestUrl.encodedPath())
assertEquals(method, it.method)
assertEquals("Commons/${BuildConfig.VERSION_NAME} (https://mediawiki.org/wiki/Apps/Commons) Android/${Build.VERSION.RELEASE}",
it.getHeader("User-Agent"))
if ("POST" == method) {
assertEquals("application/x-www-form-urlencoded", it.getHeader("Content-Type"))
}
return it
}
private fun parseQueryParams(request: RecordedRequest) = HashMap<String, String?>().apply {
request.requestUrl.let {
it.queryParameterNames().forEach { name -> put(name, it.queryParameter(name)) }
}
}
private fun parseBody(body: String): Map<String, String> = HashMap<String, String>().apply {
body.split("&".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray().forEach { prop ->
val pair = prop.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
put(pair[0], URLDecoder.decode(pair[1], "utf-8"))
}
}
}

View file

@ -0,0 +1,41 @@
package fr.free.nrw.commons.utils
import fr.free.nrw.commons.utils.StringSortingUtils.sortBySimilarity
import org.junit.Assert.assertEquals
import org.junit.Test
import java.util.Collections.sort
class StringSortingUtilsTest {
@Test
fun testSortingNumbersBySimilarity() {
val actualList = listOf("1234567", "4567", "12345", "123", "1234")
val expectedList = listOf("1234", "12345", "123", "1234567", "4567")
sort(actualList, sortBySimilarity("1234"))
assertEquals(expectedList, actualList)
}
@Test
fun testSortingTextBySimilarity() {
val actualList = listOf("The quick brown fox",
"quick brown fox",
"The",
"The quick ",
"The fox",
"brown fox",
"fox")
val expectedList = listOf("The",
"The fox",
"The quick ",
"The quick brown fox",
"quick brown fox",
"brown fox",
"fox")
sort(actualList, sortBySimilarity("The"))
assertEquals(expectedList, actualList)
}
}