diff options
| author | 2023-11-22 09:21:08 -0500 | |
|---|---|---|
| committer | 2023-11-22 09:24:53 -0500 | |
| commit | b34ae5d8650a1a4f074db68cb64c3b9648cb9275 (patch) | |
| tree | 368b28971e081060582ff39ee234421acf417b02 /java/tests | |
| parent | 55da290431232e15b5a8c175561ea57796b572d5 (diff) | |
Splits list controller into interfaces
Each interface has a single concern, allowing for list controllers to be
built by composition.
New list controllers are not currently in use, but will be in a future
change once resolver comparators and list adapters get updated.
Test: atest com.android.intentresolver.v2.listcontroller
BUG: 302113519
Change-Id: Ie1d24571c07d1408aa80f8a86311d0fee5e78255
Diffstat (limited to 'java/tests')
11 files changed, 1602 insertions, 3 deletions
diff --git a/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java b/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java index 5f0ead7b..2140a67d 100644 --- a/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java +++ b/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java @@ -118,14 +118,14 @@ public class AbstractResolverComparatorTest { Lists.newArrayList(context.getUser()), promoteToFirst) { @Override - int compare(ResolveInfo lhs, ResolveInfo rhs) { + public int compare(ResolveInfo lhs, ResolveInfo rhs) { // Used for testing pinning, so we should never get here --- the overrides // should determine the result instead. return 1; } @Override - void doCompute(List<ResolvedComponentInfo> targets) {} + public void doCompute(List<ResolvedComponentInfo> targets) {} @Override public float getScore(TargetInfo targetInfo) { @@ -133,7 +133,7 @@ public class AbstractResolverComparatorTest { } @Override - void handleResultMessage(Message message) {} + public void handleResultMessage(Message message) {} }; return testComparator; } diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt new file mode 100644 index 00000000..59494bed --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import com.android.intentresolver.ChooserRequestParameters +import com.android.intentresolver.whenever +import com.google.common.collect.ImmutableList +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +class ChooserRequestFilteredComponentsTest { + + @Mock lateinit var mockChooserRequestParameters: ChooserRequestParameters + + private lateinit var chooserRequestFilteredComponents: ChooserRequestFilteredComponents + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + chooserRequestFilteredComponents = + ChooserRequestFilteredComponents(mockChooserRequestParameters) + } + + @Test + fun isComponentFiltered_returnsRequestParametersFilteredState() { + // Arrange + whenever(mockChooserRequestParameters.filteredComponentNames) + .thenReturn( + ImmutableList.of(ComponentName("FilteredPackage", "FilteredClass")), + ) + val testComponent = ComponentName("TestPackage", "TestClass") + val filteredComponent = ComponentName("FilteredPackage", "FilteredClass") + + // Act + val result = chooserRequestFilteredComponents.isComponentFiltered(testComponent) + val filteredResult = chooserRequestFilteredComponents.isComponentFiltered(filteredComponent) + + // Assert + assertThat(result).isFalse() + assertThat(filteredResult).isTrue() + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt new file mode 100644 index 00000000..ce40567e --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.ResolveInfo +import android.content.res.Configuration +import android.content.res.Resources +import android.os.Message +import android.os.UserHandle +import com.android.intentresolver.ResolvedComponentInfo +import com.android.intentresolver.chooser.TargetInfo +import com.android.intentresolver.model.AbstractResolverComparator +import com.android.intentresolver.whenever +import java.util.Locale +import org.mockito.Mockito + +class FakeResolverComparator( + context: Context = + Mockito.mock(Context::class.java).also { + val mockResources = Mockito.mock(Resources::class.java) + whenever(it.resources).thenReturn(mockResources) + whenever(mockResources.configuration) + .thenReturn(Configuration().apply { setLocale(Locale.US) }) + }, + targetIntent: Intent = Intent("TestAction"), + resolvedActivityUserSpaceList: List<UserHandle> = emptyList(), + promoteToFirst: ComponentName? = null, +) : + AbstractResolverComparator( + context, + targetIntent, + resolvedActivityUserSpaceList, + promoteToFirst, + ) { + var lastUpdateModel: TargetInfo? = null + private set + var lastUpdateChooserCounts: Triple<String, UserHandle, String>? = null + private set + var destroyCalled = false + private set + + override fun compare(lhs: ResolveInfo?, rhs: ResolveInfo?): Int = + lhs!!.activityInfo.packageName.compareTo(rhs!!.activityInfo.packageName) + + override fun doCompute(targets: MutableList<ResolvedComponentInfo>?) {} + + override fun getScore(targetInfo: TargetInfo?): Float = 1.23f + + override fun handleResultMessage(message: Message?) {} + + override fun updateModel(targetInfo: TargetInfo?) { + lastUpdateModel = targetInfo + } + + override fun updateChooserCounts( + packageName: String, + user: UserHandle, + action: String, + ) { + lastUpdateChooserCounts = Triple(packageName, user, action) + } + + override fun destroy() { + destroyCalled = true + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt new file mode 100644 index 00000000..396505e6 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import com.android.intentresolver.ChooserRequestParameters +import com.android.intentresolver.whenever +import com.google.common.collect.ImmutableList +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +class FilterableComponentsTest { + + @Mock lateinit var mockChooserRequestParameters: ChooserRequestParameters + + private val unfilteredComponent = ComponentName("TestPackage", "TestClass") + private val filteredComponent = ComponentName("FilteredPackage", "FilteredClass") + private val noComponentFiltering = NoComponentFiltering() + + private lateinit var chooserRequestFilteredComponents: ChooserRequestFilteredComponents + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + chooserRequestFilteredComponents = + ChooserRequestFilteredComponents(mockChooserRequestParameters) + } + + @Test + fun isComponentFiltered_noComponentFiltering_neverFilters() { + // Arrange + + // Act + val unfilteredResult = noComponentFiltering.isComponentFiltered(unfilteredComponent) + val filteredResult = noComponentFiltering.isComponentFiltered(filteredComponent) + + // Assert + assertThat(unfilteredResult).isFalse() + assertThat(filteredResult).isFalse() + } + + @Test + fun isComponentFiltered_chooserRequestFilteredComponents_filtersAccordingToChooserRequest() { + // Arrange + whenever(mockChooserRequestParameters.filteredComponentNames) + .thenReturn( + ImmutableList.of(filteredComponent), + ) + + // Act + val unfilteredResult = + chooserRequestFilteredComponents.isComponentFiltered(unfilteredComponent) + val filteredResult = chooserRequestFilteredComponents.isComponentFiltered(filteredComponent) + + // Assert + assertThat(unfilteredResult).isFalse() + assertThat(filteredResult).isTrue() + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt new file mode 100644 index 00000000..09f6d373 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.net.Uri +import android.os.UserHandle +import com.android.intentresolver.any +import com.android.intentresolver.eq +import com.android.intentresolver.kotlinArgumentCaptor +import com.android.intentresolver.whenever +import com.google.common.truth.Truth.assertThat +import java.lang.IndexOutOfBoundsException +import org.junit.Assert.assertThrows +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +class IntentResolverTest { + + @Mock lateinit var mockPackageManager: PackageManager + + private lateinit var intentResolver: IntentResolver + + private val fakePinnableComponents = + object : PinnableComponents { + override fun isComponentPinned(name: ComponentName): Boolean { + return name.packageName == "PinnedPackage" + } + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + intentResolver = + IntentResolverImpl(mockPackageManager, ResolveListDeduperImpl(fakePinnableComponents)) + } + + @Test + fun getResolversForIntentAsUser_noIntents_returnsEmptyList() { + // Arrange + val testIntents = emptyList<Intent>() + + // Act + val result = + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + assertThat(result).isEmpty() + } + + @Test + fun getResolversForIntentAsUser_noResolveInfo_returnsEmptyList() { + // Arrange + val testIntents = listOf(Intent("TestAction")) + val testResolveInfos = emptyList<ResolveInfo>() + whenever(mockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), any<UserHandle>())) + .thenReturn(testResolveInfos) + + // Act + val result = + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + assertThat(result).isEmpty() + } + + @Test + fun getResolversForIntentAsUser_returnsAllResolveComponentInfo() { + // Arrange + val testIntent1 = Intent("TestAction1") + val testIntent2 = Intent("TestAction2") + val testIntents = listOf(testIntent1, testIntent2) + val testResolveInfos1 = + listOf( + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage1" + activityInfo.name = "TestClass1" + }, + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage2" + activityInfo.name = "TestClass2" + }, + ) + val testResolveInfos2 = + listOf( + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage3" + activityInfo.name = "TestClass3" + }, + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage4" + activityInfo.name = "TestClass4" + }, + ) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + eq(testIntent1), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos1) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + eq(testIntent2), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos2) + + // Act + val result = + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + result.forEachIndexed { index, it -> + val postfix = index + 1 + assertThat(it.name.packageName).isEqualTo("TestPackage$postfix") + assertThat(it.name.className).isEqualTo("TestClass$postfix") + assertThrows(IndexOutOfBoundsException::class.java) { it.getIntentAt(1) } + } + assertThat(result.map { it.getIntentAt(0) }) + .containsExactly( + testIntent1, + testIntent1, + testIntent2, + testIntent2, + ) + } + + @Test + fun getResolversForIntentAsUser_resolveInfoWithoutUserHandle_isSkipped() { + // Arrange + val testIntent = Intent("TestAction") + val testIntents = listOf(testIntent) + val testResolveInfos = + listOf( + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage" + activityInfo.name = "TestClass" + }, + ) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + any(), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos) + + // Act + val result = + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + assertThat(result).isEmpty() + } + + @Test + fun getResolversForIntentAsUser_duplicateComponents_areCombined() { + // Arrange + val testIntent1 = Intent("TestAction1") + val testIntent2 = Intent("TestAction2") + val testIntents = listOf(testIntent1, testIntent2) + val testResolveInfos1 = + listOf( + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "DuplicatePackage" + activityInfo.name = "DuplicateClass" + }, + ) + val testResolveInfos2 = + listOf( + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "DuplicatePackage" + activityInfo.name = "DuplicateClass" + }, + ) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + eq(testIntent1), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos1) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + eq(testIntent2), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos2) + + // Act + val result = + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + assertThat(result).hasSize(1) + with(result.first()) { + assertThat(name.packageName).isEqualTo("DuplicatePackage") + assertThat(name.className).isEqualTo("DuplicateClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent1) + assertThat(getIntentAt(1)).isEqualTo(testIntent2) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(2) } + } + } + + @Test + fun getResolversForIntentAsUser_pinnedComponentsArePinned() { + // Arrange + val testIntent1 = Intent("TestAction1") + val testIntent2 = Intent("TestAction2") + val testIntents = listOf(testIntent1, testIntent2) + val testResolveInfos1 = + listOf( + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "UnpinnedPackage" + activityInfo.name = "UnpinnedClass" + }, + ) + val testResolveInfos2 = + listOf( + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "PinnedPackage" + activityInfo.name = "PinnedClass" + }, + ) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + eq(testIntent1), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos1) + whenever( + mockPackageManager.queryIntentActivitiesAsUser( + eq(testIntent2), + anyInt(), + any<UserHandle>(), + ) + ) + .thenReturn(testResolveInfos2) + + // Act + val result = + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + assertThat(result.map { it.isPinned }).containsExactly(false, true) + } + + @Test + fun getResolversForIntentAsUser_whenNoExtraBehavior_usesBaseFlags() { + // Arrange + val baseFlags = + PackageManager.MATCH_DIRECT_BOOT_AWARE or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE or + PackageManager.MATCH_CLONE_PROFILE + val testIntent = Intent() + val testIntents = listOf(testIntent) + + // Act + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + val flags = kotlinArgumentCaptor<Int>() + verify(mockPackageManager) + .queryIntentActivitiesAsUser( + any(), + flags.capture(), + any<UserHandle>(), + ) + assertThat(flags.value).isEqualTo(baseFlags) + } + + @Test + fun getResolversForIntentAsUser_whenShouldGetResolvedFilter_usesGetResolvedFilterFlag() { + // Arrange + val testIntent = Intent() + val testIntents = listOf(testIntent) + + // Act + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = true, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + val flags = kotlinArgumentCaptor<Int>() + verify(mockPackageManager) + .queryIntentActivitiesAsUser( + any(), + flags.capture(), + any<UserHandle>(), + ) + assertThat(flags.value and PackageManager.GET_RESOLVED_FILTER) + .isEqualTo(PackageManager.GET_RESOLVED_FILTER) + } + + @Test + fun getResolversForIntentAsUser_whenShouldGetActivityMetadata_usesGetMetaDataFlag() { + // Arrange + val testIntent = Intent() + val testIntents = listOf(testIntent) + + // Act + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = true, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + val flags = kotlinArgumentCaptor<Int>() + verify(mockPackageManager) + .queryIntentActivitiesAsUser( + any(), + flags.capture(), + any<UserHandle>(), + ) + assertThat(flags.value and PackageManager.GET_META_DATA) + .isEqualTo(PackageManager.GET_META_DATA) + } + + @Test + fun getResolversForIntentAsUser_whenShouldGetOnlyDefaultActivities_usesMatchDefaultOnlyFlag() { + // Arrange + val testIntent = Intent() + val testIntents = listOf(testIntent) + + // Act + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = true, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + val flags = kotlinArgumentCaptor<Int>() + verify(mockPackageManager) + .queryIntentActivitiesAsUser( + any(), + flags.capture(), + any<UserHandle>(), + ) + assertThat(flags.value and PackageManager.MATCH_DEFAULT_ONLY) + .isEqualTo(PackageManager.MATCH_DEFAULT_ONLY) + } + + @Test + fun getResolversForIntentAsUser_whenWebIntent_usesMatchInstantFlag() { + // Arrange + val testIntent = Intent(Intent.ACTION_VIEW, Uri.fromParts(IntentFilter.SCHEME_HTTP, "", "")) + val testIntents = listOf(testIntent) + + // Act + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + val flags = kotlinArgumentCaptor<Int>() + verify(mockPackageManager) + .queryIntentActivitiesAsUser( + any(), + flags.capture(), + any<UserHandle>(), + ) + assertThat(flags.value and PackageManager.MATCH_INSTANT) + .isEqualTo(PackageManager.MATCH_INSTANT) + } + + @Test + fun getResolversForIntentAsUser_whenActivityMatchExternalFlag_usesMatchInstantFlag() { + // Arrange + val testIntent = Intent().addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) + val testIntents = listOf(testIntent) + + // Act + intentResolver.getResolversForIntentAsUser( + shouldGetResolvedFilter = false, + shouldGetActivityMetadata = false, + shouldGetOnlyDefaultActivities = false, + intents = testIntents, + userHandle = UserHandle(456), + ) + + // Assert + val flags = kotlinArgumentCaptor<Int>() + verify(mockPackageManager) + .queryIntentActivitiesAsUser( + any(), + flags.capture(), + any<UserHandle>(), + ) + assertThat(flags.value and PackageManager.MATCH_INSTANT) + .isEqualTo(PackageManager.MATCH_INSTANT) + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt new file mode 100644 index 00000000..ce5e52b1 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.ContentResolver +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.IPackageManager +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import com.android.intentresolver.any +import com.android.intentresolver.eq +import com.android.intentresolver.nullable +import com.android.intentresolver.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.isNull +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +class LastChosenManagerTest { + + private val testDispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(testDispatcher) + private val testTargetIntent = Intent("TestAction") + + @Mock lateinit var mockContentResolver: ContentResolver + @Mock lateinit var mockIPackageManager: IPackageManager + + private lateinit var lastChosenManager: LastChosenManager + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + lastChosenManager = + PackageManagerLastChosenManager(mockContentResolver, testDispatcher, testTargetIntent) { + mockIPackageManager + } + } + + @Test + fun getLastChosen_returnsLastChosenActivity() = + testScope.runTest { + // Arrange + val testResolveInfo = ResolveInfo() + whenever(mockIPackageManager.getLastChosenActivity(any(), nullable(), any())) + .thenReturn(testResolveInfo) + + // Act + val lastChosen = lastChosenManager.getLastChosen() + runCurrent() + + // Assert + verify(mockIPackageManager) + .getLastChosenActivity( + eq(testTargetIntent), + isNull(), + eq(PackageManager.MATCH_DEFAULT_ONLY), + ) + assertThat(lastChosen).isSameInstanceAs(testResolveInfo) + } + + @Test + fun setLastChosen_setsLastChosenActivity() = + testScope.runTest { + // Arrange + val testComponent = ComponentName("TestPackage", "TestClass") + val testIntent = Intent().apply { component = testComponent } + val testIntentFilter = IntentFilter() + val testMatch = 456 + + // Act + lastChosenManager.setLastChosen(testIntent, testIntentFilter, testMatch) + runCurrent() + + // Assert + verify(mockIPackageManager) + .setLastChosenActivity( + eq(testIntent), + isNull(), + eq(PackageManager.MATCH_DEFAULT_ONLY), + eq(testIntentFilter), + eq(testMatch), + eq(testComponent), + ) + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt new file mode 100644 index 00000000..112342ad --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.SharedPreferences +import com.android.intentresolver.any +import com.android.intentresolver.eq +import com.android.intentresolver.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +class PinnableComponentsTest { + + @Mock lateinit var mockSharedPreferences: SharedPreferences + + private val unpinnedComponent = ComponentName("TestPackage", "TestClass") + private val pinnedComponent = ComponentName("PinnedPackage", "PinnedClass") + private val noComponentPinning = NoComponentPinning() + + private lateinit var sharedPreferencesPinnedComponents: PinnableComponents + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + sharedPreferencesPinnedComponents = SharedPreferencesPinnedComponents(mockSharedPreferences) + } + + @Test + fun isComponentPinned_noComponentPinning_neverPins() { + // Arrange + + // Act + val unpinnedResult = noComponentPinning.isComponentPinned(unpinnedComponent) + val pinnedResult = noComponentPinning.isComponentPinned(pinnedComponent) + + // Assert + assertThat(unpinnedResult).isFalse() + assertThat(pinnedResult).isFalse() + } + + @Test + fun isComponentFiltered_chooserRequestFilteredComponents_filtersAccordingToChooserRequest() { + // Arrange + whenever(mockSharedPreferences.getBoolean(eq(pinnedComponent.flattenToString()), any())) + .thenReturn(true) + + // Act + val unpinnedResult = sharedPreferencesPinnedComponents.isComponentPinned(unpinnedComponent) + val pinnedResult = sharedPreferencesPinnedComponents.isComponentPinned(pinnedComponent) + + // Assert + assertThat(unpinnedResult).isFalse() + assertThat(pinnedResult).isTrue() + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt new file mode 100644 index 00000000..26f0199e --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ResolveInfo +import android.os.UserHandle +import com.android.intentresolver.ResolvedComponentInfo +import com.google.common.truth.Truth.assertThat +import java.lang.IndexOutOfBoundsException +import org.junit.Assert.assertThrows +import org.junit.Before +import org.junit.Test + +class ResolveListDeduperTest { + + private lateinit var resolveListDeduper: ResolveListDeduper + + @Before + fun setup() { + resolveListDeduper = ResolveListDeduperImpl(NoComponentPinning()) + } + + @Test + fun addResolveListDedupe_addsDifferentComponents() { + // Arrange + val testIntent = Intent() + val testResolveInfo1 = + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage1" + activityInfo.name = "TestClass1" + } + val testResolveInfo2 = + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage2" + activityInfo.name = "TestClass2" + } + val testResolvedComponentInfo1 = + ResolvedComponentInfo( + ComponentName("TestPackage1", "TestClass1"), + testIntent, + testResolveInfo1, + ) + .apply { isPinned = false } + val listUnderTest = mutableListOf(testResolvedComponentInfo1) + val listToAdd = listOf(testResolveInfo2) + + // Act + resolveListDeduper.addToResolveListWithDedupe( + into = listUnderTest, + intent = testIntent, + from = listToAdd, + ) + + // Assert + listUnderTest.forEachIndexed { index, it -> + val postfix = index + 1 + assertThat(it.name.packageName).isEqualTo("TestPackage$postfix") + assertThat(it.name.className).isEqualTo("TestClass$postfix") + assertThat(it.getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { it.getIntentAt(1) } + } + } + + @Test + fun addResolveListDedupe_combinesDuplicateComponents() { + // Arrange + val testIntent = Intent() + val testResolveInfo1 = + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "DuplicatePackage" + activityInfo.name = "DuplicateClass" + } + val testResolveInfo2 = + ResolveInfo().apply { + userHandle = UserHandle(456) + activityInfo = ActivityInfo() + activityInfo.packageName = "DuplicatePackage" + activityInfo.name = "DuplicateClass" + } + val testResolvedComponentInfo1 = + ResolvedComponentInfo( + ComponentName("DuplicatePackage", "DuplicateClass"), + testIntent, + testResolveInfo1, + ) + .apply { isPinned = false } + val listUnderTest = mutableListOf(testResolvedComponentInfo1) + val listToAdd = listOf(testResolveInfo2) + + // Act + resolveListDeduper.addToResolveListWithDedupe( + into = listUnderTest, + intent = testIntent, + from = listToAdd, + ) + + // Assert + assertThat(listUnderTest).containsExactly(testResolvedComponentInfo1) + assertThat(testResolvedComponentInfo1.getResolveInfoAt(0)).isEqualTo(testResolveInfo1) + assertThat(testResolvedComponentInfo1.getResolveInfoAt(1)).isEqualTo(testResolveInfo2) + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt new file mode 100644 index 00000000..9786b801 --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import com.android.intentresolver.ResolvedComponentInfo +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertThrows +import org.junit.Before +import org.junit.Test + +class ResolvedComponentFilteringTest { + + private lateinit var resolvedComponentFiltering: ResolvedComponentFiltering + + private val fakeFilterableComponents = + object : FilterableComponents { + override fun isComponentFiltered(name: ComponentName): Boolean { + return name.packageName == "FilteredPackage" + } + } + + private val fakePermissionChecker = + object : PermissionChecker { + override suspend fun checkComponentPermission( + permission: String, + uid: Int, + owningUid: Int, + exported: Boolean + ): Int { + return if (permission == "MissingPermission") { + PackageManager.PERMISSION_DENIED + } else { + PackageManager.PERMISSION_GRANTED + } + } + } + + @Before + fun setup() { + resolvedComponentFiltering = + ResolvedComponentFilteringImpl( + launchedFromUid = 123, + filterableComponents = fakeFilterableComponents, + permissionChecker = fakePermissionChecker, + ) + } + + @Test + fun filterIneligibleActivities_returnsListWithoutFilteredComponents() = runTest { + // Arrange + val testIntent = Intent("TestAction") + val testResolveInfo = + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage" + activityInfo.name = "TestClass" + activityInfo.permission = "TestPermission" + activityInfo.applicationInfo = ApplicationInfo() + activityInfo.applicationInfo.uid = 456 + activityInfo.exported = false + } + val filteredResolveInfo = + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "FilteredPackage" + activityInfo.name = "FilteredClass" + activityInfo.permission = "TestPermission" + activityInfo.applicationInfo = ApplicationInfo() + activityInfo.applicationInfo.uid = 456 + activityInfo.exported = false + } + val missingPermissionResolveInfo = + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "NoPermissionPackage" + activityInfo.name = "NoPermissionClass" + activityInfo.permission = "MissingPermission" + activityInfo.applicationInfo = ApplicationInfo() + activityInfo.applicationInfo.uid = 456 + activityInfo.exported = false + } + val testInput = + listOf( + ResolvedComponentInfo( + ComponentName("TestPackage", "TestClass"), + testIntent, + testResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("FilteredPackage", "FilteredClass"), + testIntent, + filteredResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("NoPermissionPackage", "NoPermissionClass"), + testIntent, + missingPermissionResolveInfo, + ) + ) + + // Act + val result = resolvedComponentFiltering.filterIneligibleActivities(testInput) + + // Assert + assertThat(result).hasSize(1) + with(result.first()) { + assertThat(name.packageName).isEqualTo("TestPackage") + assertThat(name.className).isEqualTo("TestClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + } + + @Test + fun filterLowPriority_filtersAfterFirstDifferentPriority() { + // Arrange + val testIntent = Intent("TestAction") + val testResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = true + } + val equalResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = true + } + val diffResolveInfo = + ResolveInfo().apply { + priority = 2 + isDefault = true + } + val testInput = + listOf( + ResolvedComponentInfo( + ComponentName("TestPackage", "TestClass"), + testIntent, + testResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("EqualPackage", "EqualClass"), + testIntent, + equalResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("DiffPackage", "DiffClass"), + testIntent, + diffResolveInfo, + ), + ) + + // Act + val result = resolvedComponentFiltering.filterLowPriority(testInput) + + // Assert + assertThat(result).hasSize(2) + with(result.first()) { + assertThat(name.packageName).isEqualTo("TestPackage") + assertThat(name.className).isEqualTo("TestClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + with(result[1]) { + assertThat(name.packageName).isEqualTo("EqualPackage") + assertThat(name.className).isEqualTo("EqualClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(equalResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + } + + @Test + fun filterLowPriority_filtersAfterFirstDifferentDefault() { + // Arrange + val testIntent = Intent("TestAction") + val testResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = true + } + val equalResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = true + } + val diffResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = false + } + val testInput = + listOf( + ResolvedComponentInfo( + ComponentName("TestPackage", "TestClass"), + testIntent, + testResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("EqualPackage", "EqualClass"), + testIntent, + equalResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("DiffPackage", "DiffClass"), + testIntent, + diffResolveInfo, + ), + ) + + // Act + val result = resolvedComponentFiltering.filterLowPriority(testInput) + + // Assert + assertThat(result).hasSize(2) + with(result.first()) { + assertThat(name.packageName).isEqualTo("TestPackage") + assertThat(name.className).isEqualTo("TestClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + with(result[1]) { + assertThat(name.packageName).isEqualTo("EqualPackage") + assertThat(name.className).isEqualTo("EqualClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(equalResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + } + + @Test + fun filterLowPriority_whenNoDifference_returnsOriginal() { + // Arrange + val testIntent = Intent("TestAction") + val testResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = true + } + val equalResolveInfo = + ResolveInfo().apply { + priority = 1 + isDefault = true + } + val testInput = + listOf( + ResolvedComponentInfo( + ComponentName("TestPackage", "TestClass"), + testIntent, + testResolveInfo, + ), + ResolvedComponentInfo( + ComponentName("EqualPackage", "EqualClass"), + testIntent, + equalResolveInfo, + ), + ) + + // Act + val result = resolvedComponentFiltering.filterLowPriority(testInput) + + // Assert + assertThat(result).hasSize(2) + with(result.first()) { + assertThat(name.packageName).isEqualTo("TestPackage") + assertThat(name.className).isEqualTo("TestClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + with(result[1]) { + assertThat(name.packageName).isEqualTo("EqualPackage") + assertThat(name.className).isEqualTo("EqualClass") + assertThat(getIntentAt(0)).isEqualTo(testIntent) + assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) } + assertThat(getResolveInfoAt(0)).isEqualTo(equalResolveInfo) + assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) } + } + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt new file mode 100644 index 00000000..39b328ee --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.ResolveInfo +import android.os.UserHandle +import com.android.intentresolver.ResolvedComponentInfo +import com.android.intentresolver.chooser.DisplayResolveInfo +import com.android.intentresolver.chooser.TargetInfo +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.mockito.Mockito + +@OptIn(ExperimentalCoroutinesApi::class) +class ResolvedComponentSortingTest { + + private val testDispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(testDispatcher) + + private val fakeResolverComparator = FakeResolverComparator() + + private val resolvedComponentSorting = + ResolvedComponentSortingImpl(testDispatcher, fakeResolverComparator) + + @Test + fun sorted_onNullList_returnsNull() = + testScope.runTest { + // Arrange + val testInput: List<ResolvedComponentInfo>? = null + + // Act + val result = resolvedComponentSorting.sorted(testInput) + runCurrent() + + // Assert + assertThat(result).isNull() + } + + @Test + fun sorted_onEmptyList_returnsEmptyList() = + testScope.runTest { + // Arrange + val testInput = emptyList<ResolvedComponentInfo>() + + // Act + val result = resolvedComponentSorting.sorted(testInput) + runCurrent() + + // Assert + assertThat(result).isEmpty() + } + + @Test + fun sorted_returnsListSortedByGivenComparator() = + testScope.runTest { + // Arrange + val testIntent = Intent("TestAction") + val testInput = + listOf( + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage3" + activityInfo.name = "TestClass3" + }, + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage1" + activityInfo.name = "TestClass1" + }, + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.packageName = "TestPackage2" + activityInfo.name = "TestClass2" + }, + ) + .map { + it.targetUserId = UserHandle.USER_CURRENT + ResolvedComponentInfo( + ComponentName(it.activityInfo.packageName, it.activityInfo.name), + testIntent, + it, + ) + } + + // Act + val result = async { resolvedComponentSorting.sorted(testInput) } + runCurrent() + + // Assert + assertThat(result.await()?.map { it.name.packageName }) + .containsExactly("TestPackage1", "TestPackage2", "TestPackage3") + .inOrder() + } + + @Test + fun getScore_displayResolveInfo_returnsTheScoreAccordingToTheResolverComparator() { + // Arrange + val testTarget = + DisplayResolveInfo.newDisplayResolveInfo( + Intent(), + ResolveInfo().apply { + activityInfo = ActivityInfo() + activityInfo.name = "TestClass" + activityInfo.applicationInfo = ApplicationInfo() + activityInfo.applicationInfo.packageName = "TestPackage" + }, + Intent(), + ) + + // Act + val result = resolvedComponentSorting.getScore(testTarget) + + // Assert + assertThat(result).isEqualTo(1.23f) + } + + @Test + fun getScore_targetInfo_returnsTheScoreAccordingToTheResolverComparator() { + // Arrange + val mockTargetInfo = Mockito.mock(TargetInfo::class.java) + + // Act + val result = resolvedComponentSorting.getScore(mockTargetInfo) + + // Assert + assertThat(result).isEqualTo(1.23f) + } + + @Test + fun updateModel_updatesResolverComparatorModel() = + testScope.runTest { + // Arrange + val mockTargetInfo = Mockito.mock(TargetInfo::class.java) + assertThat(fakeResolverComparator.lastUpdateModel).isNull() + + // Act + resolvedComponentSorting.updateModel(mockTargetInfo) + runCurrent() + + // Assert + assertThat(fakeResolverComparator.lastUpdateModel).isSameInstanceAs(mockTargetInfo) + } + + @Test + fun updateChooserCounts_updatesResolverComparaterChooserCounts() = + testScope.runTest { + // Arrange + val testPackageName = "TestPackage" + val testUser = UserHandle(456) + val testAction = "TestAction" + assertThat(fakeResolverComparator.lastUpdateChooserCounts).isNull() + + // Act + resolvedComponentSorting.updateChooserCounts(testPackageName, testUser, testAction) + runCurrent() + + // Assert + assertThat(fakeResolverComparator.lastUpdateChooserCounts) + .isEqualTo(Triple(testPackageName, testUser, testAction)) + } + + @Test + fun destroy_destroysResolverComparator() { + // Arrange + assertThat(fakeResolverComparator.destroyCalled).isFalse() + + // Act + resolvedComponentSorting.destroy() + + // Assert + assertThat(fakeResolverComparator.destroyCalled).isTrue() + } +} diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt new file mode 100644 index 00000000..9d6394fa --- /dev/null +++ b/java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 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.v2.listcontroller + +import android.content.ComponentName +import android.content.SharedPreferences +import com.android.intentresolver.any +import com.android.intentresolver.eq +import com.android.intentresolver.whenever +import com.google.common.truth.Truth +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +class SharedPreferencesPinnedComponentsTest { + + @Mock lateinit var mockSharedPreferences: SharedPreferences + + private lateinit var sharedPreferencesPinnedComponents: SharedPreferencesPinnedComponents + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + sharedPreferencesPinnedComponents = SharedPreferencesPinnedComponents(mockSharedPreferences) + } + + @Test + fun isComponentPinned_returnsSavedPinnedState() { + // Arrange + val testComponent = ComponentName("TestPackage", "TestClass") + val pinnedComponent = ComponentName("PinnedPackage", "PinnedClass") + whenever(mockSharedPreferences.getBoolean(eq(pinnedComponent.flattenToString()), any())) + .thenReturn(true) + + // Act + val result = sharedPreferencesPinnedComponents.isComponentPinned(testComponent) + val pinnedResult = sharedPreferencesPinnedComponents.isComponentPinned(pinnedComponent) + + // Assert + Mockito.verify(mockSharedPreferences).getBoolean(eq(testComponent.flattenToString()), any()) + Mockito.verify(mockSharedPreferences) + .getBoolean(eq(pinnedComponent.flattenToString()), any()) + Truth.assertThat(result).isFalse() + Truth.assertThat(pinnedResult).isTrue() + } +} |