diff options
Diffstat (limited to 'java/tests/src')
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) {} |