summaryrefslogtreecommitdiff
path: root/java/tests
diff options
context:
space:
mode:
author Andrey Epin <ayepin@google.com> 2022-10-04 21:36:28 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2022-10-04 21:36:28 +0000
commite2c09d4de735846ff81c86c573dd4ea5d606b498 (patch)
tree91f5184e792cc061ce0f56380c1d612d7fda8be5 /java/tests
parente95a98abd7d7f7333e48d011176429842d7c398b (diff)
parent539dad4c7b79249c44ae826d72d119a97291f45e (diff)
Merge "Move shortcut processing logic into separate components." into tm-qpr-dev am: e4b8aee927 am: 539dad4c7b
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/IntentResolver/+/20076592 Change-Id: I38e8b999b29632f0ad28005979e70486119c6f34 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'java/tests')
-rw-r--r--java/tests/Android.bp2
-rw-r--r--java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt146
-rw-r--r--java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt271
-rw-r--r--java/tests/src/com/android/intentresolver/ShortcutToChooserTargetConverterTest.kt175
-rw-r--r--java/tests/src/com/android/intentresolver/TestHelpers.kt71
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java101
6 files changed, 664 insertions, 102 deletions
diff --git a/java/tests/Android.bp b/java/tests/Android.bp
index 70c497f5..d19661e8 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 <T> eq(obj: T): T = Mockito.eq<T>(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 <T> any(type: Class<T>): T = Mockito.any<T>(type)
+inline fun <reified T> 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 <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher)
+
+/**
+ * Kotlin type-inferred version of Mockito.nullable()
+ */
+inline fun <reified T> 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 <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+
+/**
+ * Helper function for creating an argumentCaptor in kotlin.
+ *
+ * Generic T is nullable because implicitly bounded by Any?.
+ */
+inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
+ 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 <reified T : Any> 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 <T> whenever(methodCall: T): OngoingStubbing<T> = 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<T> constructor(clazz: Class<T>) {
+ private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz)
+ fun capture(): T = wrapped.capture()
+ val value: T
+ get() = wrapped.value
+ val allValues: List<T>
+ get() = wrapped.allValues
+}
+
+/**
+ * Helper function for creating an argumentCaptor in kotlin.
+ *
+ * Generic T is nullable because implicitly bounded by Any?.
+ */
+inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> =
+ KotlinArgumentCaptor(T::class.java)
+
+/**
+ * Helper function for creating and using a single-use ArgumentCaptor in kotlin.
+ *
+ * val captor = argumentCaptor<Foo>()
+ * verify(...).someMethod(captor.capture())
+ * val captured = captor.value
+ *
+ * becomes:
+ *
+ * val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
+ *
+ * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
+ */
+inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
+ kotlinArgumentCaptor<T>().apply { block() }.value
+
+/**
+ * Variant of [withArgCaptor] for capturing multiple arguments.
+ *
+ * val captor = argumentCaptor<Foo>()
+ * verify(...).someMethod(captor.capture())
+ * val captured: List<Foo> = captor.allValues
+ *
+ * becomes:
+ *
+ * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
+ */
+inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> =
+ kotlinArgumentCaptor<T>().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<String, Array<ChooserTarget>>().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<String, Array<ChooserTarget>>.get(pkg: String, idx: Int) =
+ this[pkg]?.get(idx) ?: error("missing package $pkg")
+
+ @Test
+ fun testAddShortcuts_no_limits() {
+ val serviceResults = ArrayList<ChooserTargetInfo>()
+ 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<ChooserTargetInfo>()
+ 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<ChooserTargetInfo>()
+ 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<ChooserTargetInfo>()
+ 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<ChooserTargetInfo>()
+ 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<ChooserTargetInfo>()
+ 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<SelectableTargetInfoCommunicator> {
+ whenever(targetIntent).thenReturn(Intent())
+ }
+ val context = mock<Context> {
+ 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<ChooserTarget>, actual: List<ChooserTargetInfo>, 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<ShareShortcutInfo>(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<ChooserTarget, AppTarget>()
+ val shortcutInfoCache = HashMap<ChooserTarget, ShortcutInfo>()
+
+ 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<ChooserTarget, ShortcutInfo>()
+
+ var chooserTargets = testSubject.convertToChooserTarget(
+ shortcuts,
+ shortcuts,
+ null,
+ null,
+ shortcutInfoCache,
+ )
+
+ assertCorrectShortcutToChooserTargetConversion(
+ shortcuts, chooserTargets,
+ expectedOrderAllShortcuts, expectedScoreAllShortcuts
+ )
+ assertShortcutInfoCache(chooserTargets, shortcutInfoCache)
+
+ val subset: MutableList<ShareShortcutInfo> = 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<ShareShortcutInfo>,
+ chooserTargets: List<ChooserTarget>,
+ 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<ChooserTarget>, cache: Map<ChooserTarget, AppTarget>
+ ) {
+ for (ct in chooserTargets) {
+ val target = cache[ct]
+ assertNotNull("AppTarget is missing", target)
+ }
+ }
+
+ private fun assertShortcutInfoCache(
+ chooserTargets: List<ChooserTarget>, cache: Map<ChooserTarget, ShortcutInfo>
+ ) {
+ 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<Context>()
+ 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 e9baf893..7a590584 100644
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
+++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
@@ -1370,92 +1370,6 @@ public class UnbundledChooserActivityTest {
is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
}
- @Test
- public void testConvertToChooserTarget_predictionService() {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> 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<ShareShortcutInfo> shortcuts = createShortcuts(activity);
-
- int[] expectedOrderAllShortcuts = {0, 1, 2, 3};
- float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.98f, 0.97f};
-
- List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
- null, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
- assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
- expectedOrderAllShortcuts, expectedScoreAllShortcuts);
-
- List<ShareShortcutInfo> 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<ResolvedComponentInfo> 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<ShareShortcutInfo> shortcuts = createShortcuts(activity);
-
- int[] expectedOrderAllShortcuts = {2, 0, 3, 1};
- float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.99f, 0.98f};
-
- List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
- null, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
- assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
- expectedOrderAllShortcuts, expectedScoreAllShortcuts);
-
- List<ShareShortcutInfo> 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 {
@@ -3112,21 +3026,6 @@ public class UnbundledChooserActivityTest {
return shortcuts;
}
- private void assertCorrectShortcutToChooserTargetConversion(List<ShareShortcutInfo> shortcuts,
- List<ChooserTarget> 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);
}