fix: 首次提交
This commit is contained in:
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.FakeFeatureFlagsImpl
|
||||
import android.content.pm.Flags
|
||||
import android.content.pm.ModuleInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.ApplicationInfoFlags
|
||||
import android.content.pm.PackageManager.ResolveInfoFlags
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.content.pm.UserInfo
|
||||
import android.content.res.Resources
|
||||
import android.os.BadParcelableException
|
||||
import android.os.DeadObjectException
|
||||
import android.os.UserManager
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.internal.R
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doThrow
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppListRepositoryTest {
|
||||
@get:Rule
|
||||
val mSetFlagsRule = SetFlagsRule()
|
||||
|
||||
private val resources = mock<Resources> {
|
||||
on { getStringArray(R.array.config_hideWhenDisabled_packageNames) } doReturn emptyArray()
|
||||
}
|
||||
|
||||
private val packageManager = mock<PackageManager> {
|
||||
on { getInstalledModules(any()) } doReturn emptyList()
|
||||
on { getHomeActivities(any()) } doAnswer {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val resolveInfos = it.arguments[0] as MutableList<ResolveInfo>
|
||||
resolveInfos += resolveInfoOf(packageName = HOME_APP.packageName)
|
||||
null
|
||||
}
|
||||
on { queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), any<Int>()) } doReturn
|
||||
listOf(resolveInfoOf(packageName = IN_LAUNCHER_APP.packageName))
|
||||
}
|
||||
|
||||
private val mockUserManager = mock<UserManager> {
|
||||
on { getUserInfo(ADMIN_USER_ID) } doReturn UserInfo().apply {
|
||||
flags = UserInfo.FLAG_ADMIN
|
||||
}
|
||||
on { getProfileIdsWithDisabled(ADMIN_USER_ID) } doReturn
|
||||
intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID)
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { resources } doReturn resources
|
||||
on { packageManager } doReturn packageManager
|
||||
on { getSystemService(UserManager::class.java) } doReturn mockUserManager
|
||||
}
|
||||
|
||||
private val repository = AppListRepositoryImpl(context)
|
||||
|
||||
private fun mockInstalledApplications(apps: List<ApplicationInfo>, userId: Int) {
|
||||
packageManager.stub {
|
||||
on { getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId)) } doReturn
|
||||
apps
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_notShowInstantApps() = runTest {
|
||||
mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(
|
||||
userId = ADMIN_USER_ID,
|
||||
loadInstantApps = false,
|
||||
)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_showInstantApps() = runTest {
|
||||
mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(
|
||||
userId = ADMIN_USER_ID,
|
||||
loadInstantApps = true,
|
||||
)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP, INSTANT_APP)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_notMatchAnyUserForAdmin_withRegularFlags() = runTest {
|
||||
mockInstalledApplications(listOf(NORMAL_APP), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(
|
||||
userId = ADMIN_USER_ID,
|
||||
matchAnyUserForAdmin = false,
|
||||
)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP)
|
||||
val flags = argumentCaptor<ApplicationInfoFlags> {
|
||||
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
|
||||
}.firstValue
|
||||
assertThat(flags.value and PackageManager.MATCH_ANY_USER.toLong()).isEqualTo(0L)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_matchAnyUserForAdmin_withMatchAnyUserFlag() = runTest {
|
||||
mockInstalledApplications(listOf(NORMAL_APP), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(
|
||||
userId = ADMIN_USER_ID,
|
||||
matchAnyUserForAdmin = true,
|
||||
)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP)
|
||||
val flags = argumentCaptor<ApplicationInfoFlags> {
|
||||
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
|
||||
}.firstValue
|
||||
assertThat(flags.value and PackageManager.MATCH_ANY_USER.toLong()).isGreaterThan(0L)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_matchAnyUserForAdminAndInstalledOnManagedProfileOnly_notDisplayed() = runTest {
|
||||
val managedProfileOnlyPackageName = "installed.on.managed.profile.only"
|
||||
mockInstalledApplications(listOf(ApplicationInfo().apply {
|
||||
packageName = managedProfileOnlyPackageName
|
||||
}), ADMIN_USER_ID)
|
||||
mockInstalledApplications(listOf(ApplicationInfo().apply {
|
||||
packageName = managedProfileOnlyPackageName
|
||||
flags = ApplicationInfo.FLAG_INSTALLED
|
||||
}), MANAGED_PROFILE_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(
|
||||
userId = ADMIN_USER_ID,
|
||||
matchAnyUserForAdmin = true,
|
||||
)
|
||||
|
||||
assertThat(appList).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_matchAnyUserForAdminAndInstalledOnSecondaryUserOnly_displayed() = runTest {
|
||||
val secondaryUserOnlyApp = ApplicationInfo().apply {
|
||||
packageName = "installed.on.secondary.user.only"
|
||||
}
|
||||
mockInstalledApplications(listOf(secondaryUserOnlyApp), ADMIN_USER_ID)
|
||||
mockInstalledApplications(emptyList(), MANAGED_PROFILE_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(
|
||||
userId = ADMIN_USER_ID,
|
||||
matchAnyUserForAdmin = true,
|
||||
)
|
||||
|
||||
assertThat(appList).containsExactly(secondaryUserOnlyApp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_isHideWhenDisabledPackageAndDisabled() = runTest {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = "is.hide.when.disabled"
|
||||
enabled = false
|
||||
}
|
||||
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
|
||||
.thenReturn(arrayOf(app.packageName))
|
||||
mockInstalledApplications(listOf(app), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_isHideWhenDisabledPackageAndDisabledUntilUsed() = runTest {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = "is.hide.when.disabled"
|
||||
enabled = true
|
||||
enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|
||||
}
|
||||
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
|
||||
.thenReturn(arrayOf(app.packageName))
|
||||
mockInstalledApplications(listOf(app), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_isHideWhenDisabledPackageAndEnabled() = runTest {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = "is.hide.when.disabled"
|
||||
enabled = true
|
||||
}
|
||||
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
|
||||
.thenReturn(arrayOf(app.packageName))
|
||||
mockInstalledApplications(listOf(app), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).containsExactly(app)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_disabledByUser() = runTest {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = "disabled.by.user"
|
||||
enabled = false
|
||||
enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
|
||||
}
|
||||
mockInstalledApplications(listOf(app), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).containsExactly(app)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_disabledButNotByUser() = runTest {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = "disabled"
|
||||
enabled = false
|
||||
}
|
||||
mockInstalledApplications(listOf(app), ADMIN_USER_ID)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_archivedAppsEnabled() = runTest {
|
||||
val fakeFlags = FakeFeatureFlagsImpl()
|
||||
fakeFlags.setFlag(Flags.FLAG_ARCHIVING, true)
|
||||
mockInstalledApplications(listOf(NORMAL_APP, ARCHIVED_APP), ADMIN_USER_ID)
|
||||
val repository = AppListRepositoryImpl(context, fakeFlags)
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP, ARCHIVED_APP)
|
||||
val flags = argumentCaptor<ApplicationInfoFlags> {
|
||||
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
|
||||
}.firstValue
|
||||
assertThat(flags.value).isEqualTo(
|
||||
(PackageManager.MATCH_DISABLED_COMPONENTS or
|
||||
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() or
|
||||
PackageManager.MATCH_ARCHIVED_PACKAGES
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_hasApkInApexInfo_shouldNotIncludeAllHiddenApps() = runTest {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
|
||||
packageManager.stub {
|
||||
on { getInstalledModules(any()) } doReturn listOf(HIDDEN_MODULE)
|
||||
}
|
||||
mockInstalledApplications(
|
||||
listOf(NORMAL_APP, HIDDEN_APEX_APP, HIDDEN_MODULE_APP),
|
||||
ADMIN_USER_ID
|
||||
)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_noApkInApexInfo_shouldNotIncludeHiddenSystemModule() = runTest {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
|
||||
packageManager.stub {
|
||||
on { getInstalledModules(any()) } doReturn listOf(HIDDEN_MODULE)
|
||||
}
|
||||
mockInstalledApplications(
|
||||
listOf(NORMAL_APP, HIDDEN_APEX_APP, HIDDEN_MODULE_APP),
|
||||
ADMIN_USER_ID
|
||||
)
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).containsExactly(NORMAL_APP, HIDDEN_APEX_APP)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApps_hasException_returnEmptyList() = runTest {
|
||||
packageManager.stub {
|
||||
on {
|
||||
getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(ADMIN_USER_ID))
|
||||
} doThrow BadParcelableException(DeadObjectException())
|
||||
}
|
||||
|
||||
val appList = repository.loadApps(userId = ADMIN_USER_ID)
|
||||
|
||||
assertThat(appList).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showSystemPredicate_showSystem() = runTest {
|
||||
val app = SYSTEM_APP
|
||||
|
||||
val showSystemPredicate = getShowSystemPredicate(showSystem = true)
|
||||
|
||||
assertThat(showSystemPredicate(app)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showSystemPredicate_notShowSystemAndIsSystemApp() = runTest {
|
||||
val app = SYSTEM_APP
|
||||
|
||||
val showSystemPredicate = getShowSystemPredicate(showSystem = false)
|
||||
|
||||
assertThat(showSystemPredicate(app)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showSystemPredicate_isUpdatedSystemApp() = runTest {
|
||||
val app = UPDATED_SYSTEM_APP
|
||||
|
||||
val showSystemPredicate = getShowSystemPredicate(showSystem = false)
|
||||
|
||||
assertThat(showSystemPredicate(app)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showSystemPredicate_isHome() = runTest {
|
||||
val showSystemPredicate = getShowSystemPredicate(showSystem = false)
|
||||
|
||||
assertThat(showSystemPredicate(HOME_APP)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showSystemPredicate_appInLauncher() = runTest {
|
||||
val showSystemPredicate = getShowSystemPredicate(showSystem = false)
|
||||
|
||||
assertThat(showSystemPredicate(IN_LAUNCHER_APP)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSystemPackageNames_returnExpectedValues() = runTest {
|
||||
mockInstalledApplications(
|
||||
apps = listOf(
|
||||
NORMAL_APP,
|
||||
INSTANT_APP,
|
||||
SYSTEM_APP,
|
||||
UPDATED_SYSTEM_APP,
|
||||
HOME_APP,
|
||||
IN_LAUNCHER_APP,
|
||||
),
|
||||
userId = ADMIN_USER_ID,
|
||||
)
|
||||
|
||||
val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames(
|
||||
context = context,
|
||||
userId = ADMIN_USER_ID,
|
||||
)
|
||||
|
||||
assertThat(systemPackageNames).containsExactly(SYSTEM_APP.packageName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadAndFilterApps_loadNonSystemApp_returnExpectedValues() = runTest {
|
||||
mockInstalledApplications(
|
||||
apps = listOf(
|
||||
NORMAL_APP,
|
||||
INSTANT_APP,
|
||||
SYSTEM_APP,
|
||||
UPDATED_SYSTEM_APP,
|
||||
HOME_APP,
|
||||
IN_LAUNCHER_APP,
|
||||
),
|
||||
userId = ADMIN_USER_ID,
|
||||
)
|
||||
|
||||
val appList = repository.loadAndFilterApps(userId = ADMIN_USER_ID, isSystemApp = false)
|
||||
|
||||
assertThat(appList)
|
||||
.containsExactly(NORMAL_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP)
|
||||
}
|
||||
|
||||
private suspend fun getShowSystemPredicate(showSystem: Boolean) =
|
||||
repository.showSystemPredicate(
|
||||
userIdFlow = flowOf(ADMIN_USER_ID),
|
||||
showSystemFlow = flowOf(showSystem),
|
||||
).first()
|
||||
|
||||
private companion object {
|
||||
const val ADMIN_USER_ID = 0
|
||||
const val MANAGED_PROFILE_USER_ID = 11
|
||||
|
||||
val NORMAL_APP = ApplicationInfo().apply {
|
||||
packageName = "normal"
|
||||
enabled = true
|
||||
}
|
||||
|
||||
val INSTANT_APP = ApplicationInfo().apply {
|
||||
packageName = "instant"
|
||||
enabled = true
|
||||
privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
|
||||
}
|
||||
|
||||
val SYSTEM_APP = ApplicationInfo().apply {
|
||||
packageName = "system.app"
|
||||
flags = ApplicationInfo.FLAG_SYSTEM
|
||||
}
|
||||
|
||||
val UPDATED_SYSTEM_APP = ApplicationInfo().apply {
|
||||
packageName = "updated.system.app"
|
||||
flags = ApplicationInfo.FLAG_SYSTEM or ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
|
||||
}
|
||||
|
||||
val HOME_APP = ApplicationInfo().apply {
|
||||
packageName = "home.app"
|
||||
flags = ApplicationInfo.FLAG_SYSTEM
|
||||
}
|
||||
|
||||
val IN_LAUNCHER_APP = ApplicationInfo().apply {
|
||||
packageName = "app.in.launcher"
|
||||
flags = ApplicationInfo.FLAG_SYSTEM
|
||||
}
|
||||
|
||||
val ARCHIVED_APP = ApplicationInfo().apply {
|
||||
packageName = "archived.app"
|
||||
flags = ApplicationInfo.FLAG_SYSTEM
|
||||
isArchived = true
|
||||
}
|
||||
|
||||
val HIDDEN_APEX_APP = ApplicationInfo().apply {
|
||||
packageName = "hidden.apex.package"
|
||||
}
|
||||
|
||||
val HIDDEN_MODULE_APP = ApplicationInfo().apply {
|
||||
packageName = "hidden.module.package"
|
||||
}
|
||||
|
||||
val HIDDEN_MODULE = ModuleInfo().apply {
|
||||
packageName = "hidden.module.package"
|
||||
apkInApexPackageNames = listOf("hidden.apex.package")
|
||||
isHidden = true
|
||||
}
|
||||
|
||||
fun resolveInfoOf(packageName: String) = ResolveInfo().apply {
|
||||
activityInfo = ActivityInfo().apply {
|
||||
this.packageName = packageName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.pm.ApplicationInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.framework.compose.stateOf
|
||||
import com.android.settingslib.spa.framework.util.mapItem
|
||||
import com.android.settingslib.spa.testutils.waitUntil
|
||||
import com.android.settingslib.spaprivileged.template.app.AppListConfig
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppListViewModelTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Mock
|
||||
private lateinit var application: Application
|
||||
|
||||
private val listModel = TestAppListModel()
|
||||
|
||||
private fun createViewModel(): AppListViewModelImpl<TestAppRecord> {
|
||||
val viewModel = AppListViewModelImpl<TestAppRecord>(
|
||||
application = application,
|
||||
appListRepositoryFactory = { FakeAppListRepository },
|
||||
appRepositoryFactory = { FakeAppRepository },
|
||||
)
|
||||
viewModel.appListConfig.setIfAbsent(CONFIG)
|
||||
viewModel.listModel.setIfAbsent(listModel)
|
||||
viewModel.showSystem.setIfAbsent(false)
|
||||
viewModel.optionFlow.value = 0
|
||||
viewModel.searchQuery.setIfAbsent("")
|
||||
viewModel.reloadApps()
|
||||
return viewModel
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appListDataFlow() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
val (appEntries, option) = viewModel.appListDataFlow.first()
|
||||
|
||||
assertThat(appEntries).hasSize(1)
|
||||
assertThat(appEntries[0].record.app).isSameInstanceAs(APP)
|
||||
assertThat(appEntries[0].label).isEqualTo(LABEL)
|
||||
assertThat(option).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onFirstLoaded_calledWhenLoaded() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.appListDataFlow.first()
|
||||
|
||||
waitUntil { listModel.onFirstLoadedCalled }
|
||||
}
|
||||
|
||||
private object FakeAppListRepository : AppListRepository {
|
||||
override suspend fun loadApps(
|
||||
userId: Int,
|
||||
loadInstantApps: Boolean,
|
||||
matchAnyUserForAdmin: Boolean,
|
||||
) = listOf(APP)
|
||||
|
||||
override fun showSystemPredicate(
|
||||
userIdFlow: Flow<Int>,
|
||||
showSystemFlow: Flow<Boolean>,
|
||||
): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { true }
|
||||
|
||||
override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet()
|
||||
|
||||
override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) =
|
||||
emptyList<ApplicationInfo>()
|
||||
}
|
||||
|
||||
private object FakeAppRepository : AppRepository {
|
||||
override fun loadLabel(app: ApplicationInfo) = LABEL
|
||||
|
||||
@Composable
|
||||
override fun produceIcon(app: ApplicationInfo) = stateOf(null)
|
||||
|
||||
@Composable
|
||||
override fun produceIconContentDescription(app: ApplicationInfo) = stateOf(null)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val USER_ID = 0
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
const val LABEL = "Label"
|
||||
val CONFIG = AppListConfig(
|
||||
userIds = listOf(USER_ID),
|
||||
showInstantApps = false,
|
||||
matchAnyUserForAdmin = false,
|
||||
)
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class TestAppRecord(override val app: ApplicationInfo) : AppRecord
|
||||
|
||||
private class TestAppListModel : AppListModel<TestAppRecord> {
|
||||
var onFirstLoadedCalled = false
|
||||
|
||||
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
|
||||
appListFlow.mapItem(::TestAppRecord)
|
||||
|
||||
override suspend fun onFirstLoaded(recordList: List<TestAppRecord>): Boolean {
|
||||
onFirstLoadedCalled = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.app.AppOpsManager
|
||||
import android.app.AppOpsManager.MODE_ALLOWED
|
||||
import android.app.AppOpsManager.MODE_ERRORED
|
||||
import android.app.AppOpsManager.MODE_IGNORED
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doNothing
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppOpsControllerTest {
|
||||
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Spy private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock private lateinit var appOpsManager: AppOpsManager
|
||||
|
||||
@Mock private lateinit var packageManager: PackageManager
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(context.appOpsManager).thenReturn(appOpsManager)
|
||||
whenever(context.packageManager).thenReturn(packageManager)
|
||||
doNothing().whenever(packageManager)
|
||||
.updatePermissionFlags(any(), any(), any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_setToTrue() {
|
||||
val controller =
|
||||
AppOpsController(
|
||||
context = context,
|
||||
app = APP,
|
||||
op = OP,
|
||||
)
|
||||
|
||||
controller.setAllowed(true)
|
||||
|
||||
verify(appOpsManager).setMode(OP, APP.uid, APP.packageName, MODE_ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_setToFalse() {
|
||||
val controller =
|
||||
AppOpsController(
|
||||
context = context,
|
||||
app = APP,
|
||||
op = OP,
|
||||
)
|
||||
|
||||
controller.setAllowed(false)
|
||||
|
||||
verify(appOpsManager).setMode(OP, APP.uid, APP.packageName, MODE_ERRORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_setToFalseWithModeForNotAllowed() {
|
||||
val controller =
|
||||
AppOpsController(
|
||||
context = context,
|
||||
app = APP,
|
||||
op = OP,
|
||||
modeForNotAllowed = MODE_IGNORED,
|
||||
)
|
||||
|
||||
controller.setAllowed(false)
|
||||
|
||||
verify(appOpsManager).setMode(OP, APP.uid, APP.packageName, MODE_IGNORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_setToTrueByUid() {
|
||||
val controller =
|
||||
AppOpsController(
|
||||
context = context,
|
||||
app = APP,
|
||||
op = OP,
|
||||
setModeByUid = true,
|
||||
)
|
||||
|
||||
controller.setAllowed(true)
|
||||
|
||||
verify(appOpsManager).setUidMode(OP, APP.uid, MODE_ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_setToFalseByUid() {
|
||||
val controller =
|
||||
AppOpsController(
|
||||
context = context,
|
||||
app = APP,
|
||||
op = OP,
|
||||
setModeByUid = true,
|
||||
)
|
||||
|
||||
controller.setAllowed(false)
|
||||
|
||||
verify(appOpsManager).setUidMode(OP, APP.uid, MODE_ERRORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getMode() {
|
||||
whenever(appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName))
|
||||
.thenReturn(MODE_ALLOWED)
|
||||
val controller =
|
||||
AppOpsController(
|
||||
context = context,
|
||||
app = APP,
|
||||
op = OP,
|
||||
)
|
||||
|
||||
val mode = controller.getMode()
|
||||
|
||||
assertThat(mode).isEqualTo(MODE_ALLOWED)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val OP = 1
|
||||
val APP =
|
||||
ApplicationInfo().apply {
|
||||
packageName = "package.name"
|
||||
uid = 123
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.app.AppOpsManager
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.os.UserHandle
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.testutils.toListWithTimeout
|
||||
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.stub
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppOpsRepositoryTest {
|
||||
|
||||
private var listener: AppOpsManager.OnOpChangedListener? = null
|
||||
|
||||
private val mockAppOpsManager = mock<AppOpsManager> {
|
||||
on {
|
||||
checkOpNoThrow(AppOpsManager.OP_MANAGE_MEDIA, UID, PACKAGE_NAME)
|
||||
} doReturn AppOpsManager.MODE_ERRORED
|
||||
|
||||
on {
|
||||
startWatchingMode(eq(AppOpsManager.OP_MANAGE_MEDIA), eq(PACKAGE_NAME), any())
|
||||
} doAnswer { listener = it.arguments[2] as AppOpsManager.OnOpChangedListener }
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { appOpsManager } doReturn mockAppOpsManager
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getOpMode() {
|
||||
val mode = context.appOpsManager.getOpMode(AppOpsManager.OP_MANAGE_MEDIA, APP)
|
||||
|
||||
assertThat(mode).isEqualTo(AppOpsManager.MODE_ERRORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opModeFlow() = runBlocking {
|
||||
val flow = context.appOpsManager.opModeFlow(AppOpsManager.OP_MANAGE_MEDIA, APP)
|
||||
|
||||
val mode = flow.first()
|
||||
|
||||
assertThat(mode).isEqualTo(AppOpsManager.MODE_ERRORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun opModeFlow_changed() = runBlocking {
|
||||
val listDeferred = async {
|
||||
context.appOpsManager.opModeFlow(AppOpsManager.OP_MANAGE_MEDIA, APP).toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
mockAppOpsManager.stub {
|
||||
on { checkOpNoThrow(AppOpsManager.OP_MANAGE_MEDIA, UID, PACKAGE_NAME) } doReturn
|
||||
AppOpsManager.MODE_IGNORED
|
||||
}
|
||||
listener?.onOpChanged("", "", UserHandle.getUserId(UID))
|
||||
|
||||
assertThat(listDeferred.await()).contains(AppOpsManager.MODE_IGNORED)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val UID = 110000
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.os.UserManager
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.framework.compose.stateOf
|
||||
import com.android.settingslib.spa.testutils.delay
|
||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppRepositoryTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val userManager = mock<UserManager>()
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { userManager } doReturn userManager
|
||||
}
|
||||
|
||||
private val appRepository = AppRepositoryImpl(context)
|
||||
|
||||
@Test
|
||||
fun produceIconContentDescription_workProfile() {
|
||||
whenever(userManager.isManagedProfile(APP.userId)).thenReturn(true)
|
||||
|
||||
val contentDescription = produceIconContentDescription()
|
||||
|
||||
assertThat(contentDescription.value)
|
||||
.isEqualTo(context.getString(com.android.settingslib.R.string.category_work))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun produceIconContentDescription_personalProfile() {
|
||||
whenever(userManager.isManagedProfile(APP.userId)).thenReturn(false)
|
||||
|
||||
val contentDescription = produceIconContentDescription()
|
||||
|
||||
assertThat(contentDescription.value).isNull()
|
||||
}
|
||||
|
||||
private fun produceIconContentDescription(): State<String?> {
|
||||
var contentDescription: State<String?> = stateOf(null)
|
||||
composeTestRule.setContent {
|
||||
contentDescription = appRepository.produceIconContentDescription(APP)
|
||||
}
|
||||
composeTestRule.delay()
|
||||
return contentDescription
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val UID = 123
|
||||
val APP = ApplicationInfo().apply {
|
||||
uid = UID
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.UserManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
|
||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ApplicationInfosTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Spy
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock
|
||||
private lateinit var userManager: UserManager
|
||||
|
||||
@Mock
|
||||
private lateinit var devicePolicyManager: DevicePolicyManager
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(context.userManager).thenReturn(userManager)
|
||||
whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun userId() {
|
||||
val app = ApplicationInfo().apply {
|
||||
uid = 123
|
||||
}
|
||||
|
||||
val userId = app.userId
|
||||
|
||||
assertThat(userId).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun userHandle() {
|
||||
val app = ApplicationInfo().apply {
|
||||
uid = 123
|
||||
}
|
||||
|
||||
val userHandle = app.userHandle
|
||||
|
||||
assertThat(userHandle.identifier).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasFlag() {
|
||||
val app = ApplicationInfo().apply {
|
||||
flags = ApplicationInfo.FLAG_INSTALLED
|
||||
}
|
||||
|
||||
val hasFlag = app.hasFlag(ApplicationInfo.FLAG_INSTALLED)
|
||||
|
||||
assertThat(hasFlag).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun installed() {
|
||||
val app = ApplicationInfo().apply {
|
||||
flags = ApplicationInfo.FLAG_INSTALLED
|
||||
}
|
||||
|
||||
val installed = app.installed
|
||||
|
||||
assertThat(installed).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isDisabledUntilUsed() {
|
||||
val app = ApplicationInfo().apply {
|
||||
enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|
||||
}
|
||||
|
||||
val isDisabledUntilUsed = app.isDisabledUntilUsed
|
||||
|
||||
assertThat(isDisabledUntilUsed).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isDisallowControl() {
|
||||
val app = ApplicationInfo().apply {
|
||||
uid = 123
|
||||
}
|
||||
whenever(
|
||||
userManager.hasBaseUserRestriction(UserManager.DISALLOW_APPS_CONTROL, app.userHandle)
|
||||
).thenReturn(true)
|
||||
|
||||
val isDisallowControl = app.isDisallowControl(context)
|
||||
|
||||
assertThat(isDisallowControl).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isActiveAdmin() {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = 123
|
||||
}
|
||||
whenever(devicePolicyManager.packageHasActiveAdmins(PACKAGE_NAME, app.userId))
|
||||
.thenReturn(true)
|
||||
|
||||
val isActiveAdmin = app.isActiveAdmin(context)
|
||||
|
||||
assertThat(isActiveAdmin).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toRoute() {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = 123
|
||||
}
|
||||
|
||||
val route = app.toRoute()
|
||||
|
||||
assertThat(route).isEqualTo("package.name/0")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ModuleInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.content.pm.PackageManager.ResolveInfoFlags
|
||||
import android.content.pm.ResolveInfo
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PackageManagerExtTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Mock
|
||||
private lateinit var packageManager: PackageManager
|
||||
|
||||
private fun mockResolveActivityAsUser(resolveInfo: ResolveInfo?) {
|
||||
whenever(
|
||||
packageManager.resolveActivityAsUser(any(), any<ResolveInfoFlags>(), eq(APP.userId))
|
||||
).thenReturn(resolveInfo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isSystemModule_whenSystemModule_returnTrue() {
|
||||
whenever(packageManager.getModuleInfo(PACKAGE_NAME, 0)).thenReturn(ModuleInfo())
|
||||
|
||||
val isSystemModule = packageManager.isSystemModule(PACKAGE_NAME)
|
||||
|
||||
assertThat(isSystemModule).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isSystemModule_whenNotSystemModule_returnFalse() {
|
||||
whenever(packageManager.getModuleInfo(PACKAGE_NAME, 0)).thenThrow(NameNotFoundException())
|
||||
|
||||
val isSystemModule = packageManager.isSystemModule(PACKAGE_NAME)
|
||||
|
||||
assertThat(isSystemModule).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveActionForApp_noResolveInfo() {
|
||||
mockResolveActivityAsUser(null)
|
||||
|
||||
val activityInfo = packageManager.resolveActionForApp(APP, ACTION)
|
||||
|
||||
assertThat(activityInfo).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveActionForApp_noActivityInfo() {
|
||||
mockResolveActivityAsUser(ResolveInfo())
|
||||
|
||||
val activityInfo = packageManager.resolveActionForApp(APP, ACTION)
|
||||
|
||||
assertThat(activityInfo).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveActionForApp_hasActivityInfo() {
|
||||
mockResolveActivityAsUser(ResolveInfo().apply {
|
||||
activityInfo = ActivityInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
name = ACTIVITY_NAME
|
||||
}
|
||||
})
|
||||
|
||||
val activityInfo = packageManager.resolveActionForApp(APP, ACTION)!!
|
||||
|
||||
assertThat(activityInfo.componentName).isEqualTo(ComponentName(PACKAGE_NAME, ACTIVITY_NAME))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveActionForApp_withFlags() {
|
||||
packageManager.resolveActionForApp(
|
||||
app = APP,
|
||||
action = ACTION,
|
||||
flags = PackageManager.GET_META_DATA,
|
||||
)
|
||||
|
||||
argumentCaptor<ResolveInfoFlags> {
|
||||
verify(packageManager).resolveActivityAsUser(any(), capture(), eq(APP.userId))
|
||||
assertThat(firstValue.value).isEqualTo(PackageManager.GET_META_DATA.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
const val ACTIVITY_NAME = "ActivityName"
|
||||
const val ACTION = "action"
|
||||
const val UID = 123
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PackageManagersTest {
|
||||
|
||||
private val fakePackageManagerWrapper = FakePackageManagerWrapper()
|
||||
|
||||
private val packageManagersImpl = PackageManagersImpl(fakePackageManagerWrapper)
|
||||
|
||||
@Test
|
||||
fun getPackageInfoAsUser_notFound() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = null
|
||||
|
||||
val packageInfo = packageManagersImpl.getPackageInfoAsUser(PACKAGE_NAME, 0)
|
||||
|
||||
assertThat(packageInfo).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getPackageInfoAsUser_found() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo()
|
||||
|
||||
val packageInfo = packageManagersImpl.getPackageInfoAsUser(PACKAGE_NAME, 0)
|
||||
|
||||
assertThat(packageInfo).isSameInstanceAs(fakePackageManagerWrapper.fakePackageInfo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasRequestPermission_packageInfoIsNull_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = null
|
||||
|
||||
val hasRequestPermission = with(packageManagersImpl) {
|
||||
APP.hasRequestPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasRequestPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasRequestPermission_requestedPermissionsIsNull_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = null
|
||||
}
|
||||
|
||||
val hasRequestPermission = with(packageManagersImpl) {
|
||||
APP.hasRequestPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasRequestPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasRequestPermission_notRequested_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = emptyArray()
|
||||
}
|
||||
|
||||
val hasRequestPermission = with(packageManagersImpl) {
|
||||
APP.hasRequestPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasRequestPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasRequestPermission_requested_returnTrue() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = arrayOf(PERMISSION_A)
|
||||
}
|
||||
|
||||
val hasRequestPermission = with(packageManagersImpl) {
|
||||
APP.hasRequestPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasRequestPermission).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasGrantPermission_packageInfoIsNull_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = null
|
||||
|
||||
val hasGrantPermission = with(packageManagersImpl) {
|
||||
APP.hasGrantPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasGrantPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasGrantPermission_requestedPermissionsIsNull_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = null
|
||||
}
|
||||
|
||||
val hasGrantPermission = with(packageManagersImpl) {
|
||||
APP.hasGrantPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasGrantPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasGrantPermission_notRequested_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = arrayOf(PERMISSION_B)
|
||||
requestedPermissionsFlags = intArrayOf(PackageInfo.REQUESTED_PERMISSION_GRANTED)
|
||||
}
|
||||
|
||||
val hasGrantPermission = with(packageManagersImpl) {
|
||||
APP.hasGrantPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasGrantPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasGrantPermission_notGranted_returnFalse() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = arrayOf(PERMISSION_A, PERMISSION_B)
|
||||
requestedPermissionsFlags = intArrayOf(0, PackageInfo.REQUESTED_PERMISSION_GRANTED)
|
||||
}
|
||||
|
||||
val hasGrantPermission = with(packageManagersImpl) {
|
||||
APP.hasGrantPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasGrantPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasGrantPermission_granted_returnTrue() {
|
||||
fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
|
||||
requestedPermissions = arrayOf(PERMISSION_A, PERMISSION_B)
|
||||
requestedPermissionsFlags = intArrayOf(PackageInfo.REQUESTED_PERMISSION_GRANTED, 0)
|
||||
}
|
||||
|
||||
val hasGrantPermission = with(packageManagersImpl) {
|
||||
APP.hasGrantPermission(PERMISSION_A)
|
||||
}
|
||||
|
||||
assertThat(hasGrantPermission).isTrue()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
const val PERMISSION_A = "permission.A"
|
||||
const val PERMISSION_B = "permission.B"
|
||||
const val UID = 123
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FakePackageManagerWrapper : PackageManagerWrapper {
|
||||
var fakePackageInfo: PackageInfo? = null
|
||||
|
||||
override fun getPackageInfoAsUserCached(packageName: String, flags: Long, userId: Int) =
|
||||
fakePackageInfo
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||
import com.android.settingslib.spa.testutils.toListWithTimeout
|
||||
import com.android.settingslib.spaprivileged.framework.common.asUser
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PermissionsChangedFlowTest {
|
||||
|
||||
private var onPermissionsChangedListener: PackageManager.OnPermissionsChangedListener? = null
|
||||
|
||||
private val mockPackageManager = mock<PackageManager> {
|
||||
on { addOnPermissionsChangeListener(any()) } doAnswer {
|
||||
onPermissionsChangedListener =
|
||||
it.arguments[0] as PackageManager.OnPermissionsChangedListener
|
||||
}
|
||||
}
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { asUser(APP.userHandle) } doReturn mock
|
||||
on { packageManager } doReturn mockPackageManager
|
||||
}
|
||||
|
||||
@Test
|
||||
fun permissionsChangedFlow_sendInitialValueTrue() = runBlocking {
|
||||
val flow = context.permissionsChangedFlow(APP)
|
||||
|
||||
assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun permissionsChangedFlow_collectChanged_getTwo() = runBlocking {
|
||||
val listDeferred = async {
|
||||
context.permissionsChangedFlow(APP).toListWithTimeout()
|
||||
}
|
||||
delay(100)
|
||||
|
||||
onPermissionsChangedListener?.onPermissionsChanged(APP.uid)
|
||||
|
||||
assertThat(listDeferred.await()).hasSize(2)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = "package.name"
|
||||
uid = 10000
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.settingslib.spaprivileged.model.enterprise
|
||||
|
||||
import android.app.admin.DevicePolicyResources.Strings.Settings
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.RestrictedLockUtils
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class RestrictedModeTest {
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val fakeEnterpriseRepository = object : IEnterpriseRepository {
|
||||
override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
|
||||
when (updatableStringId) {
|
||||
Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY -> ENABLED_BY_ADMIN
|
||||
Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY -> DISABLED_BY_ADMIN
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blockedByAdmin_getSummaryWhenChecked() {
|
||||
val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
|
||||
|
||||
val summary = blockedByAdmin.getSummary(true)
|
||||
|
||||
assertThat(summary).isEqualTo(ENABLED_BY_ADMIN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blockedByAdmin_getSummaryNotWhenChecked() {
|
||||
val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
|
||||
|
||||
val summary = blockedByAdmin.getSummary(false)
|
||||
|
||||
assertThat(summary).isEqualTo(DISABLED_BY_ADMIN)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val RESTRICTION = "restriction"
|
||||
val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin =
|
||||
RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
|
||||
|
||||
const val ENABLED_BY_ADMIN = "Enabled by admin"
|
||||
const val DISABLED_BY_ADMIN = "Disabled by admin"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user