With lazy loading of contributions (#3566)

This commit is contained in:
Vivek Maskara 2020-05-28 04:54:41 -07:00 committed by GitHub
parent c216fdf0d4
commit d863a404f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1397 additions and 928 deletions

View file

@ -0,0 +1,139 @@
package fr.free.nrw.commons.contributions
import android.content.Context
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.*
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.utils.NetworkUtilsTest
import fr.free.nrw.commons.utils.createMockDataSourceFactory
import io.reactivex.Scheduler
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import io.reactivex.schedulers.TestScheduler
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.*
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import java.lang.RuntimeException
import java.util.*
/**
* The unit test class for ContributionBoundaryCallbackTest
*/
class ContributionBoundaryCallbackTest {
@Mock
internal lateinit var repository: ContributionsRepository
@Mock
internal lateinit var sessionManager: SessionManager
@Mock
internal lateinit var mediaClient: MediaClient
private lateinit var contributionBoundaryCallback: ContributionBoundaryCallback
@Rule
@JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule()
lateinit var scheduler: Scheduler
/**
* initial setup
*/
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
scheduler = Schedulers.trampoline()
contributionBoundaryCallback =
ContributionBoundaryCallback(repository, sessionManager, mediaClient, scheduler);
}
@Test
fun testOnZeroItemsLoaded() {
whenever(repository.save(anyList<Contribution>()))
.thenReturn(Single.just(listOf(1L, 2L)))
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
Single.just(listOf(mock(Media::class.java)))
)
whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
.thenReturn(true)
contributionBoundaryCallback.onZeroItemsLoaded()
verify(repository).save(anyList<Contribution>());
verify(mediaClient).getMediaListForUser(anyString());
}
@Test
fun testOnLastItemLoaded() {
whenever(repository.save(anyList<Contribution>()))
.thenReturn(Single.just(listOf(1L, 2L)))
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
Single.just(listOf(mock(Media::class.java)))
)
whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
.thenReturn(true)
contributionBoundaryCallback.onItemAtEndLoaded(mock(Contribution::class.java))
verify(repository).save(anyList());
verify(mediaClient).getMediaListForUser(anyString());
}
@Test
fun testOnFrontItemLoaded() {
whenever(repository.save(anyList<Contribution>()))
.thenReturn(Single.just(listOf(1L, 2L)))
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
Single.just(listOf(mock(Media::class.java)))
)
whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
.thenReturn(true)
contributionBoundaryCallback.onItemAtFrontLoaded(mock(Contribution::class.java))
verify(repository).save(anyList());
verify(mediaClient).getMediaListForUser(anyString());
}
@Test
fun testFetchContributions() {
whenever(repository.save(anyList<Contribution>()))
.thenReturn(Single.just(listOf(1L, 2L)))
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
Single.just(listOf(mock(Media::class.java)))
)
whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
.thenReturn(true)
contributionBoundaryCallback.fetchContributions()
verify(repository).save(anyList());
verify(mediaClient).getMediaListForUser(anyString());
}
@Test
fun testFetchContributionsForEndOfList() {
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
.thenReturn(false)
contributionBoundaryCallback.fetchContributions()
verify(mediaClient, times(0)).getMediaListForUser(anyString())
verifyNoMoreInteractions(repository)
}
@Test
fun testFetchContributionsFailed() {
whenever(sessionManager.userName).thenReturn("Test")
whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
.thenReturn(true)
whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(Single.error(Exception("Error")))
contributionBoundaryCallback.fetchContributions()
verifyZeroInteractions(repository);
verify(mediaClient).getMediaListForUser(anyString());
}
}

View file

@ -0,0 +1,63 @@
package fr.free.nrw.commons.contributions
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.media.MediaClient
import io.reactivex.Completable
import io.reactivex.Scheduler
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
/**
* The unit test class for ContributionsListPresenterTest
*/
class ContributionsListPresenterTest {
@Mock
internal lateinit var contributionBoundaryCallback: ContributionBoundaryCallback
@Mock
internal lateinit var repository: ContributionsRepository
@Rule
@JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule()
lateinit var scheduler: Scheduler
lateinit var contributionsListPresenter: ContributionsListPresenter
/**
* initial setup
*/
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
scheduler = Schedulers.trampoline()
contributionsListPresenter =
ContributionsListPresenter(contributionBoundaryCallback, repository, scheduler);
}
@Test
fun testDeleteUpload() {
whenever(repository.deleteContributionFromDB(any<Contribution>()))
.thenReturn(Completable.complete())
contributionsListPresenter.deleteUpload(mock(Contribution::class.java))
verify(repository, times(1))
.deleteContributionFromDB(ArgumentMatchers.any(Contribution::class.java));
}
}

View file

@ -7,9 +7,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.loader.content.CursorLoader
import androidx.loader.content.Loader
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import io.reactivex.Completable
import io.reactivex.Scheduler
import io.reactivex.Single
import io.reactivex.schedulers.TestScheduler
@ -17,9 +19,11 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.*
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import java.util.concurrent.TimeUnit
/**
* The unit test class for ContributionsPresenter
@ -42,7 +46,7 @@ class ContributionsPresenterTest {
@Rule @JvmField var instantTaskExecutorRule = InstantTaskExecutorRule()
lateinit var scheduler : Scheduler
lateinit var scheduler : TestScheduler
/**
* initial setup
@ -60,23 +64,13 @@ class ContributionsPresenterTest {
liveData=MutableLiveData()
}
/**
* Test fetch contributions
*/
@Test
fun testFetchContributions(){
whenever(repository.getString(ArgumentMatchers.anyString())).thenReturn("10")
whenever(repository.fetchContributions()).thenReturn(liveData)
contributionsPresenter.fetchContributions()
verify(repository).fetchContributions()
}
/**
* Test presenter actions onDeleteContribution
*/
@Test
fun testDeleteContribution() {
whenever(repository.deleteContributionFromDB(ArgumentMatchers.any(Contribution::class.java))).thenReturn(Single.just(1))
whenever(repository.deleteContributionFromDB(ArgumentMatchers.any<Contribution>()))
.thenReturn(Completable.complete())
contributionsPresenter.deleteUpload(contribution)
verify(repository).deleteContributionFromDB(contribution)
}

View file

@ -0,0 +1,55 @@
package fr.free.nrw.commons.contributions
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.utils.createMockDataSourceFactory
import io.reactivex.Scheduler
import io.reactivex.Single
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.*
import org.mockito.Mockito.any
import org.mockito.Mockito.mock
/**
* The unit test class for ContributionsRepositoryTest
*/
class ContributionsRepositoryTest {
@Mock
internal lateinit var localDataSource: ContributionsLocalDataSource
@InjectMocks
private lateinit var contributionsRepository: ContributionsRepository
lateinit var scheduler: Scheduler
/**
* initial setup
*/
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
}
@Test
fun testFetchContributions() {
val contribution = mock(Contribution::class.java)
whenever(localDataSource.getContributions())
.thenReturn(createMockDataSourceFactory(listOf(contribution)))
val contributionsFactory = contributionsRepository.fetchContributions()
verify(localDataSource, times(1)).getContributions();
}
@Test
fun testSaveContribution() {
val contributions = listOf(mock(Contribution::class.java))
whenever(localDataSource.saveContributions(ArgumentMatchers.anyList()))
.thenReturn(Single.just(listOf(1L)))
val save = contributionsRepository.save(contributions).test().assertValueAt(0) {
it.size == 1 && it.get(0) == 1L
}
}
}

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.media
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.utils.CommonsDateUtil
import io.reactivex.Observable
@ -7,18 +8,16 @@ import junit.framework.Assert.*
import org.junit.Before
import org.junit.Test
import org.mockito.*
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.wikipedia.dataclient.mwapi.ImageDetails
import org.wikipedia.dataclient.mwapi.MwQueryPage
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.dataclient.mwapi.MwQueryResult
import org.wikipedia.gallery.ImageInfo
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.*
import java.util.*
import org.mockito.Captor
import org.mockito.Mockito.*
class MediaClientTest {
@ -46,9 +45,10 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
val checkPageExistsUsingTitle = mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
val checkPageExistsUsingTitle =
mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
assertTrue(checkPageExistsUsingTitle)
}
@ -63,9 +63,10 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
val checkPageExistsUsingTitle = mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
val checkPageExistsUsingTitle =
mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
assertFalse(checkPageExistsUsingTitle)
}
@ -80,7 +81,7 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
val checkFileExistsUsingSha = mediaClient!!.checkFileExistsUsingSha("abcde").blockingGet()
assertTrue(checkFileExistsUsingSha)
@ -97,7 +98,7 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
val checkFileExistsUsingSha = mediaClient!!.checkFileExistsUsingSha("abcde").blockingGet()
assertFalse(checkFileExistsUsingSha)
@ -117,7 +118,7 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.getMedia(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
assertEquals("Test", mediaClient!!.getMedia("abcde").blockingGet().filename)
}
@ -136,10 +137,11 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.getMedia(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
assertEquals(Media.EMPTY, mediaClient!!.getMedia("abcde").blockingGet())
}
@Captor
private val filenameCaptor: ArgumentCaptor<String>? = null
@ -159,18 +161,18 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.getMediaWithGenerator(filenameCaptor!!.capture()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
assertEquals("Test", mediaClient!!.getPictureOfTheDay().blockingGet().filename)
assertEquals(template, filenameCaptor.value);
}
@Captor
private val continuationCaptor: ArgumentCaptor<Map<String, String>>? = null
@Test
fun getMediaListFromCategoryTwice() {
val mockContinuation= mapOf(Pair("gcmcontinue", "test"))
val mockContinuation = mapOf(Pair("gcmcontinue", "test"))
val imageInfo = ImageInfo()
val mwQueryPage = mock(MwQueryPage::class.java)
@ -184,9 +186,13 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mockResponse.continuation()).thenReturn(mockContinuation)
`when`(mediaInterface!!.getMediaListFromCategory(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(),
continuationCaptor!!.capture()))
.thenReturn(Observable.just(mockResponse))
`when`(
mediaInterface!!.getMediaListFromCategory(
ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(),
continuationCaptor!!.capture()
)
)
.thenReturn(Observable.just(mockResponse))
val media1 = mediaClient!!.getMediaListFromCategory("abcde").blockingGet().get(0)
val media2 = mediaClient!!.getMediaListFromCategory("abcde").blockingGet().get(0)
@ -197,6 +203,38 @@ class MediaClientTest {
assertEquals(media2.filename, "Test")
}
@Test
fun getMediaListForUser() {
val mockContinuation = mapOf("gcmcontinue" to "test")
val imageInfo = ImageInfo()
val mwQueryPage = mock(MwQueryPage::class.java)
whenever(mwQueryPage.title()).thenReturn("Test")
whenever(mwQueryPage.imageInfo()).thenReturn(imageInfo)
val mwQueryResult = mock(MwQueryResult::class.java)
whenever(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
val mockResponse = mock(MwQueryResponse::class.java)
whenever(mockResponse.query()).thenReturn(mwQueryResult)
whenever(mockResponse.continuation()).thenReturn(mockContinuation)
whenever(
mediaInterface!!.getMediaListForUser(
ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(),
continuationCaptor!!.capture()
)
)
.thenReturn(Observable.just(mockResponse))
val media1 = mediaClient!!.getMediaListForUser("Test").blockingGet().get(0)
val media2 = mediaClient!!.getMediaListForUser("Test").blockingGet().get(0)
verify(mediaInterface, times(2))?.getMediaListForUser(
ArgumentMatchers.anyString(),
ArgumentMatchers.anyInt(), ArgumentMatchers.anyMap<String, String>()
)
}
@Test
fun getPageHtmlTest() {
val mwParseResult = mock(MwParseResult::class.java)
@ -207,7 +245,7 @@ class MediaClientTest {
mockResponse.setParse(mwParseResult)
`when`(mediaInterface!!.getPageHtml(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
assertEquals("Test", mediaClient!!.getPageHtml("abcde").blockingGet())
}
@ -218,7 +256,7 @@ class MediaClientTest {
mockResponse.setParse(null)
`when`(mediaInterface!!.getPageHtml(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
.thenReturn(Observable.just(mockResponse))
assertEquals("", mediaClient!!.getPageHtml("abcde").blockingGet())
}

View file

@ -6,6 +6,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
@ -28,34 +29,30 @@ public class NetworkUtilsTest {
@Test
public void testInternetConnectionEstablished() {
Context mockContext = mock(Context.class);
Application mockApplication = mock(Application.class);
ConnectivityManager mockConnectivityManager = mock(ConnectivityManager.class);
NetworkInfo mockNetworkInfo = mock(NetworkInfo.class);
when(mockNetworkInfo.isConnectedOrConnecting())
.thenReturn(true);
when(mockConnectivityManager.getActiveNetworkInfo())
.thenReturn(mockNetworkInfo);
when(mockApplication.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn(mockConnectivityManager);
when(mockContext.getApplicationContext()).thenReturn(mockApplication);
Context mockContext = getContext(true);
boolean internetConnectionEstablished = NetworkUtils.isInternetConnectionEstablished(mockContext);
assertTrue(internetConnectionEstablished);
}
@Test
public void testInternetConnectionNotEstablished() {
@NotNull
public static Context getContext(boolean connectionEstablished) {
Context mockContext = mock(Context.class);
Application mockApplication = mock(Application.class);
ConnectivityManager mockConnectivityManager = mock(ConnectivityManager.class);
NetworkInfo mockNetworkInfo = mock(NetworkInfo.class);
when(mockNetworkInfo.isConnectedOrConnecting())
.thenReturn(false);
.thenReturn(connectionEstablished);
when(mockConnectivityManager.getActiveNetworkInfo())
.thenReturn(mockNetworkInfo);
.thenReturn(mockNetworkInfo);
when(mockApplication.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn(mockConnectivityManager);
.thenReturn(mockConnectivityManager);
when(mockContext.getApplicationContext()).thenReturn(mockApplication);
return mockContext;
}
@Test
public void testInternetConnectionNotEstablished() {
Context mockContext = getContext(false);
boolean internetConnectionEstablished = NetworkUtils.isInternetConnectionEstablished(mockContext);
assertFalse(internetConnectionEstablished);
}

View file

@ -0,0 +1,74 @@
package fr.free.nrw.commons.utils
import android.database.Cursor
import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.room.InvalidationTracker
import androidx.room.RoomDatabase
import androidx.room.RoomSQLiteQuery
import androidx.room.paging.LimitOffsetDataSource
import com.nhaarman.mockitokotlin2.whenever
import org.mockito.Mockito.mock
fun <T> List<T>.asPagedList(config: PagedList.Config? = null): LiveData<PagedList<T>> {
val defaultConfig = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(size)
.setMaxSize(size + 2)
.setPrefetchDistance(1)
.build()
return LivePagedListBuilder<Int, T>(
createMockDataSourceFactory(this),
config ?: defaultConfig
).build()
}
/**
* Provides a mocked instance of the data source factory
*/
fun <T> createMockDataSourceFactory(itemList: List<T>): DataSource.Factory<Int, T> =
object : DataSource.Factory<Int, T>() {
override fun create(): DataSource<Int, T> = MockLimitDataSource(itemList)
}
/**
* Provides a mocked Room SQL query
*/
private fun mockQuery(): RoomSQLiteQuery? {
val query = mock(RoomSQLiteQuery::class.java);
whenever(query.sql).thenReturn("");
return query;
}
/**
* Provides a mocked Room DB
*/
private fun mockDb(): RoomDatabase? {
val roomDatabase = mock(RoomDatabase::class.java);
val invalidationTracker = mock(InvalidationTracker::class.java)
whenever(roomDatabase.invalidationTracker).thenReturn(invalidationTracker);
return roomDatabase;
}
/**
* Class that defines the mocked data source
*/
class MockLimitDataSource<T>(private val itemList: List<T>) :
LimitOffsetDataSource<T>(mockDb(), mockQuery(), false, null) {
override fun convertRows(cursor: Cursor?): MutableList<T> = itemList.toMutableList()
override fun countItems(): Int = itemList.count()
override fun isInvalid(): Boolean = false
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
}
override fun loadRange(startPosition: Int, loadCount: Int): MutableList<T> {
return itemList.subList(startPosition, startPosition + loadCount).toMutableList()
}
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
callback.onResult(itemList, 0)
}
}