fix: 首次提交

This commit is contained in:
2024-12-09 11:25:23 +08:00
parent d0c01071e9
commit 2c2109a5f3
4741 changed files with 290641 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
//
// 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 {
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "SpaPrivilegedLibTests",
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
defaults: ["SpaPrivilegedLib-defaults"],
srcs: [
":SpaPrivilegedLib_srcs",
"src/**/*.kt",
],
use_resource_processor: true,
static_libs: [
"SpaLibTestUtils",
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
"flag-junit",
],
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.spaprivileged.test">
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="Tests for SpaPrivilegedLib"
android:targetPackage="com.android.settingslib.spaprivileged.test" />
</manifest>

View File

@@ -0,0 +1,35 @@
<!--
~ 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.
-->
<resources>
<!-- Test Permission title. [DO NOT TRANSLATE] -->
<string name="test_permission_title" translatable="false">Test Permission</string>
<!-- Test Permission switch title. [DO NOT TRANSLATE] -->
<string name="test_permission_switch_title" translatable="false">Allow Test Permission</string>
<!-- Test Permission footer. [DO NOT TRANSLATE] -->
<string name="test_permission_footer" translatable="false">Test Permission is for testing.</string>
<!-- Test App Op Permission title. [DO NOT TRANSLATE] -->
<string name="test_app_op_permission_title" translatable="false">Test App Op Permission</string>
<!-- Test App Op Permission switch title. [DO NOT TRANSLATE] -->
<string name="test_app_op_permission_switch_title" translatable="false">Allow Test App Op Permission</string>
<!-- Test App Op Permission footer. [DO NOT TRANSLATE] -->
<string name="test_app_op_permission_footer" translatable="false">Test App Op Permission is for testing.</string>
</resources>

View File

@@ -0,0 +1,105 @@
/*
* 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.framework.common
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
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.doThrow
import org.mockito.kotlin.eq
import org.mockito.kotlin.isNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class BroadcastReceiverAsUserFlowTest {
private var registeredBroadcastReceiver: BroadcastReceiver? = null
private val context = mock<Context> {
on {
registerReceiverAsUser(
any(),
eq(USER_HANDLE),
eq(INTENT_FILTER),
isNull(),
isNull(),
eq(Context.RECEIVER_NOT_EXPORTED),
)
} doAnswer {
registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
null
}
}
@Test
fun broadcastReceiverAsUserFlow_registered() = runBlocking {
val flow = context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE)
flow.firstWithTimeoutOrNull()
assertThat(registeredBroadcastReceiver).isNotNull()
}
@Test
fun broadcastReceiverAsUserFlow_isCalledOnReceive() = runBlocking {
var onReceiveIsCalled = false
launch {
context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE).first {
onReceiveIsCalled = true
true
}
}
delay(100)
registeredBroadcastReceiver!!.onReceive(context, Intent())
delay(100)
assertThat(onReceiveIsCalled).isTrue()
}
@Test
fun broadcastReceiverAsUserFlow_unregisterReceiverThrowException_noCrash() = runBlocking {
context.stub {
on { unregisterReceiver(any()) } doThrow IllegalArgumentException()
}
val flow = context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE)
flow.firstWithTimeoutOrNull()
assertThat(registeredBroadcastReceiver).isNotNull()
}
private companion object {
val USER_HANDLE: UserHandle = UserHandle.of(0)
val INTENT_FILTER = IntentFilter()
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.framework.common
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
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.doThrow
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class BroadcastReceiverFlowTest {
private var registeredBroadcastReceiver: BroadcastReceiver? = null
private val context = mock<Context> {
on {
registerReceiver(any(), eq(INTENT_FILTER), eq(Context.RECEIVER_VISIBLE_TO_INSTANT_APPS))
} doAnswer {
registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
null
}
}
@Test
fun broadcastReceiverFlow_registered() = runBlocking {
val flow = context.broadcastReceiverFlow(INTENT_FILTER)
flow.firstWithTimeoutOrNull()
assertThat(registeredBroadcastReceiver).isNotNull()
}
@Test
fun broadcastReceiverFlow_isCalledOnReceive() = runBlocking {
var onReceiveIsCalled = false
launch {
context.broadcastReceiverFlow(INTENT_FILTER).first {
onReceiveIsCalled = true
true
}
}
delay(100)
registeredBroadcastReceiver!!.onReceive(context, Intent())
delay(100)
assertThat(onReceiveIsCalled).isTrue()
}
@Test
fun broadcastReceiverFlow_unregisterReceiverThrowException_noCrash() = runBlocking {
context.stub {
on { unregisterReceiver(any()) } doThrow IllegalArgumentException()
}
val flow = context.broadcastReceiverFlow(INTENT_FILTER)
flow.firstWithTimeoutOrNull()
assertThat(registeredBroadcastReceiver).isNotNull()
}
private companion object {
val INTENT_FILTER = IntentFilter()
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.framework.compose
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.UserHandle
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.eq
import org.mockito.kotlin.isNull
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class DisposableBroadcastReceiverAsUserTest {
@get:Rule
val composeTestRule = createComposeRule()
private var registeredBroadcastReceiver: BroadcastReceiver? = null
private val context = mock<Context> {
on {
registerReceiverAsUser(
any(),
eq(USER_HANDLE),
eq(INTENT_FILTER),
isNull(),
isNull(),
eq(Context.RECEIVER_NOT_EXPORTED),
)
} doAnswer {
registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
null
}
}
@Test
fun broadcastReceiver_registered() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalContext provides context,
LocalLifecycleOwner provides TestLifecycleOwner(),
) {
DisposableBroadcastReceiverAsUser(INTENT_FILTER, USER_HANDLE) {}
}
}
assertThat(registeredBroadcastReceiver).isNotNull()
}
@Test
fun broadcastReceiver_isCalledOnReceive() = runBlocking {
var onReceiveIsCalled = false
composeTestRule.setContent {
CompositionLocalProvider(
LocalContext provides context,
LocalLifecycleOwner provides TestLifecycleOwner(),
) {
DisposableBroadcastReceiverAsUser(INTENT_FILTER, USER_HANDLE) {
onReceiveIsCalled = true
}
}
}
registeredBroadcastReceiver!!.onReceive(context, Intent())
delay(100)
assertThat(onReceiveIsCalled).isTrue()
}
private companion object {
val USER_HANDLE: UserHandle = UserHandle.of(0)
val INTENT_FILTER = IntentFilter()
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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"
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}

View File

@@ -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
}
}
}

View File

@@ -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"
}
}

View File

@@ -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.settingslib.spaprivileged.settingsprovider
import android.content.Context
import android.provider.Settings
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.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
@RunWith(AndroidJUnit4::class)
class SettingsGlobalBooleanTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun getValue_setTrue_returnTrue() {
Settings.Global.putInt(context.contentResolver, TEST_NAME, 1)
val value by context.settingsGlobalBoolean(TEST_NAME)
assertThat(value).isTrue()
}
@Test
fun getValue_setFalse_returnFalse() {
Settings.Global.putInt(context.contentResolver, TEST_NAME, 0)
val value by context.settingsGlobalBoolean(TEST_NAME)
assertThat(value).isFalse()
}
@Test
fun setValue_setTrue_returnTrue() {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = true
assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 0)).isEqualTo(1)
}
@Test
fun setValue_setFalse_returnFalse() {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = false
assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 1)).isEqualTo(0)
}
@Test
fun settingsGlobalBooleanFlow_valueNotChanged() = runBlocking {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = false
val flow = context.settingsGlobalBooleanFlow(TEST_NAME)
assertThat(flow.firstWithTimeoutOrNull()).isFalse()
}
@Test
fun settingsGlobalBooleanFlow_collectAfterValueChanged_onlyKeepLatest() = runBlocking {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = false
val flow = context.settingsGlobalBooleanFlow(TEST_NAME)
value = true
assertThat(flow.firstWithTimeoutOrNull()).isTrue()
}
@Test
fun settingsGlobalBooleanFlow_collectBeforeValueChanged_getBoth() = runBlocking {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = false
val listDeferred = async {
context.settingsGlobalBooleanFlow(TEST_NAME).toListWithTimeout()
}
delay(100)
value = true
assertThat(listDeferred.await()).containsExactly(false, true).inOrder()
}
private companion object {
const val TEST_NAME = "test_boolean_delegate"
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.settingsprovider
import android.content.Context
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.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
@RunWith(AndroidJUnit4::class)
class SettingsGlobalChangeFlowTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun settingsGlobalChangeFlow_sendInitialValueTrue() = runBlocking {
val flow = context.settingsGlobalChangeFlow(name = TEST_NAME, sendInitialValue = true)
assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
}
@Test
fun settingsGlobalChangeFlow_sendInitialValueFalse() = runBlocking {
val flow = context.settingsGlobalChangeFlow(name = TEST_NAME, sendInitialValue = false)
assertThat(flow.firstWithTimeoutOrNull()).isNull()
}
@Test
fun settingsGlobalChangeFlow_collectAfterValueChanged_onlyKeepLatest() = runBlocking {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = false
val flow = context.settingsGlobalChangeFlow(TEST_NAME)
value = true
assertThat(flow.toListWithTimeout()).hasSize(1)
}
@Test
fun settingsGlobalChangeFlow_collectBeforeValueChanged_getBoth() = runBlocking {
var value by context.settingsGlobalBoolean(TEST_NAME)
value = false
val listDeferred = async {
context.settingsGlobalChangeFlow(TEST_NAME).toListWithTimeout()
}
delay(100)
value = true
assertThat(listDeferred.await()).hasSize(2)
}
private companion object {
const val TEST_NAME = "test_boolean_delegate"
}
}

View File

@@ -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.settingslib.spaprivileged.settingsprovider
import android.content.Context
import android.provider.Settings
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.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
@RunWith(AndroidJUnit4::class)
class SettingsSecureBooleanTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun getValue_setTrue_returnTrue() {
Settings.Secure.putInt(context.contentResolver, TEST_NAME, 1)
val value by context.settingsSecureBoolean(TEST_NAME)
assertThat(value).isTrue()
}
@Test
fun getValue_setFalse_returnFalse() {
Settings.Secure.putInt(context.contentResolver, TEST_NAME, 0)
val value by context.settingsSecureBoolean(TEST_NAME)
assertThat(value).isFalse()
}
@Test
fun setValue_setTrue_returnTrue() {
var value by context.settingsSecureBoolean(TEST_NAME)
value = true
assertThat(Settings.Secure.getInt(context.contentResolver, TEST_NAME, 0)).isEqualTo(1)
}
@Test
fun setValue_setFalse_returnFalse() {
var value by context.settingsSecureBoolean(TEST_NAME)
value = false
assertThat(Settings.Secure.getInt(context.contentResolver, TEST_NAME, 1)).isEqualTo(0)
}
@Test
fun settingsSecureBooleanFlow_valueNotChanged() = runBlocking {
var value by context.settingsSecureBoolean(TEST_NAME)
value = false
val flow = context.settingsSecureBooleanFlow(TEST_NAME)
assertThat(flow.firstWithTimeoutOrNull()).isFalse()
}
@Test
fun settingsSecureBooleanFlow_collectAfterValueChanged_onlyKeepLatest() = runBlocking {
var value by context.settingsSecureBoolean(TEST_NAME)
value = false
val flow = context.settingsSecureBooleanFlow(TEST_NAME)
value = true
assertThat(flow.firstWithTimeoutOrNull()).isTrue()
}
@Test
fun settingsSecureBooleanFlow_collectBeforeValueChanged_getBoth() = runBlocking {
var value by context.settingsSecureBoolean(TEST_NAME)
value = false
val listDeferred = async {
context.settingsSecureBooleanFlow(TEST_NAME).toListWithTimeout()
}
delay(100)
value = true
assertThat(listDeferred.await()).containsExactly(false, true).inOrder()
}
private companion object {
const val TEST_NAME = "test_boolean_delegate"
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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.template.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.waitUntilExists
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AppInfoTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun appInfoLabel_isDisplayed() {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
}
val appInfoProvider = AppInfoProvider(packageInfo)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
appInfoProvider.AppInfo()
}
}
composeTestRule.waitUntilExists(hasText(LABEL))
}
@Test
fun appInfoVersion_whenDisplayVersionIsFalse() {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
appInfoProvider.AppInfo(displayVersion = false)
}
}
composeTestRule.onNodeWithText(VERSION_NAME).assertDoesNotExist()
}
@Test
fun appInfoVersion_whenDisplayVersionIsTrue() {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
appInfoProvider.AppInfo(displayVersion = true)
}
}
composeTestRule.onNodeWithText(VERSION_NAME).assertIsDisplayed()
}
@Test
fun footerAppVersion_versionIsDisplayed() {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
packageName = PACKAGE_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
appInfoProvider.FooterAppVersion()
}
}
composeTestRule.onNodeWithText(text = "version $VERSION_NAME", substring = true)
.assertIsDisplayed()
}
@Test
fun footerAppVersion_developmentEnabled_packageNameIsDisplayed() {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
packageName = PACKAGE_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
appInfoProvider.FooterAppVersion(showPackageName = true)
}
}
composeTestRule.onNodeWithText(text = PACKAGE_NAME, substring = true).assertIsDisplayed()
}
@Test
fun footerAppVersion_developmentDisabled_packageNameDoesNotExist() {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
packageName = PACKAGE_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
appInfoProvider.FooterAppVersion(showPackageName = false)
}
}
composeTestRule.onNodeWithText(text = PACKAGE_NAME, substring = true).assertDoesNotExist()
}
private companion object {
const val LABEL = "Label"
const val VERSION_NAME = "VersionName"
const val PACKAGE_NAME = "package.name"
val APP = object : ApplicationInfo() {
override fun loadLabel(pm: PackageManager) = LABEL
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.template.app
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.tests.testutils.TestAppListModel
import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AppListPageTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun title_isDisplayed() {
setContent()
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
}
@Test
fun appListState_hasCorrectInitialState() {
val inputState by setContent()
val state = inputState!!.state
assertThat(state.showSystem()).isFalse()
assertThat(state.searchQuery()).isEqualTo("")
}
@Test
fun canShowSystem() {
val inputState by setContent()
onMoreOptions().performClick()
composeTestRule.onNodeWithText(context.getString(R.string.menu_show_system)).performClick()
val state = inputState!!.state
assertThat(state.showSystem()).isTrue()
}
@Test
fun afterShowSystem_displayHideSystem() {
setContent()
onMoreOptions().performClick()
composeTestRule.onNodeWithText(context.getString(R.string.menu_show_system)).performClick()
onMoreOptions().performClick()
composeTestRule.onNodeWithText(context.getString(R.string.menu_hide_system))
.assertIsDisplayed()
}
@Test
fun noMoreOptions_notDisplayMoreOptions() {
setContent(noMoreOptions = true)
onMoreOptions().assertDoesNotExist()
}
@Test
fun noMoreOptions_showSystemIsFalse() {
val inputState by setContent(noMoreOptions = true)
val state = inputState!!.state
assertThat(state.showSystem()).isFalse()
}
private fun setContent(
noMoreOptions: Boolean = false,
header: @Composable () -> Unit = {},
): State<AppListInput<TestAppRecord>?> {
val appListState = mutableStateOf<AppListInput<TestAppRecord>?>(null)
composeTestRule.setContent {
AppListPage(
title = TITLE,
listModel = TestAppListModel(),
noMoreOptions = noMoreOptions,
header = header,
appList = { appListState.value = this },
)
}
return appListState
}
private fun onMoreOptions() =
composeTestRule.onNodeWithContentDescription(
context.getString(androidx.appcompat.R.string.abc_action_menu_overflow_description)
)
private companion object {
const val TITLE = "Title"
}
}

View File

@@ -0,0 +1,163 @@
/*
* 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.template.app
import android.content.pm.ApplicationInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.isToggleable
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AppListSwitchItemTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun appLabel_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { null },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun summary_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { null },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
}
@Test
fun switch_checkIsNull() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { null },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertDoesNotExist()
}
@Test
fun switch_checked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { true },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsOn()
}
@Test
fun switch_notChecked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { false },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsOff()
}
@Test
fun switch_changeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { false },
changeable = { true },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsEnabled()
}
@Test
fun switch_notChangeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { false },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsNotEnabled()
}
@Test
fun switch_onClick() {
var switchClicked = false
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
checked = { false },
changeable = { true },
onCheckedChange = { switchClicked = true },
)
}
composeTestRule.onNode(isToggleable()).performClick()
assertThat(switchClicked).isTrue()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
const val SUMMARY = "Summary"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
val ITEM_MODEL = AppListItemModel(
record = object : AppRecord {
override val app = APP
},
label = LABEL,
summary = { SUMMARY },
)
}
}

View File

@@ -0,0 +1,186 @@
/*
* 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.template.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.icu.text.CollationKey
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.dp
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.AppEntry
import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.IAppListViewModel
import com.android.settingslib.spaprivileged.tests.testutils.TestAppListModel
import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@OptIn(ExperimentalTestApi::class)
@RunWith(AndroidJUnit4::class)
class AppListTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun whenHasOptions_firstOptionDisplayed() {
setContent(options = listOf(OPTION_0, OPTION_1))
composeTestRule.waitUntilExactlyOneExists(
matcher = hasText(OPTION_0),
timeoutMillis = 5_000,
)
composeTestRule.onNodeWithText(OPTION_1).assertDoesNotExist()
}
@Test
fun whenHasOptions_couldSwitchOption() {
setContent(options = listOf(OPTION_0, OPTION_1))
composeTestRule.waitUntilExactlyOneExists(
matcher = hasText(OPTION_0),
timeoutMillis = 5_000,
)
composeTestRule.onNodeWithText(OPTION_0).performClick()
composeTestRule.onNodeWithText(OPTION_1).performClick()
composeTestRule.onNodeWithText(OPTION_1).assertIsDisplayed()
composeTestRule.onNodeWithText(OPTION_0).assertDoesNotExist()
}
@Test
fun whenNoApps() {
setContent(appEntries = emptyList())
composeTestRule.waitUntilExactlyOneExists(
matcher = hasText(context.getString(R.string.no_applications)),
timeoutMillis = 5_000,
)
}
@Test
fun couldShowAppItem() {
setContent(appEntries = listOf(APP_ENTRY_A))
composeTestRule.waitUntilExactlyOneExists(
matcher = hasText(APP_ENTRY_A.label),
timeoutMillis = 5_000,
)
}
@Test
fun couldShowHeader() {
setContent(appEntries = listOf(APP_ENTRY_A), header = { Text(HEADER) })
composeTestRule.waitUntilExactlyOneExists(
matcher = hasText(HEADER),
timeoutMillis = 5_000,
)
}
@Test
fun whenNotGrouped_groupTitleDoesNotExist() {
setContent(appEntries = listOf(APP_ENTRY_A, APP_ENTRY_B), enableGrouping = false)
composeTestRule.onNodeWithText(GROUP_A).assertDoesNotExist()
composeTestRule.onNodeWithText(GROUP_B).assertDoesNotExist()
}
@Test
fun whenGrouped_groupTitleDisplayed() {
setContent(appEntries = listOf(APP_ENTRY_A, APP_ENTRY_B), enableGrouping = true)
composeTestRule.waitUntilExactlyOneExists(
matcher = hasText(GROUP_A),
timeoutMillis = 5_000,
)
composeTestRule.onNodeWithText(GROUP_B).assertIsDisplayed()
}
private fun setContent(
options: List<String> = emptyList(),
appEntries: List<AppEntry<TestAppRecord>> = emptyList(),
header: @Composable () -> Unit = {},
enableGrouping: Boolean = false,
) {
val appListInput = AppListInput(
config = AppListConfig(
userIds = listOf(USER_ID),
showInstantApps = false,
matchAnyUserForAdmin = false,
),
listModel = TestAppListModel(enableGrouping = enableGrouping),
state = AppListState(showSystem = { false }, searchQuery = { "" }),
header = header,
bottomPadding = 0.dp,
)
val listViewModel = object : IAppListViewModel<TestAppRecord> {
override val optionFlow = MutableStateFlow<Int?>(null)
override val spinnerOptionsFlow = flowOf(options.mapIndexed { index, option ->
SpinnerOption(id = index, text = option)
})
override val appListDataFlow = flowOf(AppListData(appEntries, option = 0))
}
composeTestRule.setContent {
appListInput.AppListImpl { listViewModel }
}
}
private companion object {
const val USER_ID = 0
const val OPTION_0 = "Option 1"
const val OPTION_1 = "Option 2"
const val HEADER = "Header"
const val GROUP_A = "Group A"
const val GROUP_B = "Group B"
val APP_ENTRY_A = AppEntry(
record = TestAppRecord(
app = ApplicationInfo().apply {
packageName = "package.name.a"
},
group = GROUP_A,
),
label = "Label A",
labelCollationKey = CollationKey("", byteArrayOf()),
)
val APP_ENTRY_B = AppEntry(
record = TestAppRecord(
app = ApplicationInfo().apply {
packageName = "package.name.b"
},
group = GROUP_B,
),
label = "Label B",
labelCollationKey = CollationKey("", byteArrayOf()),
)
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.template.app
import android.content.pm.ApplicationInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.isToggleable
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AppListTwoTargetSwitchItemTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun appLabel_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { null },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun summary_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { null },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
}
@Test
fun title_onClick() {
var titleClicked = false
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = { titleClicked = true },
checked = { false },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNodeWithText(LABEL).performClick()
assertThat(titleClicked).isTrue()
}
@Test
fun switch_checkIsNull() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { null },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertDoesNotExist()
}
@Test
fun switch_checked() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { true },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsOn()
}
@Test
fun switch_notChecked() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { false },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsOff()
}
@Test
fun switch_changeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { false },
changeable = { true },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsEnabled()
}
@Test
fun switch_notChangeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { false },
changeable = { false },
onCheckedChange = {},
)
}
composeTestRule.onNode(isToggleable()).assertIsNotEnabled()
}
@Test
fun switch_onClick() {
var switchClicked = false
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
checked = { false },
changeable = { true },
onCheckedChange = { switchClicked = true },
)
}
composeTestRule.onNode(isToggleable()).performClick()
assertThat(switchClicked).isTrue()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
const val SUMMARY = "Summary"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
val ITEM_MODEL = AppListItemModel(
record = object : AppRecord {
override val app = APP
},
label = LABEL,
summary = { SUMMARY },
)
}
}

View File

@@ -0,0 +1,339 @@
/*
* 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.template.app
import android.app.AppOpsManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
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.testutils.firstWithTimeoutOrNull
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.test.R
import com.google.common.truth.Truth.assertThat
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.doNothing
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppOpPermissionAppListTest {
@get:Rule
val composeTestRule = createComposeRule()
private val packageManagers = mock<IPackageManagers>()
private val appOpsManager = mock<AppOpsManager>()
private val packageManager = mock<PackageManager> {
doNothing().whenever(mock).updatePermissionFlags(any(), any(), any(), any(), any())
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { appOpsManager } doReturn appOpsManager
on { packageManager } doReturn packageManager
}
private val listModel = TestAppOpPermissionAppListModel()
@Test
fun transformItem_recordHasCorrectApp() {
val record = listModel.transformItem(APP)
assertThat(record.app).isSameInstanceAs(APP)
}
@Test
fun transformItem_hasRequestPermission() = runTest {
with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(true) }
val record = listModel.transformItem(APP)
assertThat(record.hasRequestPermission).isTrue()
}
@Test
fun transformItem_notRequestPermission() = runTest {
with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) }
val record = listModel.transformItem(APP)
assertThat(record.hasRequestPermission).isFalse()
}
@Test
fun transformItem_hasRequestBroaderPermission() = runTest {
listModel.broaderPermission = BROADER_PERMISSION
with(packageManagers) {
whenever(APP.hasRequestPermission(BROADER_PERMISSION)).thenReturn(true)
}
val record = listModel.transformItem(APP)
assertThat(record.hasRequestBroaderPermission).isTrue()
}
@Test
fun transformItem_notRequestBroaderPermission() = runTest {
listModel.broaderPermission = BROADER_PERMISSION
with(packageManagers) {
whenever(APP.hasRequestPermission(BROADER_PERMISSION)).thenReturn(false)
}
val record = listModel.transformItem(APP)
assertThat(record.hasRequestPermission).isFalse()
}
@Test
fun filter() = runTest {
with(packageManagers) { whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false) }
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val recordListFlow = listModel.filter(flowOf(USER_ID), flowOf(listOf(record)))
val recordList = recordListFlow.firstWithTimeoutOrNull()!!
assertThat(recordList).isEmpty()
}
@Test
fun isAllowed_allowed() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ALLOWED),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isTrue()
}
@Test
fun isAllowed_defaultAndHasGrantPermission() {
with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true) }
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isTrue()
}
@Test
fun isAllowed_defaultAndNotGrantPermission() {
with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) }
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isFalse()
}
@Test
fun isAllowed_broaderPermissionTrumps() {
listModel.broaderPermission = BROADER_PERMISSION
with(packageManagers) {
whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false)
whenever(APP.hasGrantPermission(BROADER_PERMISSION)).thenReturn(true)
}
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = true,
hasRequestPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isTrue()
}
@Test
fun isAllowed_notAllowed() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isFalse()
}
@Test
fun isChangeable_notRequestPermission() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isFalse()
}
@Test
fun isChangeable_notChangeablePackages() {
val record =
AppOpPermissionRecord(
app = NOT_CHANGEABLE_APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isFalse()
}
@Test
fun isChangeable_hasRequestPermissionAndChangeable() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isTrue()
}
@Test
fun isChangeable_broaderPermissionTrumps() {
listModel.broaderPermission = BROADER_PERMISSION
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = true,
hasRequestPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isFalse()
}
@Test
fun setAllowed() {
val appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
appOpsController = appOpsController,
)
listModel.setAllowed(record = record, newAllowed = true)
assertThat(appOpsController.setAllowedCalledWith).isTrue()
}
@Test
fun setAllowed_setModeByUid() {
listModel.setModeByUid = true
val record = listModel.transformItem(APP)
listModel.setAllowed(record = record, newAllowed = true)
verify(appOpsManager).setUidMode(listModel.appOp, APP.uid, AppOpsManager.MODE_ALLOWED)
}
private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? {
lateinit var isAllowedState: () -> Boolean?
composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) }
return isAllowedState()
}
private inner class TestAppOpPermissionAppListModel :
AppOpPermissionListModel(context, packageManagers) {
override val pageTitleResId = R.string.test_app_op_permission_title
override val switchTitleResId = R.string.test_app_op_permission_switch_title
override val footerResId = R.string.test_app_op_permission_footer
override val appOp = AppOpsManager.OP_MANAGE_MEDIA
override val permission = PERMISSION
override var broaderPermission: String? = null
override var setModeByUid = false
}
private companion object {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
const val PERMISSION = "PERMISSION"
const val BROADER_PERMISSION = "BROADER_PERMISSION"
val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME }
val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "android" }
}
}
private class FakeAppOpsController(private val fakeMode: Int) : IAppOpsController {
var setAllowedCalledWith: Boolean? = null
override val mode = flowOf(fakeMode)
override fun setAllowed(allowed: Boolean) {
setAllowedCalledWith = allowed
}
override fun getMode() = fakeMode
}

View File

@@ -0,0 +1,113 @@
/*
* 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.template.app
import android.app.usage.StorageStats
import android.app.usage.StorageStatsManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager.NameNotFoundException
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
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.spaprivileged.framework.common.storageStatsManager
import com.android.settingslib.spaprivileged.model.app.userHandle
import java.util.UUID
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 AppStorageSizeTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var storageStatsManager: StorageStatsManager
private val app = ApplicationInfo().apply {
storageUuid = UUID.randomUUID()
}
@Before
fun setUp() {
whenever(context.storageStatsManager).thenReturn(storageStatsManager)
whenever(
storageStatsManager.queryStatsForPackage(
app.storageUuid,
app.packageName,
app.userHandle,
)
).thenReturn(STATS)
}
@Test
fun getStorageSize() {
var storageSize = stateOf("")
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
storageSize = app.getStorageSize()
}
}
composeTestRule.waitUntil { storageSize.value == "120 B" }
}
@Test
fun getStorageSize_throwException() {
var storageSize = stateOf("Computing")
whenever(
storageStatsManager.queryStatsForPackage(
app.storageUuid,
app.packageName,
app.userHandle,
)
).thenThrow(NameNotFoundException())
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
storageSize = app.getStorageSize()
}
}
composeTestRule.waitUntil { storageSize.value == "" }
}
companion object {
private val STATS = StorageStats().apply {
codeBytes = 100
dataBytes = 20
cacheBytes = 3
}
}
}

View File

@@ -0,0 +1,237 @@
/*
* 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.template.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider
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
@RunWith(AndroidJUnit4::class)
class TogglePermissionAppInfoPageTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private val packageManagers = mock<IPackageManagers> {
on { getPackageInfoAsUser(PACKAGE_NAME, USER_ID) } doReturn PACKAGE_INFO
}
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
private val fakeRestrictionsProvider = FakeRestrictionsProvider().apply {
restrictedMode = NoRestricted
}
private val appListTemplate =
TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider))
private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate)
@Test
fun buildEntry() {
val entryList = appInfoPageProvider.buildEntry(null)
assertThat(entryList).hasSize(1)
assertThat(entryList[0].label).isEqualTo("AllowControl")
}
@Test
fun entryItem_whenNotChangeable_notDisplayed() {
val listModel = TestTogglePermissionAppListModel(isChangeable = false)
setEntryItem(listModel)
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun entryItem_whenChangeable_titleDisplayed() {
val listModel = TestTogglePermissionAppListModel(isChangeable = true)
setEntryItem(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
.assertIsDisplayed()
}
@Test
fun entryItem_whenAllowed_summaryIsAllowed() {
val listModel = TestTogglePermissionAppListModel(isAllowed = true, isChangeable = true)
setEntryItem(listModel)
composeTestRule.onNodeWithText(context.getString(R.string.app_permission_summary_allowed))
.assertIsDisplayed()
}
@Test
fun entryItem_whenNotAllowed_summaryIsNotAllowed() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = true)
setEntryItem(listModel)
composeTestRule.onNodeWithText(
context.getString(R.string.app_permission_summary_not_allowed)
).assertIsDisplayed()
}
@Test
fun entryItem_onClick() {
val listModel = TestTogglePermissionAppListModel(isChangeable = true)
setEntryItem(listModel)
composeTestRule.onRoot().performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith)
.isEqualTo("TogglePermissionAppInfoPage/test.PERMISSION/package.name/0")
}
@Test
fun infoPage_title_isDisplayed() {
val listModel = TestTogglePermissionAppListModel()
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
.assertIsDisplayed()
}
@Test
fun infoPage_whenAllowed_switchIsOn() {
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsOn()
}
@Test
fun infoPage_whenNotAllowed_switchIsOff() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsOff()
}
@Test
fun infoPage_whenChangeableAndClick() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = true)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.performClick()
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsOn()
}
@Test
fun infoPage_whenNotChangeableAndClick() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.performClick()
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsOff()
}
@Test
fun infoPage_whenNotChangeable_switchNotEnabled() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsDisplayed()
.assertIsNotEnabled()
}
@Test
fun infoPage_footer_isDisplayed() {
val listModel = TestTogglePermissionAppListModel()
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.footerResId))
.assertIsDisplayed()
}
private fun setEntryItem(listModel: TestTogglePermissionAppListModel) {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
listModel.TogglePermissionAppInfoPageEntryItem(
permissionType = PERMISSION_TYPE,
app = APP,
)
}
}
}
private fun setTogglePermissionAppInfoPage(listModel: TestTogglePermissionAppListModel) {
composeTestRule.setContent {
listModel.TogglePermissionAppInfoPage(
packageName = PACKAGE_NAME,
userId = USER_ID,
packageManagers = packageManagers,
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
)
}
}
private companion object {
const val PERMISSION_TYPE = "test.PERMISSION"
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
val PACKAGE_INFO = PackageInfo().apply {
packageName = PACKAGE_NAME
applicationInfo = APP
}
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.template.app
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TogglePermissionAppListPageTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@Test
fun pageTitle() {
val listModel = TestTogglePermissionAppListModel()
composeTestRule.setContent {
listModel.TogglePermissionAppList(
permissionType = PERMISSION_TYPE,
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
appList = {},
)
}
composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
.assertIsDisplayed()
}
@Test
fun summary_whenAllowed() {
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
val summary = getSummary(listModel)
assertThat(summary).isEqualTo(context.getString(R.string.app_permission_summary_allowed))
}
@Test
fun summary_whenNotAllowed() {
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
val summary = getSummary(listModel)
assertThat(summary)
.isEqualTo(context.getString(R.string.app_permission_summary_not_allowed))
}
@Test
fun summary_whenComputingAllowed() {
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = null)
val summary = getSummary(listModel)
assertThat(summary).isEqualTo(context.getPlaceholder())
}
@Test
fun appListItem_onClick_navigate() {
val listModel = TestTogglePermissionAppListModel()
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(createInternalAppListModel(listModel)) {
AppListItemModel(
record = listModel.transformItem(APP),
label = LABEL,
summary = { SUMMARY },
).AppItem()
}
}
}
composeTestRule.onNodeWithText(LABEL).performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith)
.isEqualTo("TogglePermissionAppInfoPage/test.PERMISSION/package.name/0")
}
@Test
fun getRoute() {
val route = TogglePermissionAppListPageProvider.getRoute(PERMISSION_TYPE)
assertThat(route).isEqualTo("TogglePermissionAppList/test.PERMISSION")
}
@Test
fun buildInjectEntry_titleDisplayed() {
val listModel = TestTogglePermissionAppListModel()
val entry = TogglePermissionAppListPageProvider.buildInjectEntry(PERMISSION_TYPE) {
listModel
}.build()
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
entry.UiLayout()
}
}
composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
.assertIsDisplayed()
}
private fun createInternalAppListModel(listModel: TestTogglePermissionAppListModel) =
TogglePermissionInternalAppListModel(
context = context,
permissionType = PERMISSION_TYPE,
listModel = listModel,
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
)
private fun getSummary(listModel: TestTogglePermissionAppListModel): String {
lateinit var summary: () -> String
composeTestRule.setContent {
summary = createInternalAppListModel(listModel).getSummary(record = TestAppRecord(APP))
}
return summary()
}
private companion object {
const val PERMISSION_TYPE = "test.PERMISSION"
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
const val SUMMARY = "Summary"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.template.app
import android.content.Context
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.test.R
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TogglePermissionAppListTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun appListInjectEntry_titleDisplayed() {
val entry = TestTogglePermissionAppListProvider.buildAppListInjectEntry().build()
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
entry.UiLayout()
}
}
composeTestRule.onNodeWithText(context.getString(R.string.test_permission_title))
.assertIsDisplayed()
}
@Test
fun appListRoute() {
val route = TestTogglePermissionAppListProvider.getAppListRoute()
assertThat(route).isEqualTo("TogglePermissionAppList/test.PERMISSION")
}
@Test
fun togglePermissionAppListTemplate_createPageProviders() {
val togglePermissionAppListTemplate =
TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider))
val createPageProviders = togglePermissionAppListTemplate.createPageProviders()
assertThat(createPageProviders).hasSize(2)
assertThat(createPageProviders.any { it is TogglePermissionAppListPageProvider }).isTrue()
assertThat(createPageProviders.any { it is TogglePermissionAppInfoPageProvider }).isTrue()
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.template.common
import android.content.Context
import android.content.pm.UserInfo
import android.content.pm.UserProperties
import android.os.UserManager
import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.framework.common.userManager
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
@RunWith(AndroidJUnit4::class)
class UserProfilePagerTest {
@get:Rule
val composeTestRule = createComposeRule()
private val mockUserManager = mock<UserManager> {
on { getProfiles(any()) } doReturn listOf(USER_0)
on { getUserProperties(USER_0.userHandle) } doReturn
UserProperties.Builder()
.setShowInSettings(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT)
.build()
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { userManager } doReturn mockUserManager
}
@Test
fun userProfilePager() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
UserProfilePager { userGroup ->
Text(text = userGroup.userInfos.joinToString { it.id.toString() })
}
}
}
composeTestRule.onNodeWithText(USER_0.id.toString()).assertIsDisplayed()
}
private companion object {
val USER_0 = UserInfo(0, "", 0)
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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.template.preference
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.isOff
import androidx.compose.ui.test.isOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RestrictedMainSwitchPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
private val switchPreferenceModel = object : SwitchPreferenceModel {
override val title = TITLE
private val checkedState = mutableStateOf(true)
override val checked = { checkedState.value }
override val onCheckedChange: (Boolean) -> Unit = { checkedState.value = it }
}
@Test
fun whenRestrictionsKeysIsEmpty_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenRestrictionsKeysIsEmpty_toggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenNoRestricted_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenNoRestricted_toggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBaseUserRestricted_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBaseUserRestricted_notToggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBlockedByAdmin_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNodeWithText(FakeBlockedByAdmin.SUMMARY).assertDoesNotExist()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenBlockedByAdmin_click() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
}
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedMainSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
fakeRestrictionsProvider
}
}
}
private companion object {
const val TITLE = "Title"
const val USER_ID = 0
const val RESTRICTION_KEY = "restriction_key"
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.template.preference
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RestrictedPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
private var clicked = false
private val preferenceModel = object : PreferenceModel {
override val title = TITLE
override val onClick = { clicked = true }
}
@Test
fun whenRestrictionsKeysIsEmpty_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenRestrictionsKeysIsEmpty_clickable() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(clicked).isTrue()
}
@Test
fun whenNoRestricted_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenNoRestricted_clickable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(clicked).isTrue()
}
@Test
fun whenBaseUserRestricted_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
}
@Test
fun whenBaseUserRestricted_notClickable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(clicked).isFalse()
}
@Test
fun whenBlockedByAdmin_widgetInEnableStateToAllowClick() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenBlockedByAdmin_click() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
}
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedPreference(preferenceModel, restrictions) { _, _ ->
fakeRestrictionsProvider
}
}
}
private companion object {
const val TITLE = "Title"
const val USER_ID = 0
const val RESTRICTION_KEY = "restriction_key"
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.template.preference
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.isOff
import androidx.compose.ui.test.isOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RestrictedSwitchPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
private val switchPreferenceModel = object : SwitchPreferenceModel {
override val title = TITLE
private val checkedState = mutableStateOf(true)
override val checked = { checkedState.value }
override val onCheckedChange: (Boolean) -> Unit = { checkedState.value = it }
}
@Test
fun whenRestrictionsKeysIsEmpty_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenRestrictionsKeysIsEmpty_toggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenNoRestricted_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenNoRestricted_toggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBaseUserRestricted_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBaseUserRestricted_notToggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBlockedByAdmin_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNodeWithText(FakeBlockedByAdmin.SUMMARY).assertIsDisplayed()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenBlockedByAdmin_click() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
}
@Test
fun whenBlockedByEcm_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNodeWithText(FakeBlockedByEcm.SUMMARY).assertIsDisplayed()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenBlockedByEcm_click() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
}
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
fakeRestrictionsProvider
}
}
}
private companion object {
const val TITLE = "Title"
const val USER_ID = 0
const val RESTRICTION_KEY = "restriction_key"
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.spaprivileged.template.scaffold
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RestrictedMenuItemTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
private var menuItemOnClickIsCalled = false
@Test
fun whenRestrictionsKeysIsEmpty_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenRestrictionsKeysIsEmpty_clickable() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(menuItemOnClickIsCalled).isTrue()
}
@Test
fun whenNoRestricted_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenNoRestricted_clickable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(menuItemOnClickIsCalled).isTrue()
}
@Test
fun whenBaseUserRestricted_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsNotEnabled()
}
@Test
fun whenBaseUserRestricted_notClickable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(menuItemOnClickIsCalled).isFalse()
}
@Test
fun whenBlockedByAdmin_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenBlockedByAdmin_onClick_showAdminSupportDetails() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
assertThat(menuItemOnClickIsCalled).isFalse()
}
@Test
fun whenBlockedByEcm_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
setContent(restrictions)
composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
}
@Test
fun whenBlockedByEcm_onClick_showEcmDetails() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
setContent(restrictions)
composeTestRule.onRoot().performClick()
assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
assertThat(menuItemOnClickIsCalled).isFalse()
}
private fun setContent(restrictions: Restrictions) {
val fakeMoreOptionsScope = object : MoreOptionsScope() {
override fun dismiss() {}
}
composeTestRule.setContent {
fakeMoreOptionsScope.RestrictedMenuItemImpl(
text = TEXT,
restrictions = restrictions,
onClick = { menuItemOnClickIsCalled = true },
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
)
}
}
private companion object {
const val TEXT = "Text"
const val USER_ID = 0
const val RESTRICTION_KEY = "restriction_key"
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.tests.testutils
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
class FakeBlockedByAdmin : BlockedByAdmin {
var sendShowAdminSupportDetailsIntentIsCalled = false
override fun getSummary(checked: Boolean?) = SUMMARY
override fun sendShowAdminSupportDetailsIntent() {
sendShowAdminSupportDetailsIntentIsCalled = true
}
companion object {
const val SUMMARY = "Blocked by admin"
}
}
class FakeBlockedByEcm : BlockedByEcm {
var showRestrictedSettingsDetailsIsCalled = false
override fun showRestrictedSettingsDetails() {
showRestrictedSettingsDetailsIsCalled = true
}
companion object {
const val SUMMARY = "Disabled"
}
}
class FakeRestrictionsProvider : RestrictionsProvider {
var restrictedMode: RestrictedMode? = null
@Composable
override fun restrictedModeState() = stateOf(restrictedMode)
}

View File

@@ -0,0 +1,38 @@
/*
* 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.tests.testutils
import android.content.pm.ApplicationInfo
import com.android.settingslib.spa.framework.util.mapItem
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import kotlinx.coroutines.flow.Flow
data class TestAppRecord(
override val app: ApplicationInfo,
val group: String? = null,
) : AppRecord
class TestAppListModel(
private val enableGrouping: Boolean = false,
) : AppListModel<TestAppRecord> {
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
appListFlow.mapItem(::TestAppRecord)
override fun getGroupTitle(option: Int, record: TestAppRecord) =
if (enableGrouping) record.group else null
}

View File

@@ -0,0 +1,51 @@
/*
* 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.tests.testutils
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
import com.android.settingslib.spaprivileged.test.R
import kotlinx.coroutines.flow.Flow
class TestTogglePermissionAppListModel(
isAllowed: Boolean? = null,
private val isChangeable: Boolean = false,
) : TogglePermissionAppListModel<TestAppRecord> {
override val pageTitleResId = R.string.test_permission_title
override val switchTitleResId = R.string.test_permission_switch_title
override val footerResId = R.string.test_permission_footer
private var isAllowed by mutableStateOf(isAllowed)
override fun transformItem(app: ApplicationInfo) = TestAppRecord(app = app)
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<TestAppRecord>>) =
recordListFlow
@Composable
override fun isAllowed(record: TestAppRecord) = { isAllowed }
override fun isChangeable(record: TestAppRecord) = isChangeable
override fun setAllowed(record: TestAppRecord, newAllowed: Boolean) {
isAllowed = newAllowed
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.tests.testutils
import android.content.Context
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object TestTogglePermissionAppListProvider : TogglePermissionAppListProvider {
override val permissionType = "test.PERMISSION"
override fun createModel(context: Context) = TestTogglePermissionAppListModel()
}