ImageLoader Test Updated (#4517)

This commit is contained in:
Aditya-Srivastav 2021-07-24 14:09:59 +05:30 committed by Aditya Srivastava
parent 443dcf445b
commit 36a94fea93
4 changed files with 101 additions and 128 deletions

View file

@ -93,6 +93,7 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1" testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
testImplementation 'com.facebook.soloader:soloader:0.9.0' testImplementation 'com.facebook.soloader:soloader:0.9.0'
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2"
// Android testing // Android testing
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION" androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"

View file

@ -1,9 +1,7 @@
package fr.free.nrw.commons.customselector.database package fr.free.nrw.commons.customselector.database
import androidx.room.* import androidx.room.*
import kotlinx.coroutines.runBlocking
import java.util.* import java.util.*
import kotlinx.coroutines.*
/** /**
* UploadedStatusDao for Custom Selector. * UploadedStatusDao for Custom Selector.
@ -29,58 +27,30 @@ abstract class UploadedStatusDao {
@Delete @Delete
abstract suspend fun delete(uploadedStatus: UploadedStatus) abstract suspend fun delete(uploadedStatus: UploadedStatus)
/**
* Get All entries from the uploaded status table.
*/
@Query("SELECT * FROM uploaded_table")
abstract suspend fun getAll() : List<UploadedStatus>
/** /**
* Query uploaded status with image sha1. * Query uploaded status with image sha1.
*/ */
@Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ") @Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ")
abstract suspend fun getFromImageSHA1(imageSHA1 : String) : UploadedStatus abstract suspend fun getFromImageSHA1(imageSHA1 : String) : UploadedStatus?
/** /**
* Query uploaded status with modified image sha1. * Query uploaded status with modified image sha1.
*/ */
@Query("SELECT * FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) ") @Query("SELECT * FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) ")
abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1 : String) : UploadedStatus abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1 : String) : UploadedStatus?
/** /**
* Asynchronous insert into uploaded status table. * Asynchronous insert into uploaded status table.
*/ */
suspend fun insertUploaded(uploadedStatus: UploadedStatus) { suspend fun insertUploaded(uploadedStatus: UploadedStatus) {
uploadedStatus.lastUpdated = Calendar.getInstance().time as Date? uploadedStatus.lastUpdated = Calendar.getInstance().time
insert(uploadedStatus) insert(uploadedStatus)
} }
/**
* Asynchronous delete from uploaded status table.
*/
suspend fun deleteUploaded(uploadedStatus: UploadedStatus) {
delete(uploadedStatus)
}
/**
* Asynchronous update entry in uploaded status table.
*/
suspend fun updateUploaded(uploadedStatus: UploadedStatus) {
update(uploadedStatus)
}
/** /**
* Asynchronous image sha1 query. * Asynchronous image sha1 query.
*/ */
suspend fun getUploadedFromImageSHA1(imageSHA1: String):UploadedStatus { suspend fun getUploadedFromImageSHA1(imageSHA1: String):UploadedStatus? {
return getFromImageSHA1(imageSHA1) return getFromImageSHA1(imageSHA1)
} }
/**
* Asynchronous modified image sha1 query.
*/
suspend fun getUploadedFromModifiedImageSHA1(modifiedImageSHA1: String):UploadedStatus {
return getFromModifiedImageSHA1(modifiedImageSHA1)
}
} }

View file

@ -62,9 +62,9 @@ class ImageLoader @Inject constructor(
/** /**
* Coroutine Dispatchers and Scope. * Coroutine Dispatchers and Scope.
*/ */
private var defaultDispatcher = Dispatchers.Default private var defaultDispatcher : CoroutineDispatcher = Dispatchers.Default
private var ioDispatcher = Dispatchers.IO private var ioDispatcher : CoroutineDispatcher = Dispatchers.IO
private val scope = MainScope() private val scope : CoroutineScope = MainScope()
/** /**
* Query image and setUp the view. * Query image and setUp the view.
@ -129,7 +129,7 @@ class ImageLoader @Inject constructor(
* @return Query result. * @return Query result.
*/ */
private suspend fun querySHA1(SHA1: String): Result { suspend fun querySHA1(SHA1: String): Result {
return withContext(ioDispatcher) { return withContext(ioDispatcher) {
mapResult[SHA1]?.let { mapResult[SHA1]?.let {
return@withContext it return@withContext it
@ -157,7 +157,7 @@ class ImageLoader @Inject constructor(
* *
* @return sha1 of the image * @return sha1 of the image
*/ */
private suspend fun getSHA1(image: Image): String { suspend fun getSHA1(image: Image): String {
mapModifiedImageSHA1[image]?.let{ mapModifiedImageSHA1[image]?.let{
return it return it
} }
@ -169,14 +169,14 @@ class ImageLoader @Inject constructor(
/** /**
* Get the uploaded status entry from the database. * Get the uploaded status entry from the database.
*/ */
private suspend fun getFromUploaded(imageSha1:String): UploadedStatus?{ suspend fun getFromUploaded(imageSha1:String): UploadedStatus? {
return uploadedStatusDao.getUploadedFromImageSHA1(imageSha1) return uploadedStatusDao.getUploadedFromImageSHA1(imageSha1)
} }
/** /**
* Insert into uploaded status table. * Insert into uploaded status table.
*/ */
private suspend fun insertIntoUploaded(imageSha1:String, modifiedImageSha1:String, imageResult:Boolean, modifiedImageResult: Boolean){ suspend fun insertIntoUploaded(imageSha1:String, modifiedImageSha1:String, imageResult:Boolean, modifiedImageResult: Boolean){
uploadedStatusDao.insertUploaded( uploadedStatusDao.insertUploaded(
UploadedStatus( UploadedStatus(
imageSha1, imageSha1,
@ -190,7 +190,7 @@ class ImageLoader @Inject constructor(
/** /**
* Get image sha1 from uri, used to retrieve the original image sha1. * Get image sha1 from uri, used to retrieve the original image sha1.
*/ */
private suspend fun getImageSHA1(uri: Uri): String { suspend fun getImageSHA1(uri: Uri): String {
return withContext(ioDispatcher) { return withContext(ioDispatcher) {
mapImageSHA1[uri]?.let{ mapImageSHA1[uri]?.let{
return@withContext it return@withContext it
@ -204,7 +204,7 @@ class ImageLoader @Inject constructor(
/** /**
* Get result data from database. * Get result data from database.
*/ */
private fun getResultFromUploadedStatus(uploadedStatus: UploadedStatus): Result { fun getResultFromUploadedStatus(uploadedStatus: UploadedStatus): Result {
if (uploadedStatus.imageResult || uploadedStatus.modifiedImageResult) { if (uploadedStatus.imageResult || uploadedStatus.modifiedImageResult) {
return Result.TRUE return Result.TRUE
} else { } else {

View file

@ -3,8 +3,7 @@ package fr.free.nrw.commons.customselector.ui.selector
import android.content.ContentResolver import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.*
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.customselector.database.UploadedStatus import fr.free.nrw.commons.customselector.database.UploadedStatus
import fr.free.nrw.commons.customselector.database.UploadedStatusDao import fr.free.nrw.commons.customselector.database.UploadedStatusDao
@ -17,6 +16,10 @@ import fr.free.nrw.commons.upload.FileProcessor
import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.upload.FileUtilsWrapper
import io.reactivex.Single import io.reactivex.Single
import junit.framework.Assert import junit.framework.Assert
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.*
import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -28,7 +31,6 @@ import org.powermock.reflect.Whitebox
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.lang.Exception
import java.util.* import java.util.*
import kotlin.collections.HashMap import kotlin.collections.HashMap
@ -38,6 +40,7 @@ import kotlin.collections.HashMap
@RunWith(PowerMockRunner::class) @RunWith(PowerMockRunner::class)
@PrepareForTest(PickedFiles::class) @PrepareForTest(PickedFiles::class)
@Config(sdk = [21], application = TestCommonsApplication::class) @Config(sdk = [21], application = TestCommonsApplication::class)
@ExperimentalCoroutinesApi
class ImageLoaderTest { class ImageLoaderTest {
@Mock @Mock
@ -73,146 +76,145 @@ class ImageLoaderTest {
@Mock @Mock
private lateinit var contentResolver: ContentResolver private lateinit var contentResolver: ContentResolver
@Mock @ExperimentalCoroutinesApi
private lateinit var image: Image; private val testDispacher = TestCoroutineDispatcher()
private lateinit var imageLoader: ImageLoader; private lateinit var imageLoader: ImageLoader;
private var mapImageSHA1: HashMap<Image, String> = HashMap() private var mapImageSHA1: HashMap<Uri, String> = HashMap()
private var mapHolderImage : HashMap<ImageAdapter.ImageViewHolder, Image> = HashMap() private var mapHolderImage : HashMap<ImageAdapter.ImageViewHolder, Image> = HashMap()
private var mapResult: HashMap<String, ImageLoader.Result> = HashMap() private var mapResult: HashMap<String, ImageLoader.Result> = HashMap()
private var mapModifiedImageSHA1: HashMap<Image, String> = HashMap()
private lateinit var image: Image;
private lateinit var uploadedStatus: UploadedStatus;
/** /**
* Setup before test. * Setup before test.
*/ */
@Before @Before
@ExperimentalCoroutinesApi
fun setup() { fun setup() {
Dispatchers.setMain(testDispacher)
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
imageLoader = imageLoader =
ImageLoader(mediaClient, fileProcessor, fileUtilsWrapper, uploadedStatusDao, context) ImageLoader(mediaClient, fileProcessor, fileUtilsWrapper, uploadedStatusDao, context)
uploadedStatus= UploadedStatus(
"testSha1",
"testSha1",
false,
false,
Calendar.getInstance().time
)
image = Image(1, "test", uri, "test", 0, "test")
Whitebox.setInternalState(imageLoader, "mapImageSHA1", mapImageSHA1); Whitebox.setInternalState(imageLoader, "mapImageSHA1", mapImageSHA1);
Whitebox.setInternalState(imageLoader, "mapHolderImage", mapHolderImage); Whitebox.setInternalState(imageLoader, "mapHolderImage", mapHolderImage);
Whitebox.setInternalState(imageLoader, "mapModifiedImageSHA1", mapModifiedImageSHA1);
Whitebox.setInternalState(imageLoader, "mapResult", mapResult); Whitebox.setInternalState(imageLoader, "mapResult", mapResult);
Whitebox.setInternalState(imageLoader, "context", context) Whitebox.setInternalState(imageLoader, "context", context)
Whitebox.setInternalState(imageLoader, "ioDispatcher", testDispacher)
Whitebox.setInternalState(imageLoader, "defaultDispatcher", testDispacher)
whenever(contentResolver.openInputStream(uri)).thenReturn(inputStream)
whenever(context.contentResolver).thenReturn(contentResolver)
whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1")
} }
/** /**
* Test queryAndSetView. * Reset Dispatchers.
*/
@After
@ExperimentalCoroutinesApi
fun tearDown() {
Dispatchers.resetMain()
testDispacher.cleanupTestCoroutines()
}
/**
* Test queryAndSetView with upload Status as null.
*/ */
@Test @Test
fun testQueryAndSetView(){ fun testQueryAndSetViewUploadedStatusNull() = testDispacher.runBlockingTest {
// TODO whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(null)
imageLoader.queryAndSetView(holder,image) mapModifiedImageSHA1[image] = "testSha1"
mapImageSHA1[uri] = "testSha1"
mapResult["testSha1"] = ImageLoader.Result.TRUE
imageLoader.queryAndSetView(holder, image)
mapResult["testSha1"] = ImageLoader.Result.FALSE
imageLoader.queryAndSetView(holder, image)
}
/**
* Test queryAndSetView with upload Status not null (ie retrieved from table)
*/
@Test
fun testQueryAndSetViewUploadedStatusNotNull() = testDispacher.runBlockingTest {
whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(uploadedStatus)
imageLoader.queryAndSetView(holder, image)
} }
/** /**
* Test querySha1 * Test querySha1
*/ */
@Test @Test
fun testQuerySha1() { fun testQuerySha1() = testDispacher.runBlockingTest {
val func = imageLoader.javaClass.getDeclaredMethod(
"querySHA1",
String::class.java
)
func.isAccessible = true
Mockito.`when`(single.blockingGet()).thenReturn(true) whenever(single.blockingGet()).thenReturn(true)
Mockito.`when`(mediaClient.checkFileExistsUsingSha("testSha1")).thenReturn(single) whenever(mediaClient.checkFileExistsUsingSha("testSha1")).thenReturn(single)
Mockito.`when`(fileUtilsWrapper.getSHA1(any())).thenReturn("testSha1") whenever(fileUtilsWrapper.getSHA1(any())).thenReturn("testSha1")
// test without saving in map. imageLoader.querySHA1("testSha1")
func.invoke(imageLoader, "testSha1");
// test with map save.
mapResult["testSha1"] = ImageLoader.Result.FALSE
func.invoke(imageLoader, "testSha1");
} }
/** /**
* Test getSha1 * Test getSha1
*/ */
@Test @Test
@Throws (Exception::class) @ExperimentalCoroutinesApi
fun testGetSha1() { fun testGetSha1() = testDispacher.runBlockingTest {
val func = imageLoader.javaClass.getDeclaredMethod(
"getSHA1",
Image::class.java
)
func.isAccessible = true
PowerMockito.mockStatic(PickedFiles::class.java); PowerMockito.mockStatic(PickedFiles::class.java)
BDDMockito.given(PickedFiles.pickedExistingPicture(context, image.uri)) BDDMockito.given(PickedFiles.pickedExistingPicture(context, image.uri))
.willReturn(UploadableFile(uri, File("ABC"))); .willReturn(UploadableFile(uri, File("ABC")))
whenever(fileUtilsWrapper.getFileInputStream("ABC")).thenReturn(inputStream) whenever(fileUtilsWrapper.getFileInputStream("ABC")).thenReturn(inputStream)
whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1")
Assert.assertEquals("testSha1", func.invoke(imageLoader, image)); Assert.assertEquals("testSha1", imageLoader.getSHA1(image));
whenever(PickedFiles.pickedExistingPicture(context,Uri.parse("test"))).thenReturn(uploadableFile) whenever(PickedFiles.pickedExistingPicture(context, Uri.parse("test"))).thenReturn(
uploadableFile
)
mapImageSHA1[image] = "testSha2" mapModifiedImageSHA1[image] = "testSha2"
Assert.assertEquals("testSha2", func.invoke(imageLoader, image)); Assert.assertEquals("testSha2", imageLoader.getSHA1(image));
}
/**
* Test insertIntoUploaded Function.
*/
@Test
@Throws (Exception::class)
fun testInsertIntoUploaded() {
val func = imageLoader.javaClass.getDeclaredMethod(
"insertIntoUploaded",
String::class.java,
String::class.java,
Boolean::class.java,
Boolean::class.java)
func.isAccessible = true
func.invoke(imageLoader, "", "", true, true)
}
/**
* Test getImageSha1.
*/
@Test
@Throws (Exception::class)
fun testGetImageSHA1() {
val func = imageLoader.javaClass.getDeclaredMethod(
"getImageSHA1",
Uri::class.java)
func.isAccessible = true
whenever(contentResolver.openInputStream(uri)).thenReturn(inputStream)
whenever(context.contentResolver).thenReturn(contentResolver)
whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1")
Assert.assertEquals("testSha1", func.invoke(imageLoader,uri))
} }
/** /**
* Test getResultFromUploadedStatus. * Test getResultFromUploadedStatus.
*/ */
@Test @Test
@Throws (Exception::class)
fun testGetResultFromUploadedStatus() { fun testGetResultFromUploadedStatus() {
val func = imageLoader.javaClass.getDeclaredMethod( val func = imageLoader.javaClass.getDeclaredMethod(
"getResultFromUploadedStatus", "getResultFromUploadedStatus",
UploadedStatus::class.java) UploadedStatus::class.java)
func.isAccessible = true func.isAccessible = true
// test Result.TRUE
Assert.assertEquals(ImageLoader.Result.TRUE,
func.invoke(imageLoader,
UploadedStatus("", "", true, true)))
// test Result.FALSE
Assert.assertEquals(ImageLoader.Result.FALSE,
func.invoke(imageLoader,
UploadedStatus("", "", false, false, Calendar.getInstance().time)))
// test Result.INVALID // test Result.INVALID
uploadedStatus.lastUpdated = Date(0);
Assert.assertEquals(ImageLoader.Result.INVALID, Assert.assertEquals(ImageLoader.Result.INVALID,
func.invoke(imageLoader, UploadedStatus("", "", false, false, Date(0)))) imageLoader.getResultFromUploadedStatus(uploadedStatus))
// test Result.TRUE
uploadedStatus.imageResult = true;
Assert.assertEquals(ImageLoader.Result.TRUE,
imageLoader.getResultFromUploadedStatus(uploadedStatus))
}
@Test
fun testCleanUP() {
imageLoader.cleanUP()
} }
} }