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