diff options
| author | 2023-04-19 09:18:56 -0700 | |
|---|---|---|
| committer | 2023-04-24 15:30:06 -0700 | |
| commit | 20e622725e112c85edd3fc7f52592782bf63b9a2 (patch) | |
| tree | a1cd0a78fcd86051de3413b0e88e260f9dccdadb /java/tests | |
| parent | df944c13e379fe924a5556fc749350270586fb8e (diff) | |
Load shortucts concurrently with app target resolution
Reload shortcuts whenever we rebuild the target list:
* in ChooserActivity#onHandlePackageChange(), called by system package
change broadcast, target pinning, and ResolverActivity#onRestart;
* in work profile status broadcast receiver.
Tie ShorctuLoader to a Lifecycle (instead of exposing lifecycle
methods i.e. destroy) and switch to coroutines from executors.
Add a startup-to-first-shortcut tracing and a debug log.
Bug: 262927266
Test: manual testing, unit tests.
Change-Id: Iaa4bd9a88f29378d75d88b2ea8fc3698cbd3be8f
Diffstat (limited to 'java/tests')
| -rw-r--r-- | java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java | 18 | ||||
| -rw-r--r-- | java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt | 163 |
2 files changed, 136 insertions, 45 deletions
diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java index cbc8d53e..6c659133 100644 --- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java +++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java @@ -1342,7 +1342,7 @@ public class UnbundledChooserActivityTest { // verify that ShortcutLoader was queried ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); - verify(shortcutLoaders.get(0).first, times(1)).queryShortcuts(appTargets.capture()); + verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); // send shortcuts assertThat( @@ -1423,7 +1423,7 @@ public class UnbundledChooserActivityTest { // verify that ShortcutLoader was queried ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); - verify(shortcutLoaders.get(0).first, times(1)).queryShortcuts(appTargets.capture()); + verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); // send shortcuts assertThat( @@ -1508,7 +1508,7 @@ public class UnbundledChooserActivityTest { // verify that ShortcutLoader was queried ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); - verify(shortcutLoaders.get(0).first, times(1)).queryShortcuts(appTargets.capture()); + verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); // send shortcuts assertThat( @@ -1583,7 +1583,7 @@ public class UnbundledChooserActivityTest { // verify that ShortcutLoader was queried ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); - verify(shortcutLoaders.get(0).first, times(1)).queryShortcuts(appTargets.capture()); + verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); // send shortcuts assertThat( @@ -1675,7 +1675,7 @@ public class UnbundledChooserActivityTest { // verify that ShortcutLoader was queried ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); - verify(shortcutLoaders.get(0).first, times(1)).queryShortcuts(appTargets.capture()); + verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); // send shortcuts assertThat( @@ -2174,7 +2174,7 @@ public class UnbundledChooserActivityTest { ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); verify(shortcutLoaders.get(0).first, times(1)) - .queryShortcuts(appTargets.capture()); + .updateAppTargets(appTargets.capture()); // send shortcuts assertThat( @@ -2255,7 +2255,7 @@ public class UnbundledChooserActivityTest { ArgumentCaptor<DisplayResolveInfo[]> appTargets = ArgumentCaptor.forClass(DisplayResolveInfo[].class); verify(shortcutLoaders.get(0).first, times(1)) - .queryShortcuts(appTargets.capture()); + .updateAppTargets(appTargets.capture()); // send shortcuts List<ChooserTarget> serviceTargets = createDirectShareTargets( @@ -2550,12 +2550,12 @@ public class UnbundledChooserActivityTest { .perform(swipeUp()); waitForIdle(); - verify(personalProfileShortcutLoader, times(1)).queryShortcuts(any()); + verify(personalProfileShortcutLoader, times(1)).updateAppTargets(any()); onView(withText(R.string.resolver_work_tab)).perform(click()); waitForIdle(); - verify(workProfileShortcutLoader, times(1)).queryShortcuts(any()); + verify(workProfileShortcutLoader, times(1)).updateAppTargets(any()); } @Test diff --git a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt index e8e2f862..742aac71 100644 --- a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt +++ b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt @@ -26,7 +26,9 @@ import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.ShortcutManager import android.os.UserHandle import android.os.UserManager +import androidx.lifecycle.Lifecycle import androidx.test.filters.SmallTest +import com.android.intentresolver.TestLifecycleOwner import com.android.intentresolver.any import com.android.intentresolver.argumentCaptor import com.android.intentresolver.capture @@ -36,19 +38,27 @@ import com.android.intentresolver.createShareShortcutInfo import com.android.intentresolver.createShortcutInfo import com.android.intentresolver.mock import com.android.intentresolver.whenever +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineScheduler +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.After import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Test import org.mockito.Mockito.anyInt import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -import java.util.concurrent.Executor import java.util.function.Consumer +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest class ShortcutLoaderTest { private val appInfo = ApplicationInfo().apply { @@ -68,7 +78,9 @@ class ShortcutLoaderTest { whenever(createContextAsUser(any(), anyInt())).thenReturn(this) whenever(getSystemService(Context.USER_SERVICE)).thenReturn(userManager) } - private val executor = ImmediateExecutor() + private val scheduler = TestCoroutineScheduler() + private val dispatcher = UnconfinedTestDispatcher(scheduler) + private val lifecycleOwner = TestLifecycleOwner() private val intentFilter = mock<IntentFilter>() private val appPredictor = mock<ShortcutLoader.AppPredictorProxy>() private val callback = mock<Consumer<ShortcutLoader.Result>>() @@ -79,20 +91,32 @@ class ShortcutLoaderTest { private val appTargets = arrayOf(appTarget) private val matchingShortcutInfo = createShortcutInfo("id-0", componentName, 1) + @Before + fun setup() { + Dispatchers.setMain(dispatcher) + lifecycleOwner.state = Lifecycle.State.CREATED + } + + @After + fun cleanup() { + lifecycleOwner.state = Lifecycle.State.DESTROYED + Dispatchers.resetMain() + } + @Test - fun test_queryShortcuts_result_consistency_with_AppPredictor() { + fun test_loadShortcutsWithAppPredictor_resultIntegrity() { val testSubject = ShortcutLoader( context, + lifecycleOwner.lifecycle, appPredictor, UserHandle.of(0), true, intentFilter, - executor, - executor, + dispatcher, callback ) - testSubject.queryShortcuts(appTargets) + testSubject.updateAppTargets(appTargets) val matchingAppTarget = createAppTarget(matchingShortcutInfo) val shortcuts = listOf( @@ -130,7 +154,7 @@ class ShortcutLoaderTest { } @Test - fun test_queryShortcuts_result_consistency_with_ShortcutManager() { + fun test_loadShortcutsWithShortcutManager_resultIntegrity() { val shortcutManagerResult = listOf( ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName), // mismatching shortcut @@ -142,16 +166,16 @@ class ShortcutLoaderTest { whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) val testSubject = ShortcutLoader( context, + lifecycleOwner.lifecycle, null, UserHandle.of(0), true, intentFilter, - executor, - executor, + dispatcher, callback ) - testSubject.queryShortcuts(appTargets) + testSubject.updateAppTargets(appTargets) val resultCaptor = argumentCaptor<ShortcutLoader.Result>() verify(callback, times(1)).accept(capture(resultCaptor)) @@ -175,7 +199,7 @@ class ShortcutLoaderTest { } @Test - fun test_queryShortcuts_falls_back_to_ShortcutManager_on_empty_reply() { + fun test_appPredictorReturnsEmptyList_fallbackToShortcutManager() { val shortcutManagerResult = listOf( ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName), // mismatching shortcut @@ -187,16 +211,16 @@ class ShortcutLoaderTest { whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) val testSubject = ShortcutLoader( context, + lifecycleOwner.lifecycle, appPredictor, UserHandle.of(0), true, intentFilter, - executor, - executor, + dispatcher, callback ) - testSubject.queryShortcuts(appTargets) + testSubject.updateAppTargets(appTargets) verify(appPredictor, times(1)).requestPredictionUpdate() val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>() @@ -226,7 +250,7 @@ class ShortcutLoaderTest { } @Test - fun test_queryShortcuts_onAppPredictorFailure_fallbackToShortcutManager() { + fun test_appPredictor_requestPredictionUpdateFailure_fallbackToShortcutManager() { val shortcutManagerResult = listOf( ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName), // mismatching shortcut @@ -240,16 +264,16 @@ class ShortcutLoaderTest { .thenThrow(IllegalStateException("Test exception")) val testSubject = ShortcutLoader( context, + lifecycleOwner.lifecycle, appPredictor, UserHandle.of(0), true, intentFilter, - executor, - executor, + dispatcher, callback ) - testSubject.queryShortcuts(appTargets) + testSubject.updateAppTargets(appTargets) verify(appPredictor, times(1)).requestPredictionUpdate() @@ -275,32 +299,105 @@ class ShortcutLoaderTest { } @Test - fun test_queryShortcuts_do_not_call_services_for_not_running_work_profile() { + fun test_ShortcutLoader_shortcutsRequestedIndependentlyFromAppTargets() { + ShortcutLoader( + context, + lifecycleOwner.lifecycle, + appPredictor, + UserHandle.of(0), + true, + intentFilter, + dispatcher, + callback + ) + + verify(appPredictor, times(1)).requestPredictionUpdate() + verify(callback, never()).accept(any()) + } + + @Test + fun test_ShortcutLoader_noResultsWithoutAppTargets() { + val shortcutManagerResult = listOf( + ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName), + // mismatching shortcut + createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1) + ) + val shortcutManager = mock<ShortcutManager> { + whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult) + } + whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager) + val testSubject = ShortcutLoader( + context, + lifecycleOwner.lifecycle, + null, + UserHandle.of(0), + true, + intentFilter, + dispatcher, + callback + ) + + verify(shortcutManager, times(1)).getShareTargets(any()) + verify(callback, never()).accept(any()) + + testSubject.reset() + + verify(shortcutManager, times(2)).getShareTargets(any()) + verify(callback, never()).accept(any()) + + testSubject.updateAppTargets(appTargets) + + verify(shortcutManager, times(2)).getShareTargets(any()) + verify(callback, times(1)).accept(any()) + } + + @Test + fun test_OnLifecycleDestroyed_unsubscribeFromAppPredictor() { + ShortcutLoader( + context, + lifecycleOwner.lifecycle, + appPredictor, + UserHandle.of(0), + true, + intentFilter, + dispatcher, + callback + ) + + verify(appPredictor, never()).unregisterPredictionUpdates(any()) + + lifecycleOwner.state = Lifecycle.State.DESTROYED + + verify(appPredictor, times(1)).unregisterPredictionUpdates(any()) + } + + @Test + fun test_workProfileNotRunning_doNotCallServices() { testDisabledWorkProfileDoNotCallSystem(isUserRunning = false) } @Test - fun test_queryShortcuts_do_not_call_services_for_locked_work_profile() { + fun test_workProfileLocked_doNotCallServices() { testDisabledWorkProfileDoNotCallSystem(isUserUnlocked = false) } @Test - fun test_queryShortcuts_do_not_call_services_if_quite_mode_is_enabled_for_work_profile() { + fun test_workProfileQuiteModeEnabled_doNotCallServices() { testDisabledWorkProfileDoNotCallSystem(isQuietModeEnabled = true) } @Test - fun test_queryShortcuts_call_services_for_not_running_main_profile() { + fun test_mainProfileNotRunning_callServicesAnyway() { testAlwaysCallSystemForMainProfile(isUserRunning = false) } @Test - fun test_queryShortcuts_call_services_for_locked_main_profile() { + fun test_mainProfileLocked_callServicesAnyway() { testAlwaysCallSystemForMainProfile(isUserUnlocked = false) } @Test - fun test_queryShortcuts_call_services_if_quite_mode_is_enabled_for_main_profile() { + fun test_mainProfileQuiteModeEnabled_callServicesAnyway() { testAlwaysCallSystemForMainProfile(isQuietModeEnabled = true) } @@ -320,16 +417,16 @@ class ShortcutLoaderTest { val callback = mock<Consumer<ShortcutLoader.Result>>() val testSubject = ShortcutLoader( context, + lifecycleOwner.lifecycle, appPredictor, userHandle, false, intentFilter, - executor, - executor, + dispatcher, callback ) - testSubject.queryShortcuts(arrayOf<DisplayResolveInfo>(mock())) + testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock())) verify(appPredictor, never()).requestPredictionUpdate() } @@ -350,23 +447,17 @@ class ShortcutLoaderTest { val callback = mock<Consumer<ShortcutLoader.Result>>() val testSubject = ShortcutLoader( context, + lifecycleOwner.lifecycle, appPredictor, userHandle, true, intentFilter, - executor, - executor, + dispatcher, callback ) - testSubject.queryShortcuts(arrayOf<DisplayResolveInfo>(mock())) + testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock())) verify(appPredictor, times(1)).requestPredictionUpdate() } } - -private class ImmediateExecutor : Executor { - override fun execute(r: Runnable) { - r.run() - } -} |