summaryrefslogtreecommitdiff
path: root/java/tests/src
diff options
context:
space:
mode:
author Andrey Epin <ayepin@google.com> 2023-03-22 15:12:43 -0700
committer Andrey Epin <ayepin@google.com> 2023-04-17 11:36:15 -0700
commit5c05904fbda5a7d8b0b3a1d474ab7224adb686ee (patch)
treeba49849d0bfbd5d462b59de246d77dac170f17c0 /java/tests/src
parentcca6189957d7158c68661c5c0f6b88c5e317a50f (diff)
Remove bitmap caching from ScrollableImagePreview
Remove bitmap caching from ScrollableImagePreview; instead, extend the image loader interface to proivde a cache-control option. ScrollableImagePreview$BatchPreviewLoader requests caching only for the first visible items as we'd like them to be displayed as fast as possible and not be evicted by any of the remaining items. Minor fixes inside ImagePreviewImageLoader. ImageLoader is moved into contentpreview package. Bug: 271613784 Test: unit tests Test: Manual testing with injected extensive logging. Change-Id: Ic07572e1fb73d589d98207af8fe58ae52698802a
Diffstat (limited to 'java/tests/src')
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java1
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java1
-rw-r--r--java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt93
-rw-r--r--java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt4
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java1
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt3
6 files changed, 96 insertions, 7 deletions
diff --git a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
index 2a4d654a..9ebeb79d 100644
--- a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
+++ b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
@@ -28,6 +28,7 @@ import android.os.UserHandle;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
import com.android.intentresolver.chooser.TargetInfo;
+import com.android.intentresolver.contentpreview.ImageLoader;
import com.android.intentresolver.flags.FeatureFlagRepository;
import com.android.intentresolver.shortcuts.ShortcutLoader;
diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
index dc9baade..d23e4a66 100644
--- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
+++ b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
@@ -35,6 +35,7 @@ import android.os.UserHandle;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.TargetInfo;
+import com.android.intentresolver.contentpreview.ImageLoader;
import com.android.intentresolver.flags.FeatureFlagRepository;
import com.android.intentresolver.grid.ChooserGridAdapter;
import com.android.intentresolver.shortcuts.ShortcutLoader;
diff --git a/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt b/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt
index f327e19e..3c399cc4 100644
--- a/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt
+++ b/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt
@@ -19,11 +19,18 @@ package com.android.intentresolver
import android.content.ContentResolver
import android.content.Context
import android.content.res.Resources
+import android.graphics.Bitmap
import android.net.Uri
import android.util.Size
import androidx.lifecycle.Lifecycle
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
@@ -41,7 +48,10 @@ class ImagePreviewImageLoaderTest {
private val imageSize = Size(300, 300)
private val uriOne = Uri.parse("content://org.package.app/image-1.png")
private val uriTwo = Uri.parse("content://org.package.app/image-2.png")
- private val contentResolver = mock<ContentResolver>()
+ private val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ private val contentResolver = mock<ContentResolver> {
+ whenever(loadThumbnail(any(), any(), anyOrNull())).thenReturn(bitmap)
+ }
private val resources = mock<Resources> {
whenever(getDimensionPixelSize(R.dimen.chooser_preview_image_max_dimen))
.thenReturn(imageSize.width)
@@ -70,7 +80,7 @@ class ImagePreviewImageLoaderTest {
}
@Test
- fun test_prePopulate() = runTest {
+ fun prePopulate_cachesImagesUpToTheCacheSize() = runTest {
testSubject.prePopulate(listOf(uriOne, uriTwo))
verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
@@ -81,7 +91,7 @@ class ImagePreviewImageLoaderTest {
}
@Test
- fun test_invoke_return_cached_image() = runTest {
+ fun invoke_returnCachedImageWhenCalledTwice() = runTest {
testSubject(uriOne)
testSubject(uriOne)
@@ -89,7 +99,33 @@ class ImagePreviewImageLoaderTest {
}
@Test
- fun test_invoke_old_records_evicted_from_the_cache() = runTest {
+ fun invoke_whenInstructed_doesNotCache() = runTest {
+ testSubject(uriOne, false)
+ testSubject(uriOne, false)
+
+ verify(contentResolver, times(2)).loadThumbnail(any(), any(), anyOrNull())
+ }
+
+ @Test
+ fun invoke_overlappedRequests_Deduplicate() = runTest {
+ val scheduler = TestCoroutineScheduler()
+ val dispatcher = StandardTestDispatcher(scheduler)
+ val testSubject = ImagePreviewImageLoader(context, lifecycleOwner.lifecycle, 1, dispatcher)
+ coroutineScope {
+ launch(start = UNDISPATCHED) {
+ testSubject(uriOne, false)
+ }
+ launch(start = UNDISPATCHED) {
+ testSubject(uriOne, false)
+ }
+ scheduler.advanceUntilIdle()
+ }
+
+ verify(contentResolver, times(1)).loadThumbnail(any(), any(), anyOrNull())
+ }
+
+ @Test
+ fun invoke_oldRecordsEvictedFromTheCache() = runTest {
testSubject(uriOne)
testSubject(uriTwo)
testSubject(uriTwo)
@@ -98,4 +134,53 @@ class ImagePreviewImageLoaderTest {
verify(contentResolver, times(2)).loadThumbnail(uriOne, imageSize, null)
verify(contentResolver, times(1)).loadThumbnail(uriTwo, imageSize, null)
}
+
+ @Test
+ fun invoke_doNotCacheNulls() = runTest {
+ whenever(contentResolver.loadThumbnail(any(), any(), anyOrNull())).thenReturn(null)
+ testSubject(uriOne)
+ testSubject(uriOne)
+
+ verify(contentResolver, times(2)).loadThumbnail(uriOne, imageSize, null)
+ }
+
+ @Test(expected = CancellationException::class)
+ fun invoke_onClosedImageLoaderScope_throwsCancellationException() = runTest {
+ lifecycleOwner.state = Lifecycle.State.DESTROYED
+ testSubject(uriOne)
+ }
+
+ @Test(expected = CancellationException::class)
+ fun invoke_imageLoaderScopeClosedMidflight_throwsCancellationException() = runTest {
+ val scheduler = TestCoroutineScheduler()
+ val dispatcher = StandardTestDispatcher(scheduler)
+ val testSubject = ImagePreviewImageLoader(context, lifecycleOwner.lifecycle, 1, dispatcher)
+ coroutineScope {
+ val deferred = async(start = UNDISPATCHED) {
+ testSubject(uriOne, false)
+ }
+ lifecycleOwner.state = Lifecycle.State.DESTROYED
+ scheduler.advanceUntilIdle()
+ deferred.await()
+ }
+ }
+
+ @Test
+ fun invoke_multipleCallsWithDifferentCacheInstructions_cachingPrevails() = runTest {
+ val scheduler = TestCoroutineScheduler()
+ val dispatcher = StandardTestDispatcher(scheduler)
+ val testSubject = ImagePreviewImageLoader(context, lifecycleOwner.lifecycle, 1, dispatcher)
+ coroutineScope {
+ launch(start = UNDISPATCHED) {
+ testSubject(uriOne, false)
+ }
+ launch(start = UNDISPATCHED) {
+ testSubject(uriOne, true)
+ }
+ scheduler.advanceUntilIdle()
+ }
+ testSubject(uriOne, true)
+
+ verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
+ }
}
diff --git a/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt b/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
index 2f240d58..74a253b8 100644
--- a/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
+++ b/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
@@ -18,6 +18,7 @@ package com.android.intentresolver
import android.graphics.Bitmap
import android.net.Uri
+import com.android.intentresolver.contentpreview.ImageLoader
import java.util.function.Consumer
internal class TestPreviewImageLoader(
@@ -27,6 +28,7 @@ internal class TestPreviewImageLoader(
callback.accept(bitmaps[uri])
}
- override suspend fun invoke(uri: Uri): Bitmap? = bitmaps[uri]
+ override suspend fun invoke(uri: Uri, caching: Boolean): Bitmap? = bitmaps[uri]
+
override fun prePopulate(uris: List<Uri>) = Unit
}
diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
index 0a60b8c7..de5498db 100644
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
+++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
@@ -104,6 +104,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.intentresolver.chooser.DisplayResolveInfo;
+import com.android.intentresolver.contentpreview.ImageLoader;
import com.android.intentresolver.shortcuts.ShortcutLoader;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
index 7b9a0ce6..8eec289e 100644
--- a/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
+++ b/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
@@ -21,7 +21,6 @@ import android.content.ContentInterface
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
-import com.android.intentresolver.ImageLoader
import com.android.intentresolver.any
import com.android.intentresolver.anyOrNull
import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory
@@ -48,7 +47,7 @@ class ChooserContentPreviewUiTest {
callback.accept(null)
}
override fun prePopulate(uris: List<Uri>) = Unit
- override suspend fun invoke(uri: Uri): Bitmap? = null
+ override suspend fun invoke(uri: Uri, caching: Boolean): Bitmap? = null
}
private val actionFactory = object : ActionFactory {
override fun createCopyButton() = ActionRow.Action(label = "Copy", icon = null) {}