From 8ce4d4290029b8f94a35ad4ed2459dd4d980b36b Mon Sep 17 00:00:00 2001 From: Andrey Epin Date: Wed, 28 Sep 2022 14:37:37 -0700 Subject: Move shortcut processing logic into separate components. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of the ongoing architectural change, move shortcut processing logic from ChooserListAdapter and ChooserActivity into spaparte components with no functional changes; add unit tests. The new ShrtcutToChooserTargetConverter class now contains previous ChooserActivity’s convertToChooserTarget method; two related test cases were moved from UnboundedChooserActivityTest class into the new component’s test class. The new ShortcutSelectionLogic class now contains previous ChooserListAdapter’s addServiceResults and insertServiceTarget methods; new unit tests added. Test: atest IntentResolverUnitTests:ShortcutSelectionLogicTest Test: atest IntentResolverUnitTests:ShortcutToChooserTargetConverterTest Test: manual test Fix: 249166394 Change-Id: I49baf7d4a435b1f270554fdf6e004875117a01d8 --- java/tests/Android.bp | 2 +- .../android/intentresolver/MockitoKotlinHelpers.kt | 146 +++++++++++ .../intentresolver/ShortcutSelectionLogicTest.kt | 271 +++++++++++++++++++++ .../ShortcutToChooserTargetConverterTest.kt | 175 +++++++++++++ .../src/com/android/intentresolver/TestHelpers.kt | 71 ++++++ .../UnbundledChooserActivityTest.java | 101 -------- 6 files changed, 664 insertions(+), 102 deletions(-) create mode 100644 java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt create mode 100644 java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt create mode 100644 java/tests/src/com/android/intentresolver/ShortcutToChooserTargetConverterTest.kt create mode 100644 java/tests/src/com/android/intentresolver/TestHelpers.kt (limited to 'java/tests') diff --git a/java/tests/Android.bp b/java/tests/Android.bp index fdabc4e0..92974aba 100644 --- a/java/tests/Android.bp +++ b/java/tests/Android.bp @@ -7,7 +7,7 @@ android_test { name: "IntentResolverUnitTests", // Include all test java files. - srcs: ["src/**/*.java"], + srcs: ["src/**/*.java", "src/**/*.kt"], libs: [ "android.test.runner", diff --git a/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt b/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt new file mode 100644 index 00000000..159c6d6a --- /dev/null +++ b/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver + +/** + * Kotlin versions of popular mockito methods that can return null in situations when Kotlin expects + * a non-null value. Kotlin will throw an IllegalStateException when this takes place ("x must not + * be null"). To fix this, we can use methods that modify the return type to be nullable. This + * causes Kotlin to skip the null checks. + * Cloned from frameworks/base/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt + */ + +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatcher +import org.mockito.Mockito +import org.mockito.stubbing.OngoingStubbing + +/** + * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when + * null is returned. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +fun eq(obj: T): T = Mockito.eq(obj) + +/** + * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when + * null is returned. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +fun any(type: Class): T = Mockito.any(type) +inline fun any(): T = any(T::class.java) + +/** + * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when + * null is returned. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +fun argThat(matcher: ArgumentMatcher): T = Mockito.argThat(matcher) + +/** + * Kotlin type-inferred version of Mockito.nullable() + */ +inline fun nullable(): T? = Mockito.nullable(T::class.java) + +/** + * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException + * when null is returned. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +fun capture(argumentCaptor: ArgumentCaptor): T = argumentCaptor.capture() + +/** + * Helper function for creating an argumentCaptor in kotlin. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +inline fun argumentCaptor(): ArgumentCaptor = + ArgumentCaptor.forClass(T::class.java) + +/** + * Helper function for creating new mocks, without the need to pass in a [Class] instance. + * + * Generic T is nullable because implicitly bounded by Any?. + * + * @param apply builder function to simplify stub configuration by improving type inference. + */ +inline fun mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java) + .apply(apply) + +/** + * Helper function for stubbing methods without the need to use backticks. + * + * @see Mockito.when + */ +fun whenever(methodCall: T): OngoingStubbing = Mockito.`when`(methodCall) + +/** + * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when + * kotlin tests are mocking kotlin objects and the methods take non-null parameters: + * + * java.lang.NullPointerException: capture() must not be null + */ +class KotlinArgumentCaptor constructor(clazz: Class) { + private val wrapped: ArgumentCaptor = ArgumentCaptor.forClass(clazz) + fun capture(): T = wrapped.capture() + val value: T + get() = wrapped.value + val allValues: List + get() = wrapped.allValues +} + +/** + * Helper function for creating an argumentCaptor in kotlin. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +inline fun kotlinArgumentCaptor(): KotlinArgumentCaptor = + KotlinArgumentCaptor(T::class.java) + +/** + * Helper function for creating and using a single-use ArgumentCaptor in kotlin. + * + * val captor = argumentCaptor() + * verify(...).someMethod(captor.capture()) + * val captured = captor.value + * + * becomes: + * + * val captured = withArgCaptor { verify(...).someMethod(capture()) } + * + * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException. + */ +inline fun withArgCaptor(block: KotlinArgumentCaptor.() -> Unit): T = + kotlinArgumentCaptor().apply { block() }.value + +/** + * Variant of [withArgCaptor] for capturing multiple arguments. + * + * val captor = argumentCaptor() + * verify(...).someMethod(captor.capture()) + * val captured: List = captor.allValues + * + * becomes: + * + * val capturedList = captureMany { verify(...).someMethod(capture()) } + */ +inline fun captureMany(block: KotlinArgumentCaptor.() -> Unit): List = + kotlinArgumentCaptor().apply{ block() }.allValues diff --git a/java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt b/java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt new file mode 100644 index 00000000..052ad446 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.service.chooser.ChooserTarget +import com.android.intentresolver.chooser.ChooserTargetInfo +import com.android.intentresolver.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +private const val PACKAGE_A = "package.a" +private const val PACKAGE_B = "package.b" +private const val CLASS_NAME = "./MainActivity" + +class ShortcutSelectionLogicTest { + private val packageTargets = HashMap>().apply { + arrayOf(PACKAGE_A, PACKAGE_B).forEach { pkg -> + // shortcuts in reverse priority order + val targets = Array(3) { i -> + createChooserTarget( + "Shortcut $i", + (i + 1).toFloat() / 10f, + ComponentName(pkg, CLASS_NAME), + pkg.shortcutId(i), + ) + } + this[pkg] = targets + } + } + + private operator fun Map>.get(pkg: String, idx: Int) = + this[pkg]?.get(idx) ?: error("missing package $pkg") + + @Test + fun testAddShortcuts_no_limits() { + val serviceResults = ArrayList() + val sc1 = packageTargets[PACKAGE_A, 0] + val sc2 = packageTargets[PACKAGE_A, 1] + val testSubject = ShortcutSelectionLogic( + /* maxShortcutTargetsPerApp = */ 1, + /* applySharingAppLimits = */ false + ) + + val isUpdated = testSubject.addServiceResults( + /* origTarget = */ mock(), + /* origTargetScore = */ 0.1f, + /* targets = */ listOf(sc1, sc2), + /* isShortcutResult = */ true, + /* directShareToShortcutInfos = */ emptyMap(), + /* userContext = */ mock(), + /* mSelectableTargetInfoCommunicator = */ mock(), + /* maxRankedTargets = */ 4, + /* serviceTargets = */ serviceResults + ) + + assertTrue("Updates are expected", isUpdated) + assertShortcutsInOrder( + listOf(sc2, sc1), + serviceResults, + "Two shortcuts are expected as we do not apply per-app shortcut limit" + ) + } + + @Test + fun testAddShortcuts_same_package_with_per_package_limit() { + val serviceResults = ArrayList() + val sc1 = packageTargets[PACKAGE_A, 0] + val sc2 = packageTargets[PACKAGE_A, 1] + val testSubject = ShortcutSelectionLogic( + /* maxShortcutTargetsPerApp = */ 1, + /* applySharingAppLimits = */ true + ) + + val isUpdated = testSubject.addServiceResults( + /* origTarget = */ mock(), + /* origTargetScore = */ 0.1f, + /* targets = */ listOf(sc1, sc2), + /* isShortcutResult = */ true, + /* directShareToShortcutInfos = */ emptyMap(), + /* userContext = */ mock(), + /* mSelectableTargetInfoCommunicator = */ mock(), + /* maxRankedTargets = */ 4, + /* serviceTargets = */ serviceResults + ) + + assertTrue("Updates are expected", isUpdated) + assertShortcutsInOrder( + listOf(sc2), + serviceResults, + "One shortcut is expected as we apply per-app shortcut limit" + ) + } + + @Test + fun testAddShortcuts_same_package_no_per_app_limit_with_target_limit() { + val serviceResults = ArrayList() + val sc1 = packageTargets[PACKAGE_A, 0] + val sc2 = packageTargets[PACKAGE_A, 1] + val testSubject = ShortcutSelectionLogic( + /* maxShortcutTargetsPerApp = */ 1, + /* applySharingAppLimits = */ false + ) + + val isUpdated = testSubject.addServiceResults( + /* origTarget = */ mock(), + /* origTargetScore = */ 0.1f, + /* targets = */ listOf(sc1, sc2), + /* isShortcutResult = */ true, + /* directShareToShortcutInfos = */ emptyMap(), + /* userContext = */ mock(), + /* mSelectableTargetInfoCommunicator = */ mock(), + /* maxRankedTargets = */ 1, + /* serviceTargets = */ serviceResults + ) + + assertTrue("Updates are expected", isUpdated) + assertShortcutsInOrder( + listOf(sc2), + serviceResults, + "One shortcut is expected as we apply overall shortcut limit" + ) + } + + @Test + fun testAddShortcuts_different_packages_with_per_package_limit() { + val serviceResults = ArrayList() + val pkgAsc1 = packageTargets[PACKAGE_A, 0] + val pkgAsc2 = packageTargets[PACKAGE_A, 1] + val pkgBsc1 = packageTargets[PACKAGE_B, 0] + val pkgBsc2 = packageTargets[PACKAGE_B, 1] + val testSubject = ShortcutSelectionLogic( + /* maxShortcutTargetsPerApp = */ 1, + /* applySharingAppLimits = */ true + ) + + testSubject.addServiceResults( + /* origTarget = */ mock(), + /* origTargetScore = */ 0.1f, + /* targets = */ listOf(pkgAsc1, pkgAsc2), + /* isShortcutResult = */ true, + /* directShareToShortcutInfos = */ emptyMap(), + /* userContext = */ mock(), + /* mSelectableTargetInfoCommunicator = */ mock(), + /* maxRankedTargets = */ 4, + /* serviceTargets = */ serviceResults + ) + testSubject.addServiceResults( + /* origTarget = */ mock(), + /* origTargetScore = */ 0.2f, + /* targets = */ listOf(pkgBsc1, pkgBsc2), + /* isShortcutResult = */ true, + /* directShareToShortcutInfos = */ emptyMap(), + /* userContext = */ mock(), + /* mSelectableTargetInfoCommunicator = */ mock(), + /* maxRankedTargets = */ 4, + /* serviceTargets = */ serviceResults + ) + + assertShortcutsInOrder( + listOf(pkgBsc2, pkgAsc2), + serviceResults, + "Two shortcuts are expected as we apply per-app shortcut limit" + ) + } + + @Test + fun testAddShortcuts_pinned_shortcut() { + val serviceResults = ArrayList() + val sc1 = packageTargets[PACKAGE_A, 0] + val sc2 = packageTargets[PACKAGE_A, 1] + val testSubject = ShortcutSelectionLogic( + /* maxShortcutTargetsPerApp = */ 1, + /* applySharingAppLimits = */ false + ) + + val isUpdated = testSubject.addServiceResults( + /* origTarget = */ mock(), + /* origTargetScore = */ 0.1f, + /* targets = */ listOf(sc1, sc2), + /* isShortcutResult = */ true, + /* directShareToShortcutInfos = */ mapOf( + sc1 to createShortcutInfo( + PACKAGE_A.shortcutId(1), + sc1.componentName, 1).apply { + addFlags(ShortcutInfo.FLAG_PINNED) + } + ), + /* userContext = */ mock(), + /* mSelectableTargetInfoCommunicator = */ mock(), + /* maxRankedTargets = */ 4, + /* serviceTargets = */ serviceResults + ) + + assertTrue("Updates are expected", isUpdated) + assertShortcutsInOrder( + listOf(sc1, sc2), + serviceResults, + "Two shortcuts are expected as we do not apply per-app shortcut limit" + ) + } + + @Test + fun test_available_caller_shortcuts_count_is_limited() { + val serviceResults = ArrayList() + val sc1 = packageTargets[PACKAGE_A, 0] + val sc2 = packageTargets[PACKAGE_A, 1] + val sc3 = packageTargets[PACKAGE_A, 2] + val testSubject = ShortcutSelectionLogic( + /* maxShortcutTargetsPerApp = */ 1, + /* applySharingAppLimits = */ true + ) + val targetInfoCommunicator = mock { + whenever(targetIntent).thenReturn(Intent()) + } + val context = mock { + whenever(packageManager).thenReturn(mock()) + } + + testSubject.addServiceResults( + /* origTarget = */ null, + /* origTargetScore = */ 0f, + /* targets = */ listOf(sc1, sc2, sc3), + /* isShortcutResult = */ false /*isShortcutResult*/, + /* directShareToShortcutInfos = */ emptyMap(), + /* userContext = */ context, + /* mSelectableTargetInfoCommunicator = */ targetInfoCommunicator, + /* maxRankedTargets = */ 4, + /* serviceTargets = */ serviceResults + ) + + assertShortcutsInOrder( + listOf(sc3, sc2), + serviceResults, + "At most two caller-provided shortcuts are allowed" + ) + } + + private fun assertShortcutsInOrder( + expected: List, actual: List, msg: String? = "" + ) { + assertEquals(msg, expected.size, actual.size) + for (i in expected.indices) { + assertEquals( + "Unexpected item at position $i", + expected[i], + actual[i].chooserTarget + ) + } + } + + private fun String.shortcutId(id: Int) = "$this.$id" +} \ No newline at end of file diff --git a/java/tests/src/com/android/intentresolver/ShortcutToChooserTargetConverterTest.kt b/java/tests/src/com/android/intentresolver/ShortcutToChooserTargetConverterTest.kt new file mode 100644 index 00000000..5529e714 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/ShortcutToChooserTargetConverterTest.kt @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver + +import android.app.prediction.AppTarget +import android.content.ComponentName +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.content.pm.ShortcutManager.ShareShortcutInfo +import android.service.chooser.ChooserTarget +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Test + +private const val PACKAGE = "org.package" + +class ShortcutToChooserTargetConverterTest { + private val testSubject = ShortcutToChooserTargetConverter() + private val ranks = arrayOf(3 ,7, 1 ,3) + private val shortcuts = ranks + .foldIndexed(ArrayList(ranks.size)) { i, acc, rank -> + val id = i + 1 + acc.add( + createShareShortcutInfo( + id = "id-$i", + componentName = ComponentName(PACKAGE, "Class$id"), + rank, + ) + ) + acc + } + + @Test + fun testConvertToChooserTarget_predictionService() { + val appTargets = shortcuts.map { createAppTarget(it.shortcutInfo) } + val expectedOrderAllShortcuts = intArrayOf(0, 1, 2, 3) + val expectedScoreAllShortcuts = floatArrayOf(1.0f, 0.99f, 0.98f, 0.97f) + val appTargetCache = HashMap() + val shortcutInfoCache = HashMap() + + var chooserTargets = testSubject.convertToChooserTarget( + shortcuts, + shortcuts, + appTargets, + appTargetCache, + shortcutInfoCache, + ) + + assertCorrectShortcutToChooserTargetConversion( + shortcuts, + chooserTargets, + expectedOrderAllShortcuts, + expectedScoreAllShortcuts, + ) + assertAppTargetCache(chooserTargets, appTargetCache) + assertShortcutInfoCache(chooserTargets, shortcutInfoCache) + + val subset = shortcuts.subList(1, shortcuts.size) + val expectedOrderSubset = intArrayOf(1, 2, 3) + val expectedScoreSubset = floatArrayOf(0.99f, 0.98f, 0.97f) + appTargetCache.clear() + shortcutInfoCache.clear() + + chooserTargets = testSubject.convertToChooserTarget( + subset, + shortcuts, + appTargets, + appTargetCache, + shortcutInfoCache, + ) + + assertCorrectShortcutToChooserTargetConversion( + shortcuts, + chooserTargets, + expectedOrderSubset, + expectedScoreSubset, + ) + assertAppTargetCache(chooserTargets, appTargetCache) + assertShortcutInfoCache(chooserTargets, shortcutInfoCache) + } + + @Test + fun testConvertToChooserTarget_shortcutManager() { + val testSubject = ShortcutToChooserTargetConverter() + val expectedOrderAllShortcuts = intArrayOf(2, 0, 3, 1) + val expectedScoreAllShortcuts = floatArrayOf(1.0f, 0.99f, 0.99f, 0.98f) + val shortcutInfoCache = HashMap() + + var chooserTargets = testSubject.convertToChooserTarget( + shortcuts, + shortcuts, + null, + null, + shortcutInfoCache, + ) + + assertCorrectShortcutToChooserTargetConversion( + shortcuts, chooserTargets, + expectedOrderAllShortcuts, expectedScoreAllShortcuts + ) + assertShortcutInfoCache(chooserTargets, shortcutInfoCache) + + val subset: MutableList = java.util.ArrayList() + subset.add(shortcuts[1]) + subset.add(shortcuts[2]) + subset.add(shortcuts[3]) + val expectedOrderSubset = intArrayOf(2, 3, 1) + val expectedScoreSubset = floatArrayOf(1.0f, 0.99f, 0.98f) + shortcutInfoCache.clear() + + chooserTargets = testSubject.convertToChooserTarget( + subset, + shortcuts, + null, + null, + shortcutInfoCache, + ) + + assertCorrectShortcutToChooserTargetConversion( + shortcuts, chooserTargets, + expectedOrderSubset, expectedScoreSubset + ) + assertShortcutInfoCache(chooserTargets, shortcutInfoCache) + } + + private fun assertCorrectShortcutToChooserTargetConversion( + shortcuts: List, + chooserTargets: List, + expectedOrder: IntArray, + expectedScores: FloatArray, + ) { + assertEquals("Unexpected ChooserTarget count", expectedOrder.size, chooserTargets.size) + for (i in chooserTargets.indices) { + val ct = chooserTargets[i] + val si = shortcuts[expectedOrder[i]].shortcutInfo + val cn = shortcuts[expectedOrder[i]].targetComponent + assertEquals(si.id, ct.intentExtras.getString(Intent.EXTRA_SHORTCUT_ID)) + assertEquals(si.label, ct.title) + assertEquals(expectedScores[i], ct.score) + assertEquals(cn, ct.componentName) + } + } + + private fun assertAppTargetCache( + chooserTargets: List, cache: Map + ) { + for (ct in chooserTargets) { + val target = cache[ct] + assertNotNull("AppTarget is missing", target) + } + } + + private fun assertShortcutInfoCache( + chooserTargets: List, cache: Map + ) { + for (ct in chooserTargets) { + val si = cache[ct] + assertNotNull("AppTarget is missing", si) + } + } +} diff --git a/java/tests/src/com/android/intentresolver/TestHelpers.kt b/java/tests/src/com/android/intentresolver/TestHelpers.kt new file mode 100644 index 00000000..f4b83249 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/TestHelpers.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver + +import android.app.prediction.AppTarget +import android.app.prediction.AppTargetId +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.content.pm.ShortcutManager.ShareShortcutInfo +import android.os.Bundle +import android.service.chooser.ChooserTarget +import org.mockito.Mockito.`when` as whenever + +internal fun createShareShortcutInfo( + id: String, + componentName: ComponentName, + rank: Int +): ShareShortcutInfo = + ShareShortcutInfo( + createShortcutInfo(id, componentName, rank), + componentName + ) + +internal fun createShortcutInfo( + id: String, + componentName: ComponentName, + rank: Int +): ShortcutInfo { + val context = mock() + whenever(context.packageName).thenReturn(componentName.packageName) + return ShortcutInfo.Builder(context, id) + .setShortLabel("Short Label $id") + .setLongLabel("Long Label $id") + .setActivity(componentName) + .setRank(rank) + .build() +} + +internal fun createAppTarget(shortcutInfo: ShortcutInfo) = + AppTarget( + AppTargetId(shortcutInfo.id), + shortcutInfo, + shortcutInfo.activity?.className ?: error("missing activity info") + ) + +internal fun createChooserTarget( + title: String, score: Float, componentName: ComponentName, shortcutId: String +): ChooserTarget = + ChooserTarget( + title, + null, + score, + componentName, + Bundle().apply { putString(Intent.EXTRA_SHORTCUT_ID, shortcutId) } + ) diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java index b901fc1e..089bb8bc 100644 --- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java +++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java @@ -1347,92 +1347,6 @@ public class UnbundledChooserActivityTest { is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST)); } - @Test - public void testConvertToChooserTarget_predictionService() { - Intent sendIntent = createSendTextIntent(); - List resolvedComponentInfos = createResolvedComponentsForTest(2); - when( - ChooserActivityOverrideData - .getInstance() - .resolverListController - .getResolversForIntent( - Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.isA(List.class))) - .thenReturn(resolvedComponentInfos); - - final ChooserActivity activity = - mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); - waitForIdle(); - - List shortcuts = createShortcuts(activity); - - int[] expectedOrderAllShortcuts = {0, 1, 2, 3}; - float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.98f, 0.97f}; - - List chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts, - null, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); - assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, - expectedOrderAllShortcuts, expectedScoreAllShortcuts); - - List subset = new ArrayList<>(); - subset.add(shortcuts.get(1)); - subset.add(shortcuts.get(2)); - subset.add(shortcuts.get(3)); - - int[] expectedOrderSubset = {1, 2, 3}; - float[] expectedScoreSubset = {0.99f, 0.98f, 0.97f}; - - chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null, - TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); - assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, - expectedOrderSubset, expectedScoreSubset); - } - - @Test - public void testConvertToChooserTarget_shortcutManager() { - Intent sendIntent = createSendTextIntent(); - List resolvedComponentInfos = createResolvedComponentsForTest(2); - when( - ChooserActivityOverrideData - .getInstance() - .resolverListController - .getResolversForIntent( - Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.anyBoolean(), - Mockito.isA(List.class))) - .thenReturn(resolvedComponentInfos); - - final ChooserActivity activity = - mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); - waitForIdle(); - - List shortcuts = createShortcuts(activity); - - int[] expectedOrderAllShortcuts = {2, 0, 3, 1}; - float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.99f, 0.98f}; - - List chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts, - null, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER); - assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, - expectedOrderAllShortcuts, expectedScoreAllShortcuts); - - List subset = new ArrayList<>(); - subset.add(shortcuts.get(1)); - subset.add(shortcuts.get(2)); - subset.add(shortcuts.get(3)); - - int[] expectedOrderSubset = {2, 3, 1}; - float[] expectedScoreSubset = {1.0f, 0.99f, 0.98f}; - - chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null, - TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER); - assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, - expectedOrderSubset, expectedScoreSubset); - } - // This test is too long and too slow and should not be taken as an example for future tests. @Test @Ignore public void testDirectTargetSelectionLogging() throws InterruptedException { @@ -3092,21 +3006,6 @@ public class UnbundledChooserActivityTest { return shortcuts; } - private void assertCorrectShortcutToChooserTargetConversion(List shortcuts, - List chooserTargets, int[] expectedOrder, float[] expectedScores) { - assertEquals(expectedOrder.length, chooserTargets.size()); - for (int i = 0; i < chooserTargets.size(); i++) { - ChooserTarget ct = chooserTargets.get(i); - ShortcutInfo si = shortcuts.get(expectedOrder[i]).getShortcutInfo(); - ComponentName cn = shortcuts.get(expectedOrder[i]).getTargetComponent(); - - assertEquals(si.getId(), ct.getIntentExtras().getString(Intent.EXTRA_SHORTCUT_ID)); - assertEquals(si.getShortLabel(), ct.getTitle()); - assertThat(Math.abs(expectedScores[i] - ct.getScore()) < 0.000001, is(true)); - assertEquals(cn.flattenToString(), ct.getComponentName().flattenToString()); - } - } - private void markWorkProfileUserAvailable() { ChooserActivityOverrideData.getInstance().workProfileUserHandle = UserHandle.of(10); } -- cgit v1.2.3-59-g8ed1b