mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
Fix app stuck and memory issues while uploading images (#2287)
* Do not use an image array to store all bitmap pixels at once * Extract image preprocessing to a different service and use computation thread * Add java docs * Cleanup code to remove temp file logic * Add logs in upload flow * Fix tests * Fix more tests
This commit is contained in:
parent
21f82dd346
commit
559127dfa3
21 changed files with 320 additions and 891 deletions
|
|
@ -1,25 +1,21 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.BitmapRegionDecoder
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.kvstore.BasicKvStore
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.utils.BitmapRegionDecoderWrapper
|
||||
import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK
|
||||
import fr.free.nrw.commons.utils.ImageUtilsWrapper
|
||||
import io.reactivex.Single
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.*
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
|
|
@ -27,6 +23,7 @@ import org.mockito.Mockito.mock
|
|||
import org.mockito.MockitoAnnotations
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
|
|
@ -51,11 +48,9 @@ class UploadModelTest {
|
|||
@Mock
|
||||
internal var fileUtilsWrapper: FileUtilsWrapper? = null
|
||||
@Mock
|
||||
internal var imageUtilsWrapper: ImageUtilsWrapper? = null
|
||||
@Mock
|
||||
internal var bitmapRegionDecoderWrapper: BitmapRegionDecoderWrapper? = null
|
||||
@Mock
|
||||
internal var fileProcessor: FileProcessor? = null
|
||||
@Mock
|
||||
internal var imageProcessingService: ImageProcessingService? = null
|
||||
|
||||
@InjectMocks
|
||||
var uploadModel: UploadModel? = null
|
||||
|
|
@ -67,8 +62,6 @@ class UploadModelTest {
|
|||
|
||||
`when`(context!!.applicationContext)
|
||||
.thenReturn(mock(Application::class.java))
|
||||
`when`(fileUtilsWrapper!!.createCopyPathAndCopy(anyBoolean(), any(Uri::class.java), nullable(ContentResolver::class.java), any(Context::class.java)))
|
||||
.thenReturn("file.jpg")
|
||||
`when`(fileUtilsWrapper!!.getFileExt(anyString()))
|
||||
.thenReturn("jpg")
|
||||
`when`(fileUtilsWrapper!!.getSHA1(any(InputStream::class.java)))
|
||||
|
|
@ -77,12 +70,10 @@ class UploadModelTest {
|
|||
.thenReturn(mock(FileInputStream::class.java))
|
||||
`when`(fileUtilsWrapper!!.getGeolocationOfFile(anyString()))
|
||||
.thenReturn("")
|
||||
`when`(imageUtilsWrapper!!.checkIfImageIsTooDark(any(BitmapRegionDecoder::class.java)))
|
||||
.thenReturn(IMAGE_OK)
|
||||
`when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(anyString(), any(LatLng::class.java)))
|
||||
.thenReturn(false)
|
||||
`when`(bitmapRegionDecoderWrapper!!.newInstance(any(FileInputStream::class.java), anyBoolean()))
|
||||
.thenReturn(mock(BitmapRegionDecoder::class.java))
|
||||
`when`(imageProcessingService!!.checkImageQuality(anyString()))
|
||||
.thenReturn(Single.just(IMAGE_OK))
|
||||
`when`(imageProcessingService!!.checkImageQuality(any(Place::class.java), anyString()))
|
||||
.thenReturn(Single.just(IMAGE_OK))
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -93,154 +84,99 @@ class UploadModelTest {
|
|||
|
||||
@Test
|
||||
fun receive() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.items.size == 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun receiveDirect() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.items.size == 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyPreviousNotAvailableForDirectUpload() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertFalse(uploadModel!!.isPreviousAvailable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyNextAvailableForDirectUpload() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.isNextAvailable)
|
||||
val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.items.size == 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyPreviousNotAvailable() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertFalse(uploadModel!!.isPreviousAvailable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyNextAvailable() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.isNextAvailable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isSubmitAvailable() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.isNextAvailable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isSubmitAvailableForDirectUpload() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.isNextAvailable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentStepForDirectUpload() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.currentStep == 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentStep() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.currentStep == 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getStepCount() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.stepCount == 4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getStepCountForDirectUpload() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.stepCount == 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDirectCount() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.count == 1)
|
||||
val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.stepCount == 4)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCount() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.count == 2)
|
||||
val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.count == 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getUploads() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.uploads.size == 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDirectUploads() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
assertTrue(uploadModel!!.uploads.size == 1)
|
||||
val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.uploads.size == 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isTopCardState() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.isTopCardState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isTopCardStateForDirectUpload() {
|
||||
val element = mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.isTopCardState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun next() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.currentStep == 1)
|
||||
uploadModel!!.next()
|
||||
assertTrue(uploadModel!!.currentStep == 2)
|
||||
|
|
@ -248,10 +184,10 @@ class UploadModelTest {
|
|||
|
||||
@Test
|
||||
fun previous() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.currentStep == 1)
|
||||
uploadModel!!.next()
|
||||
assertTrue(uploadModel!!.currentStep == 2)
|
||||
|
|
@ -261,11 +197,19 @@ class UploadModelTest {
|
|||
|
||||
@Test
|
||||
fun isShowingItem() {
|
||||
val element = mock(Uri::class.java)
|
||||
val element2 = mock(Uri::class.java)
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.isShowingItem)
|
||||
val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.isShowingItem)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getElement(): Uri {
|
||||
val mock = mock(Uri::class.java)
|
||||
`when`(mock.path).thenReturn(UUID.randomUUID().toString() + "/file.jpg")
|
||||
return mock
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ package fr.free.nrw.commons.upload
|
|||
import android.net.Uri
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import io.reactivex.Observable
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.*
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
class UploadPresenterTest {
|
||||
|
||||
|
|
@ -26,6 +26,12 @@ class UploadPresenterTest {
|
|||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
`when`(uploadModel!!.preProcessImages(ArgumentMatchers.anyListOf(Uri::class.java),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.any(Place::class.java),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.any(SimilarImageInterface::class.java)))
|
||||
.thenReturn(Observable.just(mock(UploadModel.UploadItem::class.java)))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -33,18 +39,6 @@ class UploadPresenterTest {
|
|||
val element = Mockito.mock(Uri::class.java)
|
||||
val element2 = Mockito.mock(Uri::class.java)
|
||||
var uriList: List<Uri> = mutableListOf<Uri>(element, element2)
|
||||
uploadPresenter!!.receive(uriList, "image/jpeg", "external")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun receiveSingleItem() {
|
||||
val element = Mockito.mock(Uri::class.java)
|
||||
uploadPresenter!!.receive(element, "image/jpeg", "external")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun receiveDirect() {
|
||||
val element = Mockito.mock(Uri::class.java)
|
||||
uploadModel!!.receiveDirect(element, "image/jpeg", "external", Mockito.mock(Place::class.java)) { _, _ -> }
|
||||
uploadPresenter!!.receive(uriList, "image/jpeg", "external", mock(Place::class.java))
|
||||
}
|
||||
}
|
||||
|
|
@ -7,17 +7,6 @@ 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))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteFile() {
|
||||
val file = File.createTempFile("testfile", "")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue