fix: 引入Settings的Module

This commit is contained in:
2024-12-10 14:57:24 +08:00
parent ad8fc8731d
commit df105485bd
6934 changed files with 896168 additions and 2 deletions

View File

@@ -0,0 +1,51 @@
//
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package {
default_team: "trendy_team_android_settings_app",
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "SettingsSpaUnitTests",
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
srcs: [
"src/**/*.kt",
],
use_resource_processor: true,
static_libs: [
"Settings-core",
"SpaLibTestUtils",
"androidx.compose.runtime_runtime",
"androidx.test.ext.junit",
"androidx.test.runner",
"androidx.fragment_fragment-testing",
"flag-junit",
"mockito-target-extended-minus-junit4",
],
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
kotlincflags: [
"-Xjvm-default=all",
],
instrumentation_for: "Settings-core",
}

View File

@@ -0,0 +1,38 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.settings.tests.spa_unit">
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
<application android:debuggable="true">
<provider android:name="com.android.settings.slices.SettingsSliceProvider"
android:authorities="${applicationId}.slices"
tools:replace="android:authorities"/>
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="Tests for Settings SPA package"
android:targetPackage="com.android.settings.tests.spa_unit"/>
</manifest>

View File

@@ -0,0 +1,20 @@
<!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ff0000"/>
<size android:width="24dp" android:height="24dp" />
</shape>

View File

@@ -0,0 +1,21 @@
<!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#00ff00"/>
<size android:width="24dp" android:height="24dp" />
</shape>

View File

@@ -0,0 +1,20 @@
<!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#0000ff"/>
<size android:width="24dp" android:height="24dp" />
</shape>

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.activityembedding
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.activityembedding.EmbeddedDeepLinkUtils.getTrampolineIntent
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class EmbeddedDeepLinkUtilsTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun getTrampolineIntent_intentSelector_shouldNotChangeIntentAction() {
val targetIntent = Intent().setClassName(
"android",
"com.android.internal.app.PlatLogoActivity"
)
val intent = Intent(Settings.ACTION_DISPLAY_SETTINGS).apply {
setComponent(resolveActivity(context.packageManager))
setSelector(
Intent().setData(
Uri.fromParts(
targetIntent.toUri(Intent.URI_INTENT_SCHEME),
/* ssp= */ "",
/* fragment= */ null,
)
)
)
}
val resultIntent = getTrampolineIntent(intent, "menu_key")
val intentUriString =
resultIntent.getStringExtra(Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI)
val parsedIntent = Intent.parseUri(intentUriString, Intent.URI_INTENT_SCHEME)
assertThat(parsedIntent.action).isEqualTo(intent.action)
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.settings.applications.specialaccess
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.res.Resources
import android.net.NetworkPolicyManager
import android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.applications.specialaccess.DataSaverController.Companion.getUnrestrictedSummary
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
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.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class DataSaverControllerTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Spy
private val resources: Resources = context.resources
@Mock
private lateinit var networkPolicyManager: NetworkPolicyManager
@Mock
private lateinit var dataSaverController: DataSaverController
@Before
fun setUp() {
whenever(context.applicationContext).thenReturn(context)
whenever(context.resources).thenReturn(resources)
whenever(NetworkPolicyManager.from(context)).thenReturn(networkPolicyManager)
dataSaverController = DataSaverController(context, "key")
}
@Test
fun getAvailabilityStatus_whenConfigOn_available() {
whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true)
assertThat(dataSaverController.availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_whenConfigOff_unsupportedOnDevice() {
whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false)
assertThat(dataSaverController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
}
@Test
fun getUnrestrictedSummary_whenTwoAppsAllowed() = runTest {
whenever(
networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
).thenReturn(intArrayOf(APP1.uid, APP2.uid))
val summary =
getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
assertThat(summary)
.isEqualTo("2 apps allowed to use unrestricted data when Data Saver is on")
}
@Test
fun getUnrestrictedSummary_whenNoAppsAllowed() = runTest {
whenever(
networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
).thenReturn(intArrayOf())
val summary =
getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
assertThat(summary)
.isEqualTo("0 apps allowed to use unrestricted data when Data Saver is on")
}
private companion object {
val APP1 = ApplicationInfo().apply { uid = 10001 }
val APP2 = ApplicationInfo().apply { uid = 10002 }
val APP3 = ApplicationInfo().apply { uid = 10003 }
object FakeAppListRepository : AppListRepository {
override suspend fun loadApps(
userId: Int,
loadInstantApps: Boolean,
matchAnyUserForAdmin: Boolean,
) = emptyList<ApplicationInfo>()
override fun showSystemPredicate(
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { false }
override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet()
override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) =
listOf(APP1, APP2, APP3)
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.settings.datausage
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
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.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.argThat
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 AppDataUsageAppSettingsControllerTest {
private val packageManager = mock<PackageManager>()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn packageManager
}
private val controller = AppDataUsageAppSettingsController(context, KEY)
private val preference = PreferenceCategory(context).apply { key = KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
}
@Test
fun onViewCreated_noSettingsActivity_hidePreference(): Unit = runBlocking {
controller.init(listOf(PACKAGE_NAME), USER_ID)
controller.displayPreference(preferenceScreen)
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isVisible).isFalse()
}
@Test
fun onViewCreated_hasSettingsActivity_showPreference(): Unit = runBlocking {
packageManager.stub {
on {
resolveActivityAsUser(
argThat {
action == Intent.ACTION_MANAGE_NETWORK_USAGE && getPackage() == PACKAGE_NAME
},
eq(0),
eq(USER_ID),
)
} doReturn ResolveInfo()
}
controller.init(listOf(PACKAGE_NAME), USER_ID)
controller.displayPreference(preferenceScreen)
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isVisible).isTrue()
}
private companion object {
const val KEY = "test_key"
const val PACKAGE_NAME = "package.name"
const val USER_ID = 0
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.settings.datausage
import android.content.Context
import android.util.Range
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.IAppDataUsageDetailsRepository
import com.android.settings.datausage.lib.NetworkUsageDetailsData
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class AppDataUsageCycleControllerTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val preference = spy(SpinnerPreference(context, null).apply { key = KEY })
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private val controller = AppDataUsageCycleController(context, KEY)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
}
@Test
fun onViewCreated_noUsage_hidePreference(): Unit = runBlocking {
val repository = object : IAppDataUsageDetailsRepository {
override suspend fun queryDetailsForCycles() = emptyList<NetworkUsageDetailsData>()
}
controller.displayPreference(preferenceScreen)
controller.init(repository) {}
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isVisible).isFalse()
}
@Test
fun onViewCreated_hasUsage_showPreference(): Unit = runBlocking {
val detailsData = NetworkUsageDetailsData(
range = Range(1, 2),
totalUsage = 11,
foregroundUsage = 1,
backgroundUsage = 10,
)
val repository = object : IAppDataUsageDetailsRepository {
override suspend fun queryDetailsForCycles() = listOf(detailsData)
}
controller.displayPreference(preferenceScreen)
controller.init(repository) {}
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isVisible).isTrue()
}
@Test
fun setInitialCycles() {
val repository = object : IAppDataUsageDetailsRepository {
override suspend fun queryDetailsForCycles() = emptyList<NetworkUsageDetailsData>()
}
controller.displayPreference(preferenceScreen)
controller.init(repository) {}
controller.setInitialCycles(
initialCycles = listOf(CYCLE2_END_TIME, CYCLE1_END_TIME, CYCLE1_START_TIME),
initialSelectedEndTime = CYCLE1_END_TIME,
)
verify(preference).setSelection(1)
}
private companion object {
const val KEY = "test_key"
const val CYCLE1_START_TIME = 1694444444000L
const val CYCLE1_END_TIME = 1695555555000L
const val CYCLE2_END_TIME = 1695566666000L
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.settings.datausage
import android.content.Context
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.AppPreferenceRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class AppDataUsageListControllerTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val repository = mock<AppPreferenceRepository> {
on { loadAppPreferences(any()) } doAnswer {
val uids = it.arguments[0] as List<*>
uids.map { Preference(context) }
}
}
private val controller = AppDataUsageListController(
context = context,
preferenceKey = KEY,
repository = repository,
)
private val preference = PreferenceCategory(context).apply { key = KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
}
@Test
fun onViewCreated_singleUid_hidePreference(): Unit = runBlocking {
controller.init(listOf(UID_0))
controller.displayPreference(preferenceScreen)
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isVisible).isFalse()
}
@Test
fun onViewCreated_twoUid_showPreference(): Unit = runBlocking {
controller.init(listOf(UID_0, UID_1))
controller.displayPreference(preferenceScreen)
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isVisible).isTrue()
assertThat(preference.preferenceCount).isEqualTo(2)
}
private companion object {
const val KEY = "test_key"
const val UID_0 = 10000
const val UID_1 = 10001
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.settings.datausage
import android.content.Context
import android.net.NetworkTemplate
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.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.datausage.lib.BillingCycleRepository
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.stub
@RunWith(AndroidJUnit4::class)
class BillingCyclePreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val mockBillingCycleRepository = mock<BillingCycleRepository>()
private val context: Context = ApplicationProvider.getApplicationContext()
private val preference = BillingCyclePreference(context, null, mockBillingCycleRepository)
@Test
fun setTemplate_titleDisplayed() {
setTemplate()
composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle))
.assertIsDisplayed()
}
@Test
fun setTemplate_modifiable_enabled() {
mockBillingCycleRepository.stub {
on { isModifiable(SUB_ID) } doReturn true
}
setTemplate()
composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle)).assertIsEnabled()
}
@Test
fun setTemplate_notModifiable_notEnabled() {
mockBillingCycleRepository.stub {
on { isModifiable(SUB_ID) } doReturn false
}
setTemplate()
composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle))
.assertIsNotEnabled()
}
private fun setTemplate() {
preference.setTemplate(mock<NetworkTemplate>(), SUB_ID)
composeTestRule.setContent {
preference.Content()
}
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage
import android.content.Context
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.PreferenceScreen
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.NetworkCycleChartData
import com.android.settings.datausage.lib.NetworkUsageData
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class ChartDataUsagePreferenceControllerTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val preference = mock<ChartDataUsagePreference>()
private val preferenceScreen = mock<PreferenceScreen> {
onGeneric { findPreference(KEY) } doReturn preference
}
private val controller = ChartDataUsagePreferenceController(context, KEY)
@Before
fun setUp() {
controller.displayPreference(preferenceScreen)
controller.onViewCreated(TestLifecycleOwner())
}
@Test
fun update() {
controller.update(CycleChartDate)
verify(preference).setTime(START_TIME, END_TIME)
verify(preference).setNetworkCycleData(CycleChartDate)
}
private companion object {
const val KEY = "test_key"
const val START_TIME = 1L
const val END_TIME = 2L
val UsageData = NetworkUsageData(startTime = START_TIME, endTime = END_TIME, usage = 10)
val CycleChartDate =
NetworkCycleChartData(total = UsageData, dailyUsage = listOf(UsageData))
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.settings.datausage
import android.net.NetworkPolicy
import android.telephony.SubscriptionPlan
import android.util.Range
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.INetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settings.testutils.zonedDateTime
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class DataPlanRepositoryTest {
private object FakeNetworkCycleDataRepository : INetworkCycleDataRepository {
override fun getCycles(): List<Range<Long>> = emptyList()
override fun getPolicy() = null
override fun queryUsage(range: Range<Long>) = NetworkUsageData(
startTime = CYCLE_CYCLE_START_TIME,
endTime = CYCLE_CYCLE_END_TIME,
usage = CYCLE_BYTES,
)
}
private val repository = DataPlanRepositoryImpl(FakeNetworkCycleDataRepository)
private val policy = mock<NetworkPolicy> {
on { cycleIterator() } doReturn listOf(
Range(zonedDateTime(CYCLE_CYCLE_START_TIME), zonedDateTime(CYCLE_CYCLE_END_TIME)),
).iterator()
}
@Test
fun getDataPlanInfo_hasSubscriptionPlan() {
val dataPlanInfo = repository.getDataPlanInfo(policy, listOf(SUBSCRIPTION_PLAN))
assertThat(dataPlanInfo).isEqualTo(
DataPlanInfo(
dataPlanCount = 1,
dataPlanSize = DATA_LIMIT_BYTES,
dataBarSize = DATA_LIMIT_BYTES,
dataPlanUse = DATA_USAGE_BYTES,
cycleEnd = PLAN_CYCLE_END_TIME,
snapshotTime = DATA_USAGE_TIME,
)
)
}
@Test
fun getDataPlanInfo_noSubscriptionPlan() {
val dataPlanInfo = repository.getDataPlanInfo(policy, emptyList())
assertThat(dataPlanInfo).isEqualTo(
DataPlanInfo(
dataPlanCount = 0,
dataPlanSize = SubscriptionPlan.BYTES_UNKNOWN,
dataBarSize = CYCLE_BYTES,
dataPlanUse = CYCLE_BYTES,
cycleEnd = CYCLE_CYCLE_END_TIME,
snapshotTime = SubscriptionPlan.TIME_UNKNOWN,
)
)
}
private companion object {
const val CYCLE_CYCLE_START_TIME = 1L
const val CYCLE_CYCLE_END_TIME = 2L
const val CYCLE_BYTES = 11L
const val PLAN_CYCLE_START_TIME = 100L
const val PLAN_CYCLE_END_TIME = 200L
const val DATA_LIMIT_BYTES = 300L
const val DATA_USAGE_BYTES = 400L
const val DATA_USAGE_TIME = 500L
val SUBSCRIPTION_PLAN: SubscriptionPlan = SubscriptionPlan.Builder.createNonrecurring(
zonedDateTime(PLAN_CYCLE_START_TIME),
zonedDateTime(PLAN_CYCLE_END_TIME),
).apply {
setDataLimit(DATA_LIMIT_BYTES, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
setDataUsage(DATA_USAGE_BYTES, DATA_USAGE_TIME)
}.build()
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.settings.datausage
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.DataUsageFormatter.getBytesDisplayUnit
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class DataUsageFormatterTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun getUnitDisplayName_megaByte() {
val displayName = context.resources.getBytesDisplayUnit(ONE_MEGA_BYTE_IN_BYTES)
assertThat(displayName).isEqualTo("MB")
}
@Test
fun getUnitDisplayName_gigaByte() {
val displayName = context.resources.getBytesDisplayUnit(ONE_GIGA_BYTE_IN_BYTES)
assertThat(displayName).isEqualTo("GB")
}
private companion object {
const val ONE_MEGA_BYTE_IN_BYTES = 1024L * 1024
const val ONE_GIGA_BYTE_IN_BYTES = 1024L * 1024 * 1024
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage
import android.content.Context
import android.content.Intent
import android.net.NetworkTemplate
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.SettingsActivity
import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settingslib.AppItem
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class DataUsageListAppsControllerTest {
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val controller = DataUsageListAppsController(context, "test_key")
@Before
fun setUp() {
controller.init(mock<NetworkTemplate>())
val data = NetworkUsageData(START_TIME, END_TIME, 0)
controller.updateCycles(listOf(data))
}
@Test
fun startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
controller.startAppDataUsage(AppItem(), END_TIME)
val intent = argumentCaptor<Intent> {
verify(context).startActivity(capture())
}.firstValue
val arguments = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(END_TIME)
assertThat(
arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES, ArrayList::class.java)
).containsExactly(END_TIME, START_TIME).inOrder()
}
private companion object {
const val START_TIME = 1521583200000L
const val END_TIME = 1521676800000L
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.settings.datausage
import android.content.Context
import android.net.NetworkTemplate
import android.view.LayoutInflater
import android.view.View
import android.widget.Spinner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class DataUsageListHeaderControllerTest {
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val header =
LayoutInflater.from(context).inflate(R.layout.apps_filter_spinner, null, false)
private val configureButton: View = header.requireViewById(R.id.filter_settings)
private val spinner: Spinner = header.requireViewById(R.id.filter_spinner)
private val testLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.CREATED)
private val controller = DataUsageListHeaderController(
header = header,
template = mock<NetworkTemplate>(),
sourceMetricsCategory = 0,
viewLifecycleOwner = testLifecycleOwner,
cyclesFlow = flowOf(emptyList()),
updateSelectedCycle = {},
)
@Test
fun onViewCreated_shouldHideCycleSpinner() {
assertThat(spinner.visibility).isEqualTo(View.GONE)
}
@Test
fun updateCycleData_shouldShowCycleSpinner() = runBlocking {
testLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
delay(100)
assertThat(spinner.visibility).isEqualTo(View.VISIBLE)
}
@Test
fun setConfigButtonVisible_setToTrue_shouldShowConfigureButton() {
controller.setConfigButtonVisible(true)
assertThat(configureButton.visibility).isEqualTo(View.VISIBLE)
}
@Test
fun setConfigButtonVisible_setToFalse_shouldHideConfigureButton() {
controller.setConfigButtonVisible(false)
assertThat(configureButton.visibility).isEqualTo(View.GONE)
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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.settings.datausage
import android.content.Context
import android.content.Intent
import android.net.NetworkTemplate
import android.os.UserManager
import android.provider.Settings
import android.telephony.SubscriptionManager
import androidx.core.os.bundleOf
import androidx.fragment.app.testing.launchFragment
import androidx.fragment.app.testing.withFragment
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
import org.junit.Before
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.stub
private val mockUserManager: UserManager = mock<UserManager>()
private val mockContext: Context = spy(ApplicationProvider.getApplicationContext()) {
on { userManager } doReturn mockUserManager
}
private var fakeIntent = Intent()
@RunWith(AndroidJUnit4::class)
class DataUsageListTest {
@Before
fun setUp() {
mockUserManager.stub {
on { isGuestUser } doReturn false
}
fakeIntent = Intent()
}
@Test
fun launchFragment_withoutArguments_finish() {
val scenario = launchFragment<TestDataUsageList>(initialState = Lifecycle.State.CREATED)
scenario.withFragment {
assertThat(template).isNull()
assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
assertThat(activity!!.isFinishing).isTrue()
}
}
@Test
fun launchFragment_isGuestUser_finish() {
mockUserManager.stub {
on { isGuestUser } doReturn true
}
val fragmentArgs = bundleOf(
DataUsageList.EXTRA_NETWORK_TEMPLATE to mock<NetworkTemplate>(),
DataUsageList.EXTRA_SUB_ID to 3,
)
val scenario = launchFragment<TestDataUsageList>(
fragmentArgs = fragmentArgs,
initialState = Lifecycle.State.CREATED,
)
scenario.withFragment {
assertThat(activity!!.isFinishing).isTrue()
}
}
@Test
fun launchFragment_withArguments_getTemplateFromArgument() {
val fragmentArgs = bundleOf(
DataUsageList.EXTRA_NETWORK_TEMPLATE to mock<NetworkTemplate>(),
DataUsageList.EXTRA_SUB_ID to 3,
)
val scenario = launchFragment<TestDataUsageList>(
fragmentArgs = fragmentArgs,
initialState = Lifecycle.State.CREATED,
)
scenario.withFragment {
assertThat(template).isNotNull()
assertThat(subId).isEqualTo(3)
assertThat(activity!!.isFinishing).isFalse()
}
}
@Test
fun launchFragment_withIntent_getTemplateFromIntent() {
fakeIntent = Intent().apply {
putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mock<NetworkTemplate>())
putExtra(Settings.EXTRA_SUB_ID, 2)
}
val scenario = launchFragment<TestDataUsageList>(initialState = Lifecycle.State.CREATED)
scenario.withFragment {
assertThat(template).isNotNull()
assertThat(subId).isEqualTo(2)
assertThat(activity!!.isFinishing).isFalse()
}
}
}
class TestDataUsageList : DataUsageList() {
override fun getContext() = mockContext
override fun getIntent() = fakeIntent
}

View File

@@ -0,0 +1,274 @@
/*
* 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.settings.datausage
import android.content.Context
import android.net.NetworkPolicy
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionPlan
import android.telephony.TelephonyManager
import android.util.Range
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
import com.android.settings.datausage.lib.INetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settings.network.ProxySubscriptionManager
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class DataUsageSummaryPreferenceControllerTest {
private var policy: NetworkPolicy? = mock<NetworkPolicy>()
private val mockTelephonyManager = mock<TelephonyManager> {
on { isDataCapable } doReturn true
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { getSubscriptionPlans(any()) } doReturn emptyList()
}
private val mockProxySubscriptionManager = mock<ProxySubscriptionManager> {
on { get() } doReturn mockSubscriptionManager
}
private val fakeNetworkCycleDataRepository = object : INetworkCycleDataRepository {
override fun getCycles(): List<Range<Long>> = emptyList()
override fun getPolicy() = policy
override fun queryUsage(range: Range<Long>) = NetworkUsageData.AllZero
}
private var dataPlanInfo = EMPTY_DATA_PLAN_INFO
private val fakeDataPlanRepository = object : DataPlanRepository {
override fun getDataPlanInfo(policy: NetworkPolicy, plans: List<SubscriptionPlan>) =
dataPlanInfo
}
private val controller = DataUsageSummaryPreferenceController(
context = context,
subId = SUB_ID,
proxySubscriptionManager = mockProxySubscriptionManager,
networkCycleDataRepositoryFactory = { fakeNetworkCycleDataRepository },
dataPlanRepositoryFactory = { fakeDataPlanRepository },
)
private val preference = mock<DataUsageSummaryPreference> {
on { key } doReturn controller.preferenceKey
}
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
}
@Test
fun getAvailabilityStatus_noMobileData_conditionallyUnavailable() {
mockTelephonyManager.stub {
on { isDataCapable } doReturn false
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_hasSubInfoAndPolicy_available() {
mockProxySubscriptionManager.stub {
on { getAccessibleSubscriptionInfo(SUB_ID) } doReturn SubscriptionInfo.Builder().build()
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
assertThat(availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_noSubInfo_conditionallyUnavailable() {
mockProxySubscriptionManager.stub {
on { getAccessibleSubscriptionInfo(SUB_ID) } doReturn null
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_noPolicy_conditionallyUnavailable() {
policy = null
val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun displayPreference_policyHasNoLimitInfo() {
policy = mock<NetworkPolicy>().apply {
warningBytes = NetworkPolicy.WARNING_DISABLED
limitBytes = NetworkPolicy.LIMIT_DISABLED
}
controller.displayPreference(preferenceScreen)
verify(preference).setLimitInfo(null)
verify(preference, never()).setLabels(any(), any())
}
@Test
fun displayPreference_policyWarningOnly() {
policy = mock<NetworkPolicy>().apply {
warningBytes = 1L
limitBytes = NetworkPolicy.LIMIT_DISABLED
}
controller.displayPreference(preferenceScreen)
val limitInfo = argumentCaptor {
verify(preference).setLimitInfo(capture())
}.firstValue.toString()
assertThat(limitInfo).isEqualTo("1 B data warning")
verify(preference).setLabels("0 B", "1 B")
}
@Test
fun displayPreference_policyLimitOnly() {
policy = mock<NetworkPolicy>().apply {
warningBytes = NetworkPolicy.WARNING_DISABLED
limitBytes = 1L
}
controller.displayPreference(preferenceScreen)
val limitInfo = argumentCaptor {
verify(preference).setLimitInfo(capture())
}.firstValue.toString()
assertThat(limitInfo).isEqualTo("1 B data limit")
verify(preference).setLabels("0 B", "1 B")
}
@Test
fun displayPreference_policyHasWarningAndLimit() {
policy = mock<NetworkPolicy>().apply {
warningBytes = BillingCycleSettings.GIB_IN_BYTES / 2
limitBytes = BillingCycleSettings.GIB_IN_BYTES
}
controller.displayPreference(preferenceScreen)
val limitInfo = argumentCaptor {
verify(preference).setLimitInfo(capture())
}.firstValue.toString()
assertThat(limitInfo).isEqualTo("512 MB data warning / 1.00 GB data limit")
verify(preference).setLabels("0 B", "1.00 GB")
}
@Test
fun onViewCreated_emptyDataPlanInfo() = runBlocking {
dataPlanInfo = EMPTY_DATA_PLAN_INFO
controller.displayPreference(preferenceScreen)
clearInvocations(preference)
controller.onViewCreated(TestLifecycleOwner())
delay(100)
verify(preference).setUsageNumbers(
EMPTY_DATA_PLAN_INFO.dataPlanUse,
EMPTY_DATA_PLAN_INFO.dataPlanSize,
)
verify(preference).setChartEnabled(false)
verify(preference).setUsageInfo(
EMPTY_DATA_PLAN_INFO.cycleEnd,
EMPTY_DATA_PLAN_INFO.snapshotTime,
null,
EMPTY_DATA_PLAN_INFO.dataPlanCount,
)
}
@Test
fun onViewCreated_positiveDataPlanInfo() = runBlocking {
dataPlanInfo = POSITIVE_DATA_PLAN_INFO
controller.displayPreference(preferenceScreen)
clearInvocations(preference)
controller.onViewCreated(TestLifecycleOwner())
delay(100)
verify(preference).setUsageNumbers(
POSITIVE_DATA_PLAN_INFO.dataPlanUse,
POSITIVE_DATA_PLAN_INFO.dataPlanSize,
)
verify(preference).setChartEnabled(true)
verify(preference).setLabels("0 B", "9 B")
val progress = argumentCaptor {
verify(preference).setProgress(capture())
}.firstValue
assertThat(progress).isEqualTo(0.8888889f)
verify(preference).setUsageInfo(
POSITIVE_DATA_PLAN_INFO.cycleEnd,
POSITIVE_DATA_PLAN_INFO.snapshotTime,
null,
POSITIVE_DATA_PLAN_INFO.dataPlanCount,
)
}
private companion object {
const val SUB_ID = 1234
val EMPTY_DATA_PLAN_INFO = DataPlanInfo(
dataPlanCount = 0,
dataPlanSize = SubscriptionPlan.BYTES_UNKNOWN,
dataBarSize = SubscriptionPlan.BYTES_UNKNOWN,
dataPlanUse = 0,
cycleEnd = null,
snapshotTime = SubscriptionPlan.TIME_UNKNOWN,
)
val POSITIVE_DATA_PLAN_INFO = DataPlanInfo(
dataPlanCount = 0,
dataPlanSize = 10L,
dataBarSize = 9L,
dataPlanUse = 8L,
cycleEnd = 7L,
snapshotTime = 6L,
)
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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.settings.datausage.lib
import android.app.usage.NetworkStats
import android.content.Context
import android.net.NetworkTemplate
import android.util.Range
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.Bucket
@RunWith(AndroidJUnit4::class)
class AppDataUsageDetailsRepositoryTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val template = mock<NetworkTemplate>()
private val networkCycleDataRepository = mock<INetworkCycleDataRepository> {
on { getCycles() } doReturn listOf(Range(CYCLE1_END_TIME, CYCLE2_END_TIME))
}
private val networkStatsRepository = mock<NetworkStatsRepository>()
@Test
fun queryDetailsForCycles_hasCycles(): Unit = runBlocking {
networkStatsRepository.stub {
on { queryBuckets(CYCLE1_START_TIME, CYCLE1_END_TIME) } doReturn listOf(
Bucket(
uid = UID,
bytes = BACKGROUND_USAGE,
state = NetworkStats.Bucket.STATE_DEFAULT,
startTimeStamp = 0L,
endTimeStamp = 0L,
),
Bucket(
uid = UID,
bytes = FOREGROUND_USAGE,
state = NetworkStats.Bucket.STATE_FOREGROUND,
startTimeStamp = 0L,
endTimeStamp = 0L,
),
)
}
val repository = AppDataUsageDetailsRepository(
context = context,
cycles = listOf(CYCLE1_END_TIME, CYCLE1_START_TIME),
template = template,
uids = listOf(UID),
networkCycleDataRepository = networkCycleDataRepository,
networkStatsRepository = networkStatsRepository,
)
val detailsForCycles = repository.queryDetailsForCycles()
assertThat(detailsForCycles).containsExactly(
NetworkUsageDetailsData(
range = Range(CYCLE1_START_TIME, CYCLE1_END_TIME),
totalUsage = BACKGROUND_USAGE + FOREGROUND_USAGE,
foregroundUsage = FOREGROUND_USAGE,
backgroundUsage = BACKGROUND_USAGE,
)
)
}
@Test
fun queryDetailsForCycles_defaultCycles(): Unit = runBlocking {
networkStatsRepository.stub {
on { queryBuckets(CYCLE1_END_TIME, CYCLE2_END_TIME) } doReturn listOf(
Bucket(
uid = UID,
bytes = BACKGROUND_USAGE,
state = NetworkStats.Bucket.STATE_DEFAULT,
startTimeStamp = 0L,
endTimeStamp = 0L,
),
Bucket(
uid = UID,
bytes = FOREGROUND_USAGE,
state = NetworkStats.Bucket.STATE_FOREGROUND,
startTimeStamp = 0L,
endTimeStamp = 0L,
),
)
}
val repository = AppDataUsageDetailsRepository(
context = context,
cycles = null,
template = template,
uids = listOf(UID),
networkCycleDataRepository = networkCycleDataRepository,
networkStatsRepository = networkStatsRepository,
)
val detailsForCycles = repository.queryDetailsForCycles()
assertThat(detailsForCycles).containsExactly(
NetworkUsageDetailsData(
range = Range(CYCLE1_END_TIME, CYCLE2_END_TIME),
totalUsage = BACKGROUND_USAGE + FOREGROUND_USAGE,
foregroundUsage = FOREGROUND_USAGE,
backgroundUsage = BACKGROUND_USAGE,
)
)
}
private companion object {
const val CYCLE1_START_TIME = 1694444444000L
const val CYCLE1_END_TIME = 1695555555000L
const val CYCLE2_END_TIME = 1695566666000L
const val UID = 10000
const val BACKGROUND_USAGE = 8L
const val FOREGROUND_USAGE = 2L
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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.settings.datausage.lib
import android.content.Context
import android.content.pm.UserInfo
import android.content.res.Resources
import android.net.NetworkPolicyManager
import android.net.NetworkTemplate
import android.os.UserHandle
import android.os.UserManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.Bucket
import com.android.settingslib.AppItem
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.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
@RunWith(AndroidJUnit4::class)
class AppDataUsageRepositoryTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
private val mockUserManager = mock<UserManager> {
on { userProfiles } doReturn listOf(UserHandle.of(USER_ID))
on { getUserInfo(USER_ID) } doReturn UserInfo(USER_ID, "", 0)
}
private val mockNetworkPolicyManager = mock<NetworkPolicyManager> {
on { getUidsWithPolicy(NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) } doReturn
intArrayOf()
}
private val mockResources = mock<Resources> {
on { getIntArray(R.array.datausage_hiding_carrier_service_carrier_id) } doReturn
intArrayOf(HIDING_CARRIER_ID)
on { getStringArray(R.array.datausage_hiding_carrier_service_package_names) } doReturn
arrayOf(HIDING_PACKAGE_NAME)
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { userManager } doReturn mockUserManager
on { getSystemService(NetworkPolicyManager::class.java) } doReturn mockNetworkPolicyManager
on { resources } doReturn mockResources
}
@Test
fun getAppPercent_noAppToHide() {
val repository = AppDataUsageRepository(
context = context,
currentUserId = USER_ID,
template = Template,
getPackageName = { null },
)
val buckets = listOf(
Bucket(uid = APP_ID_1, bytes = 1, startTimeStamp = 0, endTimeStamp = 0),
Bucket(uid = APP_ID_2, bytes = 2, startTimeStamp = 0, endTimeStamp = 0),
)
val appPercentList = repository.getAppPercent(null, buckets)
assertThat(appPercentList).hasSize(2)
appPercentList[0].first.apply {
assertThat(key).isEqualTo(APP_ID_2)
assertThat(category).isEqualTo(AppItem.CATEGORY_APP)
assertThat(total).isEqualTo(2)
}
assertThat(appPercentList[0].second).isEqualTo(100)
appPercentList[1].first.apply {
assertThat(key).isEqualTo(APP_ID_1)
assertThat(category).isEqualTo(AppItem.CATEGORY_APP)
assertThat(total).isEqualTo(1)
}
assertThat(appPercentList[1].second).isEqualTo(50)
}
@Test
fun getAppPercent_hasAppToHide() {
val repository = AppDataUsageRepository(
context = context,
currentUserId = USER_ID,
template = Template,
getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else null },
)
val buckets = listOf(
Bucket(uid = APP_ID_1, bytes = 1, startTimeStamp = 0, endTimeStamp = 0),
Bucket(uid = APP_ID_2, bytes = 2, startTimeStamp = 0, endTimeStamp = 0),
)
val appPercentList = repository.getAppPercent(HIDING_CARRIER_ID, buckets)
assertThat(appPercentList).hasSize(1)
appPercentList[0].first.apply {
assertThat(key).isEqualTo(APP_ID_2)
assertThat(category).isEqualTo(AppItem.CATEGORY_APP)
assertThat(total).isEqualTo(2)
}
assertThat(appPercentList[0].second).isEqualTo(100)
}
private companion object {
const val USER_ID = 1
const val APP_ID_1 = 110001
const val APP_ID_2 = 110002
const val HIDING_CARRIER_ID = 4
const val HIDING_PACKAGE_NAME = "hiding.package.name"
val Template: NetworkTemplate = mock<NetworkTemplate>()
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage.lib
import android.content.Context
import android.net.NetworkTemplate
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class AppDataUsageSummaryRepositoryTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val template = mock<NetworkTemplate>()
private val networkStatsRepository = mock<NetworkStatsRepository> {
on {
queryAggregateForUid(range = NetworkStatsRepository.AllTimeRange, uid = APP_UID)
} doReturn NetworkUsageData(APP_START_TIME, APP_END_TIME, APP_USAGE)
on {
queryAggregateForUid(range = NetworkStatsRepository.AllTimeRange, uid = SDK_SANDBOX_UID)
} doReturn NetworkUsageData(SDK_SANDBOX_START_TIME, SDK_SANDBOX_END_TIME, SDK_SANDBOX_USAGE)
}
private val repository =
AppDataUsageSummaryRepository(context, template, networkStatsRepository)
@Test
fun querySummary(): Unit = runBlocking {
val networkUsageData = repository.querySummary(APP_UID)
assertThat(networkUsageData).isEqualTo(
NetworkUsageData(
startTime = APP_START_TIME,
endTime = SDK_SANDBOX_END_TIME,
usage = APP_USAGE + SDK_SANDBOX_USAGE,
)
)
}
private companion object {
const val APP_UID = 10000
const val APP_START_TIME = 10L
const val APP_END_TIME = 30L
const val APP_USAGE = 3L
const val SDK_SANDBOX_UID = 20000
const val SDK_SANDBOX_START_TIME = 20L
const val SDK_SANDBOX_END_TIME = 40L
const val SDK_SANDBOX_USAGE = 5L
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage.lib
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.UserHandle
import android.util.IconDrawableFactory
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class AppPreferenceRepositoryTest {
private val packageManager = mock<PackageManager> {
on { getPackagesForUid(UID) } doReturn arrayOf(PACKAGE_NAME)
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn packageManager
}
private val mockIconDrawableFactory = mock<IconDrawableFactory>()
private val repository = AppPreferenceRepository(context, mockIconDrawableFactory)
@Test
fun loadAppPreferences_packageNotFound_returnEmpty() {
packageManager.stub {
on {
getApplicationInfoAsUser(PACKAGE_NAME, 0, UserHandle.getUserId(UID))
} doThrow PackageManager.NameNotFoundException()
}
val preferences = repository.loadAppPreferences(listOf(UID))
assertThat(preferences).isEmpty()
}
@Test
fun loadAppPreferences_packageFound_returnPreference() {
val app = mock<ApplicationInfo> {
on { loadLabel(any()) } doReturn LABEL
}
mockIconDrawableFactory.stub {
on { getBadgedIcon(app) } doReturn UNBADGED_ICON
}
packageManager.stub {
on {
getApplicationInfoAsUser(PACKAGE_NAME, 0, UserHandle.getUserId(UID))
} doReturn app
}
val preferences = repository.loadAppPreferences(listOf(UID))
assertThat(preferences).hasSize(1)
preferences[0].apply {
assertThat(title).isEqualTo(LABEL)
assertThat(icon).isNotNull()
assertThat(isSelectable).isFalse()
}
}
private companion object {
const val UID = 10000
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
val UNBADGED_ICON = mock<Drawable>()
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.settings.datausage.lib
import android.content.Context
import android.os.INetworkManagementService
import android.os.UserManager
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
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 BillingCycleRepositoryTest {
private val mockNetworkManagementService = mock<INetworkManagementService> {
on { isBandwidthControlEnabled } doReturn true
}
private val mockUserManager = mock<UserManager> {
on { isAdminUser } doReturn true
}
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) } doReturn false
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { userManager } doReturn mockUserManager
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
private val repository = BillingCycleRepository(context, mockNetworkManagementService)
@Test
fun isModifiable_bandwidthControlDisabled_returnFalse() {
whenever(mockNetworkManagementService.isBandwidthControlEnabled).thenReturn(false)
val modifiable = repository.isModifiable(SUB_ID)
assertThat(modifiable).isFalse()
}
@Test
fun isModifiable_notAdminUser_returnFalse() {
whenever(mockUserManager.isAdminUser).thenReturn(false)
val modifiable = repository.isModifiable(SUB_ID)
assertThat(modifiable).isFalse()
}
@Test
fun isModifiable_dataDisabled_returnFalse() {
whenever(
mockTelephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
).thenReturn(false)
val modifiable = repository.isModifiable(SUB_ID)
assertThat(modifiable).isFalse()
}
@Test
fun isModifiable_meetAllRequirements_returnTrue() {
whenever(mockNetworkManagementService.isBandwidthControlEnabled).thenReturn(true)
whenever(mockUserManager.isAdminUser).thenReturn(true)
whenever(
mockTelephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
).thenReturn(true)
val modifiable = repository.isModifiable(SUB_ID)
assertThat(modifiable).isTrue()
}
@Test
fun isBandwidthControlEnabled_bandwidthControlDisabled_returnFalse() {
whenever(mockNetworkManagementService.isBandwidthControlEnabled).thenReturn(false)
val enabled = repository.isBandwidthControlEnabled()
assertThat(enabled).isFalse()
}
@Test
fun isBandwidthControlEnabled_bandwidthControlEnabled_returnTrue() {
whenever(mockNetworkManagementService.isBandwidthControlEnabled).thenReturn(true)
val enabled = repository.isBandwidthControlEnabled()
assertThat(enabled).isTrue()
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.settings.datausage.lib
import android.content.Context
import android.net.NetworkStats
import android.net.NetworkTemplate
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
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 DataUsageLibTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var telephonyManager: TelephonyManager
@Mock
private lateinit var subscriptionManager: SubscriptionManager
@Before
fun setUp() {
whenever(context.getSystemService(TelephonyManager::class.java))
.thenReturn(telephonyManager)
whenever(context.getSystemService(SubscriptionManager::class.java))
.thenReturn(subscriptionManager)
whenever(telephonyManager.subscriptionId).thenReturn(DEFAULT_SUB_ID)
whenever(telephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID)
whenever(telephonyManager.getSubscriberId(DEFAULT_SUB_ID)).thenReturn(DEFAULT_SUBSCRIBER_ID)
whenever(telephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(telephonyManager)
}
@Test
fun getMobileTemplate_availableSubscriptionInfoListIsNull_returnDefaultSub() {
whenever(subscriptionManager.availableSubscriptionInfoList).thenReturn(null)
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(DEFAULT_SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_subscriptionNotActive_returnDefaultSub() {
whenever(subscriptionManager.availableSubscriptionInfoList).thenReturn(listOf(null))
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(DEFAULT_SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupEmpty_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup).thenReturn(emptyArray())
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupNotContainSub_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup).thenReturn(arrayOf(DEFAULT_SUBSCRIBER_ID))
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplate_mergedImsisFromGroupContainSub_returnRequestedSub() {
whenever(subscriptionManager.availableSubscriptionInfoList)
.thenReturn(listOf(SUBSCRIBER_INFO))
whenever(telephonyManager.mergedImsisFromGroup)
.thenReturn(arrayOf(DEFAULT_SUBSCRIBER_ID, SUBSCRIBER_ID))
val mobileTemplate = DataUsageLib.getMobileTemplate(context, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds)
.containsExactly(SUBSCRIBER_ID, DEFAULT_SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplateForSubId_subscriberIdNotNull() {
whenever(telephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID)
val mobileTemplate = DataUsageLib.getMobileTemplateForSubId(telephonyManager, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_CARRIER)
assertThat(mobileTemplate.subscriberIds).containsExactly(SUBSCRIBER_ID)
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
@Test
fun getMobileTemplateForSubId_subscriberIdIsNull() {
whenever(telephonyManager.getSubscriberId(SUB_ID)).thenReturn(null)
val mobileTemplate = DataUsageLib.getMobileTemplateForSubId(telephonyManager, SUB_ID)
assertThat(mobileTemplate.matchRule).isEqualTo(NetworkTemplate.MATCH_MOBILE)
assertThat(mobileTemplate.subscriberIds).isEmpty()
assertThat(mobileTemplate.meteredness).isEqualTo(NetworkStats.METERED_YES)
}
private companion object {
const val DEFAULT_SUB_ID = 0
const val SUB_ID = 1
const val DEFAULT_SUBSCRIBER_ID = "Default Test Subscriber"
const val SUBSCRIBER_ID = "Test Subscriber"
val SUBSCRIBER_INFO: SubscriptionInfo = SubscriptionInfo.Builder().setId(SUB_ID).build()
}
}

View File

@@ -0,0 +1,178 @@
/*
* 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.settings.datausage.lib
import android.content.Context
import android.net.NetworkPolicy
import android.net.NetworkTemplate
import android.text.format.DateUtils
import android.util.Range
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.Bucket
import com.google.common.truth.Truth.assertThat
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class NetworkCycleBucketRepositoryTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val template = mock<NetworkTemplate>()
private val mockNetworkCycleDataRepository = mock<NetworkCycleDataRepository>()
@Test
fun loadCycles_byPolicy() {
val policy = mock<NetworkPolicy> {
on { cycleIterator() } doReturn listOf(
Range(zonedDateTime(CYCLE1_START_TIME), zonedDateTime(CYCLE1_END_TIME)),
).iterator()
}
mockNetworkCycleDataRepository.stub {
on { getPolicy() } doReturn policy
}
val repository = NetworkCycleBucketRepository(
context = context,
networkTemplate = template,
buckets = listOf(
Bucket(
uid = 0,
bytes = CYCLE1_BYTES,
startTimeStamp = CYCLE1_START_TIME,
endTimeStamp = CYCLE1_END_TIME,
)
),
networkCycleDataRepository = mockNetworkCycleDataRepository,
)
val cycles = repository.loadCycles()
assertThat(cycles).containsExactly(
NetworkUsageData(
startTime = CYCLE1_START_TIME,
endTime = CYCLE1_END_TIME,
usage = CYCLE1_BYTES,
),
)
}
@Test
fun loadCycles_asFourWeeks() {
mockNetworkCycleDataRepository.stub {
on { getPolicy() } doReturn null
}
val repository = NetworkCycleBucketRepository(
context = context,
networkTemplate = template,
buckets = listOf(
Bucket(
uid = 0,
bytes = CYCLE2_BYTES,
startTimeStamp = CYCLE2_START_TIME,
endTimeStamp = CYCLE2_END_TIME,
)
),
networkCycleDataRepository = mockNetworkCycleDataRepository,
)
val cycles = repository.loadCycles()
assertThat(cycles).containsExactly(
NetworkUsageData(
startTime = CYCLE2_END_TIME - DateUtils.WEEK_IN_MILLIS * 4,
endTime = CYCLE2_END_TIME,
usage = CYCLE2_BYTES,
),
)
}
@Test
fun queryChartData() {
val cycle = NetworkUsageData(
startTime = CYCLE3_START_TIME,
endTime = CYCLE4_END_TIME,
usage = CYCLE3_BYTES + CYCLE4_BYTES,
)
val repository = NetworkCycleBucketRepository(
context = context,
networkTemplate = template,
buckets = listOf(
Bucket(
uid = 0,
bytes = CYCLE3_BYTES,
startTimeStamp = CYCLE3_START_TIME,
endTimeStamp = CYCLE3_END_TIME,
),
Bucket(
uid = 0,
bytes = CYCLE4_BYTES,
startTimeStamp = CYCLE4_START_TIME,
endTimeStamp = CYCLE4_END_TIME,
),
),
networkCycleDataRepository = mockNetworkCycleDataRepository,
)
val summary = repository.queryChartData(cycle)
assertThat(summary).isEqualTo(
NetworkCycleChartData(
total = cycle,
dailyUsage = listOf(
NetworkUsageData(
startTime = CYCLE3_START_TIME,
endTime = CYCLE3_END_TIME,
usage = CYCLE3_BYTES,
),
NetworkUsageData(
startTime = CYCLE4_START_TIME,
endTime = CYCLE4_END_TIME,
usage = CYCLE4_BYTES,
),
),
)
)
}
private fun zonedDateTime(epochMilli: Long): ZonedDateTime? =
ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault())
private companion object {
const val CYCLE1_START_TIME = 1L
const val CYCLE1_END_TIME = 2L
const val CYCLE1_BYTES = 11L
const val CYCLE2_START_TIME = 1695555555000L
const val CYCLE2_END_TIME = 1695566666000L
const val CYCLE2_BYTES = 22L
const val CYCLE3_START_TIME = 1695555555000L
const val CYCLE3_END_TIME = CYCLE3_START_TIME + DateUtils.DAY_IN_MILLIS
const val CYCLE3_BYTES = 33L
const val CYCLE4_START_TIME = CYCLE3_END_TIME
const val CYCLE4_END_TIME = CYCLE4_START_TIME + DateUtils.DAY_IN_MILLIS
const val CYCLE4_BYTES = 44L
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.settings.datausage.lib
import android.content.Context
import android.net.NetworkPolicy
import android.net.NetworkTemplate
import android.text.format.DateUtils
import android.util.Range
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.testutils.zonedDateTime
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
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.stub
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class NetworkCycleDataRepositoryTest {
private val mockNetworkStatsRepository = mock<NetworkStatsRepository> {
on { querySummaryForDevice(CYCLE1_START_TIME, CYCLE1_END_TIME) } doReturn CYCLE1_BYTES
on {
querySummaryForDevice(
startTime = CYCLE2_END_TIME - DateUtils.WEEK_IN_MILLIS * 4,
endTime = CYCLE2_END_TIME,
)
} doReturn CYCLE2_BYTES
on { querySummaryForDevice(CYCLE3_START_TIME, CYCLE4_END_TIME) } doReturn
CYCLE3_BYTES + CYCLE4_BYTES
on { querySummaryForDevice(CYCLE3_START_TIME, CYCLE3_END_TIME) } doReturn CYCLE3_BYTES
on { querySummaryForDevice(CYCLE4_START_TIME, CYCLE4_END_TIME) } doReturn CYCLE4_BYTES
}
private val context: Context = ApplicationProvider.getApplicationContext()
private val template = mock<NetworkTemplate>()
private val repository =
spy(NetworkCycleDataRepository(context, template, mockNetworkStatsRepository))
@Test
fun loadFirstCycle_byPolicy() = runTest {
val policy = mock<NetworkPolicy> {
on { cycleIterator() } doReturn listOf(
Range(zonedDateTime(CYCLE1_START_TIME), zonedDateTime(CYCLE1_END_TIME))
).iterator()
}
doReturn(policy).whenever(repository).getPolicy()
val firstCycle = repository.loadFirstCycle()
assertThat(firstCycle).isEqualTo(
NetworkUsageData(startTime = 1, endTime = 2, usage = CYCLE1_BYTES),
)
}
@Test
fun loadFirstCycle_asFourWeeks() = runTest {
doReturn(null).whenever(repository).getPolicy()
mockNetworkStatsRepository.stub {
on { getTimeRange() } doReturn Range(CYCLE2_START_TIME, CYCLE2_END_TIME)
}
val firstCycle = repository.loadFirstCycle()
assertThat(firstCycle).isEqualTo(
NetworkUsageData(
startTime = CYCLE2_END_TIME - DateUtils.WEEK_IN_MILLIS * 4,
endTime = CYCLE2_END_TIME,
usage = CYCLE2_BYTES,
),
)
}
private companion object {
const val CYCLE1_START_TIME = 1L
const val CYCLE1_END_TIME = 2L
const val CYCLE1_BYTES = 11L
const val CYCLE2_START_TIME = 1695555555000L
const val CYCLE2_END_TIME = 1695566666000L
const val CYCLE2_BYTES = 22L
const val CYCLE3_START_TIME = 1695555555000L
const val CYCLE3_END_TIME = CYCLE3_START_TIME + DateUtils.DAY_IN_MILLIS
const val CYCLE3_BYTES = 33L
const val CYCLE4_START_TIME = CYCLE3_END_TIME
const val CYCLE4_END_TIME = CYCLE4_START_TIME + DateUtils.DAY_IN_MILLIS
const val CYCLE4_BYTES = 44L
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.settings.datausage.lib
import android.app.usage.NetworkStats
import android.app.usage.NetworkStatsManager
import android.content.Context
import android.net.NetworkTemplate
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
@RunWith(AndroidJUnit4::class)
class NetworkStatsRepositoryTest {
private val template = mock<NetworkTemplate>()
private val mockNetworkStatsManager = mock<NetworkStatsManager> {
on { querySummaryForDevice(template, START_TIME, END_TIME) } doReturn BUCKET
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(NetworkStatsManager::class.java) } doReturn mockNetworkStatsManager
}
private val repository = NetworkStatsRepository(context, template)
@Test
fun querySummaryForDevice() {
val bytes = repository.querySummaryForDevice(START_TIME, END_TIME)
assertThat(bytes).isEqualTo(11)
}
private companion object {
const val START_TIME = 1L
const val END_TIME = 2L
val BUCKET = mock<NetworkStats.Bucket> {
on { rxBytes } doReturn 1
on { txBytes } doReturn 10
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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.settings.development
import android.content.Context
import android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS
import android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
import com.android.settings.development.SensitiveContentProtectionPreferenceController.Companion.SETTING_VALUE_OFF
import com.android.settings.development.SensitiveContentProtectionPreferenceController.Companion.SETTING_VALUE_ON
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class SensitiveContentProtectionPreferenceControllerTest {
@get:Rule
val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@get:Rule
val mocks = MockitoJUnit.rule()
@Mock
private lateinit var preference: SwitchPreference
@Mock
private lateinit var screen: PreferenceScreen
private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
private lateinit var controller: SensitiveContentProtectionPreferenceController
@Before
fun setUp() {
controller = SensitiveContentProtectionPreferenceController(context)
whenever(screen.findPreference<Preference>(controller.getPreferenceKey()))
.thenReturn(preference)
controller.displayPreference(screen)
}
@Test
fun onPreferenceChange_settingEnabled_shouldDisableSensitiveContentProtection() {
controller.onPreferenceChange(preference, true /* new value */)
val mode = Settings.Global.getInt(
context.contentResolver,
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
-1 /* default */
)
assertEquals(mode, SETTING_VALUE_ON)
}
@Test
fun onPreferenceChange_settingDisabled_shouldEnableSensitiveContentProtection() {
controller.onPreferenceChange(preference, false /* new value */)
val mode = Settings.Global.getInt(
context.contentResolver,
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
-1 /* default */
)
assertEquals(mode, SETTING_VALUE_OFF)
}
@Test
fun updateState_settingEnabled_preferenceShouldBeChecked() {
Settings.Global.putInt(
context.contentResolver,
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
SETTING_VALUE_ON
)
controller.updateState(preference)
verify(preference).isChecked = true
}
@Test
fun updateState_settingDisabled_preferenceShouldNotBeChecked() {
Settings.Global.putInt(
context.contentResolver,
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
SETTING_VALUE_OFF
)
controller.updateState(preference)
verify(preference).isChecked = false
}
@Test
fun onDeveloperOptionsSwitchDisabled_preferenceShouldBeDisabled() {
controller.onDeveloperOptionsSwitchDisabled()
val mode = Settings.Global.getInt(
context.contentResolver,
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
-1 /* default */
)
assertEquals(mode, SETTING_VALUE_OFF)
verify(preference).isChecked = false
verify(preference).isEnabled = false
}
@Test
@RequiresFlagsDisabled(
FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION,
FLAG_SCREENSHARE_NOTIFICATION_HIDING,
FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
fun isAvailable_flagsDisabled_returnFalse() {
assertFalse(controller.isAvailable)
}
@Test
@RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
fun isAvailable_sensitiveNotificationAppProtectionEnabled_returnTrue() {
assertTrue(controller.isAvailable)
}
@Test
@RequiresFlagsEnabled(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
fun isAvailable_screenshareNotificationHidingEnabled_returnTrue() {
assertTrue(controller.isAvailable)
}
@Test
@RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
fun isAvailable_screenshareSensitiveContentHidingEnabled_returnTrue() {
assertTrue(controller.isAvailable)
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.regulatory
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.SystemProperties
import androidx.annotation.DrawableRes
import androidx.core.graphics.drawable.toBitmap
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.KEY_COO
import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.KEY_SKU
import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.getRegulatoryInfo
import com.android.settings.tests.spa_unit.R
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class RegulatoryInfoTest {
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SystemProperties::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun getRegulatoryInfo_noSkuProperty_shouldReturnDefaultLabel() {
doReturn("").`when` { SystemProperties.get(KEY_SKU) }
val regulatoryInfo = context.getRegulatoryInfo()
assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info)
}
@Test
fun getResourceId_noCooProperty_shouldReturnSkuLabel() {
doReturn("sku").`when` { SystemProperties.get(KEY_SKU) }
doReturn("").`when` { SystemProperties.get(KEY_COO) }
val regulatoryInfo = context.getRegulatoryInfo()
assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku)
}
@Test
fun getResourceId_hasSkuAndCooProperties_shouldReturnCooLabel() {
doReturn("sku1").`when` { SystemProperties.get(KEY_SKU) }
doReturn("coo").`when` { SystemProperties.get(KEY_COO) }
val regulatoryInfo = context.getRegulatoryInfo()
assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku1_coo)
}
@Test
fun getResourceId_noCorrespondingCooLabel_shouldReturnSkuLabel() {
doReturn("sku").`when` { SystemProperties.get(KEY_SKU) }
doReturn("unknown").`when` { SystemProperties.get(KEY_COO) }
val regulatoryInfo = context.getRegulatoryInfo()
assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku)
}
private fun assertDrawableSameAs(drawable: Drawable?, @DrawableRes resId: Int) {
val expected = context.getDrawable(resId)!!.toBitmap()
assertThat(drawable!!.toBitmap().sameAs(expected)).isTrue()
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.simstatus
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionUtil
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class SimEidPreferenceControllerTest {
private lateinit var mockSession: MockitoSession
private val context: Context = ApplicationProvider.getApplicationContext()
private val controller = SimEidPreferenceController(context, TEST_KEY)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SubscriptionUtil::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun getAvailabilityStatus_isSimHardwareVisible() {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.AVAILABLE)
}
@Test
fun getAvailabilityStatus_notSimHardwareVisible() {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE)
}
private companion object {
const val TEST_KEY = "test_key"
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.settings.network
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.res.Resources
import android.net.ConnectivityManager
import android.net.NetworkPolicyManager
import android.net.VpnManager
import android.os.UserManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class BluetoothWiFiResetPreferenceControllerTest {
private val mockUserManager = mock<UserManager>()
private val mockBluetoothAdapter = mock<BluetoothAdapter>()
private val mockBluetoothManager = mock<BluetoothManager> {
on { adapter } doReturn mockBluetoothAdapter
}
private val mockConnectivityManager = mock<ConnectivityManager>()
private val mockNetworkPolicyManager = mock<NetworkPolicyManager>()
private val mockVpnManager = mock<VpnManager>()
private val mockResources = mock<Resources>()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(Context.USER_SERVICE) } doReturn mockUserManager
on { getSystemService(Context.BLUETOOTH_SERVICE) } doReturn mockBluetoothManager
on { getSystemService(Context.CONNECTIVITY_SERVICE) } doReturn mockConnectivityManager
on { getSystemService(Context.NETWORK_POLICY_SERVICE) } doReturn mockNetworkPolicyManager
on { getSystemService(Context.VPN_MANAGEMENT_SERVICE) } doReturn mockVpnManager
on { resources } doReturn mockResources
}
private val controller = BluetoothWiFiResetPreferenceController(context, TEST_KEY)
@Test
fun getAvailabilityStatus_isAdminUser_returnAvailable() {
mockUserManager.stub {
on { isAdminUser } doReturn true
}
val availabilityStatus = controller.getAvailabilityStatus()
assertThat(availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_notAdminUser_returnConditionallyUnavailable() {
mockUserManager.stub {
on { isAdminUser } doReturn false
}
val availabilityStatus = controller.getAvailabilityStatus()
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun resetOperation_resetBluetooth() {
controller.resetOperation().run()
verify(mockBluetoothAdapter).clearBluetooth()
}
@Test
fun resetOperation_onDeviceWithSimVisible_notResetConnectivity() {
mockResources.stub {
on { getBoolean(R.bool.config_show_sim_info) } doReturn true
}
controller.resetOperation().run()
verify(mockConnectivityManager, never()).factoryReset()
}
@Test
fun resetOperation_onDeviceWithSimInvisible_resetVpn() {
mockResources.stub {
on { getBoolean(R.bool.config_show_sim_info) } doReturn false
}
controller.resetOperation().run()
verify(mockVpnManager).factoryReset()
}
private companion object {
const val TEST_KEY = "test_key"
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.settings.network
import android.content.Context
import android.provider.Settings
import android.telephony.SubscriptionManager
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.settingsprovider.settingsGlobalBoolean
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 MobileDataEnabledFlowTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun mobileDataEnabledFlow_notified(): Unit = runBlocking {
val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
}
@Test
fun mobileDataEnabledFlow_changed_notified(): Unit = runBlocking {
var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
mobileDataEnabled = false
val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
mobileDataEnabled = true
assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
}
@Test
fun mobileDataEnabledFlow_forSubIdNotChanged(): Unit = runBlocking {
var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
mobileDataEnabled = false
var mobileDataEnabledForSubId
by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
mobileDataEnabledForSubId = false
val listDeferred = async {
context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout()
}
assertThat(listDeferred.await()).hasSize(1)
}
@Test
fun mobileDataEnabledFlow_forSubIdChanged(): Unit = runBlocking {
var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
mobileDataEnabled = false
var mobileDataEnabledForSubId
by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
mobileDataEnabledForSubId = false
val listDeferred = async {
context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout()
}
delay(100)
mobileDataEnabledForSubId = true
assertThat(listDeferred.await()).hasSize(2)
}
private companion object {
const val SUB_ID = 123
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.settings.network
import android.text.Spanned
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 MobileIconGroupExtTest {
@Test
fun maybeToHtml_withoutHtmlTag() {
val actual = CONNECTED_5G.maybeToHtml()
assertThat(actual).isSameInstanceAs(CONNECTED_5G)
}
@Test
fun maybeToHtml_withHtmlTag() {
val actual = CONNECTED_5GE.maybeToHtml()
assertThat(actual).isInstanceOf(Spanned::class.java)
}
private companion object {
private const val CONNECTED_5G = "Connected / 5G"
private const val CONNECTED_5GE = "Connected / <i>5G <small>E</small></i>"
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network
import android.content.Context
import android.content.res.Resources
import android.os.UserManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
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.stub
@RunWith(AndroidJUnit4::class)
class MobileNetworkListFragmentTest {
private val mockUserManager = mock<UserManager>()
private val mockResources = mock<Resources>()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { userManager } doReturn mockUserManager
on { resources } doReturn mockResources
}
@Test
fun isPageSearchEnabled_adminUser_shouldReturnTrue() {
mockUserManager.stub {
on { isAdminUser } doReturn true
}
mockResources.stub {
on { getBoolean(R.bool.config_show_sim_info) } doReturn true
}
val isEnabled =
MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
assertThat(isEnabled).isTrue()
}
@Test
fun isPageSearchEnabled_nonAdminUser_shouldReturnFalse() {
mockUserManager.stub {
on { isAdminUser } doReturn false
}
mockResources.stub {
on { getBoolean(R.bool.config_show_sim_info) } doReturn true
}
val isEnabled =
MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
assertThat(isEnabled).isFalse()
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NetworkProviderCallsSmsControllerTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private var isInService: (Int) -> Boolean = { true }
private val controller = NetworkProviderCallsSmsController(
context = context,
preferenceKey = TEST_KEY,
getDisplayName = { subInfo -> subInfo.displayName },
isInService = { isInService(it) },
)
@Test
fun getSummary_noSim_returnNoSim() {
val summary = controller.getSummary(
activeSubscriptionInfoList = emptyList(),
defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
)
assertThat(summary).isEqualTo(context.getString(R.string.calls_sms_no_sim))
}
@Test
fun getSummary_invalidSubId_returnUnavailable() {
isInService = { false }
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1),
defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
)
assertThat(summary).isEqualTo("Sub 1 (Temporarily unavailable)")
}
@Test
fun getSummary_oneIsInvalidSubIdTwoIsValidSubId_returnOneIsUnavailable() {
isInService = { it == SUB_INFO_2.subscriptionId }
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2),
defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
)
assertThat(summary).isEqualTo("Sub 1 (unavailable), Sub 2")
}
@Test
fun getSummary_oneSubscription_returnDisplayName() {
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1),
defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
)
assertThat(summary).isEqualTo(DISPLAY_NAME_1)
}
@Test
fun getSummary_allSubscriptionsHaveNoPreferredStatus_returnDisplayName() {
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2),
defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
)
assertThat(summary).isEqualTo("Sub 1, Sub 2")
}
@Test
fun getSummary_oneSubscriptionsIsCallPreferredTwoIsSmsPreferred_returnStatus() {
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2),
defaultVoiceSubscriptionId = SUB_INFO_1.subscriptionId,
defaultSmsSubscriptionId = SUB_INFO_2.subscriptionId,
)
assertThat(summary).isEqualTo("Sub 1 (preferred for calls), Sub 2 (preferred for SMS)")
}
@Test
fun getSummary_oneSubscriptionsIsSmsPreferredTwoIsCallPreferred_returnStatus() {
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2),
defaultVoiceSubscriptionId = SUB_INFO_2.subscriptionId,
defaultSmsSubscriptionId = SUB_INFO_1.subscriptionId,
)
assertThat(summary).isEqualTo("Sub 1 (preferred for SMS), Sub 2 (preferred for calls)")
}
@Test
fun getSummary_oneSubscriptionsIsSmsPreferredAndIsCallPreferred_returnStatus() {
val summary = controller.getSummary(
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2),
defaultVoiceSubscriptionId = SUB_INFO_1.subscriptionId,
defaultSmsSubscriptionId = SUB_INFO_1.subscriptionId,
)
assertThat(summary).isEqualTo("Sub 1 (preferred), Sub 2")
}
private companion object {
const val TEST_KEY = "test_key"
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(1)
setDisplayName(DISPLAY_NAME_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(2)
setDisplayName(DISPLAY_NAME_2)
}.build()
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.settings.network
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import android.app.Application
import android.content.Context
import android.platform.test.flag.junit.SetFlagsRule
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.telephony.flags.Flags
import com.android.settings.network.telephony.CallStateFlowTest
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.Rule
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
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class SubscriptionInfoListViewModelTest {
@get:Rule
val mSetFlagsRule = SetFlagsRule()
private var subInfoListener: SubscriptionManager.OnSubscriptionsChangedListener? = null
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { activeSubscriptionInfoList } doAnswer { activeSubscriptionInfoList }
on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer {
subInfoListener =
it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
subInfoListener?.onSubscriptionsChanged()
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
}
private val subscriptionInfoListViewModel: SubscriptionInfoListViewModel =
SubscriptionInfoListViewModel(context as Application);
private var activeSubscriptionInfoList: List<SubscriptionInfo>? = null
@Test
fun onSubscriptionsChanged_noProvisioning_resultSameAsInput() = runBlocking {
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(activeSubscriptionInfoList)
}
@Test
fun onSubscriptionsChanged_hasProvisioning_filterProvisioning() = runBlocking {
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3)
val expectation = listOf(SUB_INFO_1, SUB_INFO_2)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(expectation)
}
@Test
fun onSubscriptionsChanged_flagOffHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() =
runBlocking {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
val expectation = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(expectation)
}
@Test
fun onSubscriptionsChanged_flagOnHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() =
runBlocking {
mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
val expectation = listOf(SUB_INFO_1, SUB_INFO_2)
val listDeferred = async {
subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).contains(expectation)
}
private companion object {
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(2)
}.build()
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(3)
setEmbedded(true)
setProfileClass(PROFILE_CLASS_PROVISIONING)
setOnlyNonTerrestrialNetwork(false)
}.build()
val SUB_INFO_4: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(4)
setEmbedded(true)
setOnlyNonTerrestrialNetwork(true)
}.build()
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.settings.network
import android.content.Context
import android.net.TetheringManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settingslib.TetherUtil
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class TetherPreferenceControllerTest {
private lateinit var mockSession: MockitoSession
private val context: Context = ApplicationProvider.getApplicationContext()
private val controller = TetherPreferenceController(context, TEST_KEY)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(TetherUtil::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
ExtendedMockito.doReturn(true).`when` { TetherUtil.isTetherAvailable(context) }
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun getAvailabilityStatus_whenTetherAvailable() {
ExtendedMockito.doReturn(true).`when` { TetherUtil.isTetherAvailable(context) }
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.AVAILABLE)
}
@Test
fun getAvailabilityStatus_whenTetherNotAvailable() {
ExtendedMockito.doReturn(false).`when` { TetherUtil.isTetherAvailable(context) }
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getSummaryResId_bothWifiAndBluetoothOn() {
val summaryResId = controller.getSummaryResId(
setOf(TetheringManager.TETHERING_WIFI, TetheringManager.TETHERING_BLUETOOTH)
)
assertThat(summaryResId).isEqualTo(R.string.tether_settings_summary_hotspot_on_tether_on)
}
@Test
fun getSummaryResId_onlyWifiHotspotOn() {
val summaryResId = controller.getSummaryResId(setOf(TetheringManager.TETHERING_WIFI))
assertThat(summaryResId).isEqualTo(R.string.tether_settings_summary_hotspot_on_tether_off)
}
@Test
fun getSummaryResId_onlyBluetoothTetheringOn() {
val summaryResId = controller.getSummaryResId(setOf(TetheringManager.TETHERING_BLUETOOTH))
assertThat(summaryResId).isEqualTo(R.string.tether_settings_summary_hotspot_off_tether_on)
}
@Test
fun getSummaryResId_allOff() {
val summaryResId = controller.getSummaryResId(emptySet())
assertThat(summaryResId).isEqualTo(R.string.tether_preference_summary_off)
}
private companion object {
const val TEST_KEY = "test_key"
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.settings.network
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothPan
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.net.TetheringInterface
import android.net.TetheringManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.mapNotNull
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.stub
@RunWith(AndroidJUnit4::class)
class TetheredRepositoryTest {
private var tetheringInterfaces: Set<TetheringInterface> = emptySet()
private var tetheringEventCallback: TetheringManager.TetheringEventCallback? = null
private val mockTetheringManager = mock<TetheringManager> {
on { registerTetheringEventCallback(any(), any()) } doAnswer {
tetheringEventCallback = it.arguments[1] as TetheringManager.TetheringEventCallback
tetheringEventCallback?.onTetheredInterfacesChanged(tetheringInterfaces)
}
}
private val mockBluetoothPan = mock<BluetoothPan> {
on { isTetheringOn } doReturn false
}
private val mockBluetoothAdapter = mock<BluetoothAdapter> {
on { getProfileProxy(any(), any(), eq(BluetoothProfile.PAN)) } doAnswer {
val listener = it.arguments[1] as BluetoothProfile.ServiceListener
listener.onServiceConnected(BluetoothProfile.PAN, mockBluetoothPan)
true
}
}
private val mockBluetoothManager = mock<BluetoothManager> {
on { adapter } doReturn mockBluetoothAdapter
}
private val context = mock<Context> {
on { getSystemService(TetheringManager::class.java) } doReturn mockTetheringManager
on { getSystemService(BluetoothManager::class.java) } doReturn mockBluetoothManager
}
private val repository = TetheredRepository(context)
@Test
fun tetheredTypesFlow_allOff() = runBlocking {
val tetheredTypes = repository.tetheredTypesFlow().firstWithTimeoutOrNull()
assertThat(tetheredTypes).isEmpty()
}
@Test
fun tetheredTypesFlow_wifiHotspotOn(): Unit = runBlocking {
tetheringInterfaces = setOf(TetheringInterface(TetheringManager.TETHERING_WIFI, ""))
val tetheredTypes = repository.tetheredTypesFlow().firstWithTimeoutOrNull()
assertThat(tetheredTypes).containsExactly(TetheringManager.TETHERING_WIFI)
}
@Test
fun tetheredTypesFlow_usbTetheringTurnOnLater(): Unit = runBlocking {
val tetheredTypeDeferred = async {
repository.tetheredTypesFlow().mapNotNull {
it.singleOrNull()
}.firstWithTimeoutOrNull()
}
delay(100)
tetheringEventCallback?.onTetheredInterfacesChanged(
setOf(TetheringInterface(TetheringManager.TETHERING_USB, ""))
)
assertThat(tetheredTypeDeferred.await()).isEqualTo(TetheringManager.TETHERING_USB)
}
@Test
fun tetheredTypesFlow_bluetoothOff(): Unit = runBlocking {
mockBluetoothAdapter.stub {
on { state } doReturn BluetoothAdapter.STATE_OFF
}
val tetheredTypes = repository.tetheredTypesFlow().firstWithTimeoutOrNull()
assertThat(tetheredTypes).isEmpty()
}
@Test
fun tetheredTypesFlow_bluetoothOnTetheringOff(): Unit = runBlocking {
mockBluetoothAdapter.stub {
on { state } doReturn BluetoothAdapter.STATE_ON
}
val tetheredTypes = repository.tetheredTypesFlow().firstWithTimeoutOrNull()
assertThat(tetheredTypes).isEmpty()
}
@Test
fun tetheredTypesFlow_bluetoothTetheringOn(): Unit = runBlocking {
mockBluetoothAdapter.stub {
on { state } doReturn BluetoothAdapter.STATE_ON
}
mockBluetoothPan.stub {
on { isTetheringOn } doReturn true
}
val tetheredTypes = repository.tetheredTypesFlow().firstWithTimeoutOrNull()
assertThat(tetheredTypes).containsExactly(TetheringManager.TETHERING_BLUETOOTH)
}
}

View File

@@ -0,0 +1,223 @@
/*
* 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.settings.network.apn
import android.content.Context
import android.net.Uri
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.isFocused
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onChild
import androidx.compose.ui.test.onChildAt
import androidx.compose.ui.test.onLast
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.google.common.truth.Truth
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class ApnEditPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private val apnName = "apn_name"
private val proxy = "proxy"
private val port = "port"
private val apnType = context.resources.getString(R.string.apn_type)
private val apnRoaming = "IPv4"
private val apnEnable = context.resources.getString(R.string.carrier_enabled)
private val apnProtocolOptions =
context.resources.getStringArray(R.array.apn_protocol_entries).toList()
private val networkType = context.resources.getString(R.string.network_type)
private val passwordTitle = context.resources.getString(R.string.apn_password)
private val apnInit = ApnData(
name = apnName,
proxy = proxy,
port = port,
apnType = apnType,
apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
apnEnable = true
)
private val apnData = mutableStateOf(
apnInit
)
private val uri = mock<Uri> {}
@Test
fun apnEditPageProvider_name() {
Truth.assertThat(ApnEditPageProvider.name).isEqualTo("ApnEdit")
}
@Test
fun title_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
}
@Test
fun name_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onNodeWithText(apnName, true).assertIsDisplayed()
}
@Test
fun proxy_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(proxy, true))
composeTestRule.onNodeWithText(proxy, true).assertIsDisplayed()
}
@Test
fun port_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(port, true))
composeTestRule.onNodeWithText(port, true).assertIsDisplayed()
}
@Test
fun apn_type_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnType, true))
composeTestRule.onNodeWithText(apnType, true).assertIsDisplayed()
}
@Test
fun apn_roaming_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnRoaming, true))
composeTestRule.onNodeWithText(apnRoaming, true).assertIsDisplayed()
}
@Test
fun carrier_enabled_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnEnable, true))
composeTestRule.onNodeWithText(apnEnable, true).assertIsDisplayed()
}
@Test
fun carrier_enabled_isChecked() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnEnable, true))
composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
}
@Test
fun carrier_enabled_checkChanged() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnEnable, true))
composeTestRule.onNodeWithText(apnEnable, true).performClick()
composeTestRule.onNodeWithText(apnEnable, true).assertIsOff()
}
@Test
fun network_type_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(networkType, true))
composeTestRule.onNodeWithText(networkType, true).assertIsDisplayed()
}
@Test
fun network_type_changed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(networkType, true))
composeTestRule.onNodeWithText(networkType, true).performClick()
composeTestRule.onNodeWithText(NETWORK_TYPE_LTE, true).performClick()
composeTestRule.onNode(hasText(NETWORK_TYPE_UNSPECIFIED) and isFocused(), true)
.assertDoesNotExist()
composeTestRule.onNode(hasText(NETWORK_TYPE_LTE) and isFocused(), true).assertIsDisplayed()
}
@Test
fun network_type_changed_back2Default() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(networkType, true))
composeTestRule.onNodeWithText(networkType, true).performClick()
composeTestRule.onNodeWithText(NETWORK_TYPE_LTE, true).performClick()
composeTestRule.onNode(hasText(NETWORK_TYPE_UNSPECIFIED) and isFocused(), true)
.assertDoesNotExist()
composeTestRule.onNode(hasText(NETWORK_TYPE_LTE) and isFocused(), true).assertIsDisplayed()
composeTestRule.onAllNodesWithText(NETWORK_TYPE_LTE, true).onLast().performClick()
composeTestRule.onNode(hasText(NETWORK_TYPE_UNSPECIFIED) and isFocused(), true)
.assertIsDisplayed()
composeTestRule.onNode(hasText(NETWORK_TYPE_LTE) and isFocused(), true).assertDoesNotExist()
}
@Test
fun password_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(passwordTitle, true))
composeTestRule.onNodeWithText(passwordTitle, true).assertIsDisplayed()
}
private companion object {
const val NETWORK_TYPE_UNSPECIFIED = "Unspecified"
const val NETWORK_TYPE_LTE = "LTE"
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.settings.network.apn
import android.content.ContentResolver
import android.content.Context
import android.database.MatrixCursor
import android.net.Uri
import android.provider.Telephony
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
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.stub
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class ApnRepositoryTest {
private val contentResolver = mock<ContentResolver>()
private val mockSubscriptionInfo = mock<SubscriptionInfo> {
on { mccString } doReturn MCC
on { mncString } doReturn MNC
}
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { getActiveSubscriptionInfo(SUB_ID) } doReturn mockSubscriptionInfo
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { contentResolver } doReturn contentResolver
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
}
private val uri = mock<Uri> {}
@Test
fun getApnDataFromUri() {
// mock out resources and the feature provider
val cursor = MatrixCursor(Projection)
cursor.addRow(
arrayOf<Any>(
0,
"name",
"apn",
"proxy",
"port",
"userName",
"server",
"passWord",
"mmsc",
"mmsProxy",
"mmsPort",
0,
"apnType",
"apnProtocol",
0,
0,
"apnRoaming",
0,
1,
)
)
whenever(contentResolver.query(uri, Projection, null, null, null)).thenReturn(cursor)
val apnData = getApnDataFromUri(uri, context)
assertThat(apnData.name).isEqualTo("name")
}
@Test
fun getApnIdMap_knownCarrierId() {
mockSubscriptionInfo.stub {
on { carrierId } doReturn CARRIER_ID
}
val idMap = context.getApnIdMap(SUB_ID)
assertThat(idMap).containsExactly(Telephony.Carriers.CARRIER_ID, CARRIER_ID)
}
@Test
fun getApnIdMap_unknownCarrierId() {
mockSubscriptionInfo.stub {
on { carrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
}
val idMap = context.getApnIdMap(SUB_ID)
assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, MCC + MNC)
}
private companion object {
const val SUB_ID = 2
const val CARRIER_ID = 10
const val MCC = "310"
const val MNC = "101"
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.settings.network.apn
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class ApnStatusTest {
private val apnData = mock<ApnData> {
on {
it.subId
} doReturn 1
}
private val configManager = mock<CarrierConfigManager> {
val p = PersistableBundle()
p.putBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL, true)
on {
getConfigForSubId(
apnData.subId,
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY,
CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY,
CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING,
CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING,
CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL
)
} doReturn p
}
@Test
fun getCarrierCustomizedConfig_test() {
assert(getCarrierCustomizedConfig(apnData, configManager).isAddApnAllowed)
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
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
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 AllowedNetworkTypesFlowTest {
private var allowedNetworkTypesListener: TelephonyCallback.AllowedNetworkTypesListener? = null
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { registerTelephonyCallback(any(), any()) } doAnswer {
allowedNetworkTypesListener =
it.arguments[1] as TelephonyCallback.AllowedNetworkTypesListener
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
@Test
fun allowedNetworkTypesFlow_initial_notSndInitialValue() = runBlocking {
val flow = context.allowedNetworkTypesFlow(SUB_ID)
val state = flow.firstWithTimeoutOrNull()
assertThat(state).isNull()
}
@Test
fun allowedNetworkTypesFlow_userReasonChanged_sendChanged(): Unit = runBlocking {
val listDeferred = async {
context.allowedNetworkTypesFlow(SUB_ID).toListWithTimeout()
}
delay(100)
allowedNetworkTypesListener?.onAllowedNetworkTypesChanged(
TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
ALLOWED_NETWORK_TYPE,
)
assertThat(listDeferred.await()).containsExactly(ALLOWED_NETWORK_TYPE)
}
@Test
fun allowedNetworkTypesFlow_carrierReasonChanged_sendChanged(): Unit = runBlocking {
val listDeferred = async {
context.allowedNetworkTypesFlow(SUB_ID).toListWithTimeout()
}
delay(100)
allowedNetworkTypesListener?.onAllowedNetworkTypesChanged(
TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
ALLOWED_NETWORK_TYPE,
)
assertThat(listDeferred.await()).containsExactly(ALLOWED_NETWORK_TYPE)
}
@Test
fun allowedNetworkTypesFlow_powerReasonChanged_notSendChanged() = runBlocking {
val listDeferred = async {
context.allowedNetworkTypesFlow(SUB_ID).toListWithTimeout()
}
delay(100)
allowedNetworkTypesListener?.onAllowedNetworkTypesChanged(
TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
ALLOWED_NETWORK_TYPE,
)
assertThat(listDeferred.await()).isEmpty()
}
private companion object {
const val SUB_ID = 1
const val ALLOWED_NETWORK_TYPE = 10L
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
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
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 CallStateFlowTest {
private var callStateListener: TelephonyCallback.CallStateListener? = null
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { registerTelephonyCallback(any(), any()) } doAnswer {
callStateListener = it.arguments[1] as TelephonyCallback.CallStateListener
callStateListener?.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE)
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
@Test
fun callStateFlow_initial_sendInitialState() = runBlocking {
val flow = context.callStateFlow(SUB_ID)
val state = flow.firstWithTimeoutOrNull()
assertThat(state).isEqualTo(TelephonyManager.CALL_STATE_IDLE)
}
@Test
fun callStateFlow_changed_sendChangedState() = runBlocking {
val listDeferred = async {
context.callStateFlow(SUB_ID).toListWithTimeout()
}
delay(100)
callStateListener?.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING)
assertThat(listDeferred.await())
.containsExactly(TelephonyManager.CALL_STATE_IDLE, TelephonyManager.CALL_STATE_RINGING)
.inOrder()
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.settings.network.telephony
import android.content.Context
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.spa.preference.ComposePreference
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class CallingPreferenceCategoryControllerTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val preference = ComposePreference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private val controller = CallingPreferenceCategoryController(context, TEST_KEY)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@Test
fun updateChildVisible_singleChildVisible_categoryVisible() {
controller.updateChildVisible(CHILD_A_KEY, true)
assertThat(preference.isVisible).isTrue()
}
@Test
fun updateChildVisible_singleChildNotVisible_categoryNotVisible() {
controller.updateChildVisible(CHILD_A_KEY, false)
assertThat(preference.isVisible).isFalse()
}
@Test
fun updateChildVisible_oneChildVisible_categoryVisible() {
controller.updateChildVisible(CHILD_A_KEY, true)
controller.updateChildVisible(CHILD_B_KEY, false)
assertThat(preference.isVisible).isTrue()
}
@Test
fun updateChildVisible_nonChildNotVisible_categoryNotVisible() {
controller.updateChildVisible(CHILD_A_KEY, false)
controller.updateChildVisible(CHILD_B_KEY, false)
assertThat(preference.isVisible).isFalse()
}
private companion object {
const val TEST_KEY = "test_key"
const val CHILD_A_KEY = "a"
const val CHILD_B_KEY = "b"
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.CarrierConfigManager
import androidx.core.os.persistableBundleOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class CarrierConfigManagerExtTest {
private val mockCarrierConfigManager = mock<CarrierConfigManager>()
private val context = mock<Context> {
on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
}
@Test
fun safeGetConfig_managerReturnKeyValue_returnNonEmptyBundle() {
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), eq(KEY)) } doReturn persistableBundleOf(KEY to VALUE)
}
val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
val bundle = carrierConfigManager.safeGetConfig(listOf(KEY))
assertThat(bundle.getString(KEY)).isEqualTo(VALUE)
}
@Test
fun safeGetConfig_managerThrowIllegalStateException_returnEmptyBundle() {
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), eq(KEY)) } doThrow IllegalStateException()
}
val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
val bundle = carrierConfigManager.safeGetConfig(listOf(KEY))
assertThat(bundle.containsKey(KEY)).isFalse()
}
private companion object {
const val KEY = "key"
const val VALUE = "value"
}
}

View File

@@ -0,0 +1,193 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.content.Intent
import android.net.NetworkTemplate
import android.provider.Settings
import android.telephony.SubscriptionManager
import android.util.DataUnit
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE
import com.android.settings.datausage.DataUsageUtils
import com.android.settings.datausage.lib.DataUsageLib
import com.android.settings.datausage.lib.NetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange
import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settingslib.spa.testutils.waitUntil
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.doReturn
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
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class DataUsagePreferenceControllerTest {
private lateinit var mockSession: MockitoSession
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val preference = Preference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private val networkTemplate = mock<NetworkTemplate>()
private val repository = mock<NetworkCycleDataRepository> {
on { queryUsage(any()) } doReturn NetworkUsageData(START_TIME, END_TIME, 0L)
}
private val controller = spy(DataUsagePreferenceController(context, TEST_KEY)) {
doReturn(repository).whenever(mock).createNetworkCycleDataRepository()
}
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.spyStatic(DataUsageUtils::class.java)
.spyStatic(DataUsageLib::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
ExtendedMockito.doReturn(true).`when` { DataUsageUtils.hasMobileData(context) }
ExtendedMockito.doReturn(networkTemplate).`when` {
DataUsageLib.getMobileTemplate(context, SUB_ID)
}
preferenceScreen.addPreference(preference)
controller.apply {
init(SUB_ID)
displayPreference(preferenceScreen)
}
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun getAvailabilityStatus_validSubId_returnAvailable() {
assertThat(controller.availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_invalidSubId_returnUnsearchable() {
val availabilityStatus =
controller.getAvailabilityStatus(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
assertThat(availabilityStatus).isEqualTo(AVAILABLE_UNSEARCHABLE)
}
@Test
fun handlePreferenceTreeClick_startActivity() = runBlocking {
val usageData = NetworkUsageData(START_TIME, END_TIME, 1L)
repository.stub {
on { loadFirstCycle() } doReturn usageData
}
controller.onViewCreated(TestLifecycleOwner())
waitUntil { preference.summary != null }
controller.handlePreferenceTreeClick(preference)
val intent = argumentCaptor<Intent> {
verify(context).startActivity(capture())
}.firstValue
assertThat(intent.action).isEqualTo(Settings.ACTION_MOBILE_DATA_USAGE)
assertThat(intent.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID)
}
@Test
fun updateState_invalidSubId_disabled() = runBlocking {
controller.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
controller.onViewCreated(TestLifecycleOwner())
waitUntil { !preference.isEnabled }
}
@Test
fun updateState_noFistCycleUsageButOtherUsage_shouldEnablePreference() = runBlocking {
val usageData = NetworkUsageData(START_TIME, END_TIME, 0L)
repository.stub {
on { loadFirstCycle() } doReturn usageData
on { queryUsage(AllTimeRange) } doReturn
NetworkUsageData(AllTimeRange.lower, AllTimeRange.upper, 1L)
}
preference.isEnabled = false
controller.onViewCreated(TestLifecycleOwner())
waitUntil { preference.isEnabled }
waitUntil { preference.summary?.contains("0 B used") == true }
}
@Test
fun updateState_noDataUsage_shouldDisablePreference() = runBlocking {
val usageData = NetworkUsageData(START_TIME, END_TIME, 0L)
repository.stub {
on { loadFirstCycle() } doReturn usageData
on { queryUsage(AllTimeRange) } doReturn
NetworkUsageData(AllTimeRange.lower, AllTimeRange.upper, 0L)
}
preference.isEnabled = true
controller.onViewCreated(TestLifecycleOwner())
waitUntil { !preference.isEnabled }
waitUntil { preference.summary?.contains("0 B used") == true }
}
@Test
fun updateState_shouldUseIecUnit() = runBlocking {
val usageData = NetworkUsageData(START_TIME, END_TIME, DataUnit.MEBIBYTES.toBytes(1))
repository.stub {
on { loadFirstCycle() } doReturn usageData
}
controller.onViewCreated(TestLifecycleOwner())
waitUntil { preference.summary?.contains("1.00 MB used") == true }
}
private companion object {
const val TEST_KEY = "test_key"
const val SUB_ID = 2
const val START_TIME = 10L
const val END_TIME = 30L
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.settings.network.telephony
import android.app.KeyguardManager
import android.content.Context
import android.os.UserManager
import android.telephony.SubscriptionInfo
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.network.SubscriptionUtil
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
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.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class DeleteSimProfilePreferenceControllerTest {
private val subscriptionInfo = mock<SubscriptionInfo> {
on { subscriptionId } doReturn SUB_ID
on { isEmbedded } doReturn true
}
private val mockKeyguardManager = mock<KeyguardManager>() {
on { isKeyguardSecure() } doReturn false
}
private var context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
on { getSystemService(Context.KEYGUARD_SERVICE) } doReturn mockKeyguardManager
}
private val preference = Preference(context).apply { key = PREF_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
.apply { addPreference(preference) }
private var controller = DeleteSimProfilePreferenceController(context, PREF_KEY)
@Before
fun setUp() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(subscriptionInfo))
}
@After
fun tearDown() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(null)
}
@Test
fun getAvailabilityStatus_noSubs_notAvailable() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(emptyList())
controller.init(SUB_ID)
assertThat(controller.isAvailable()).isFalse()
}
@Test
fun getAvailabilityStatus_physicalSim_notAvailable() {
whenever(subscriptionInfo.isEmbedded).thenReturn(false)
controller.init(SUB_ID)
assertThat(controller.isAvailable()).isFalse()
}
@Test
fun getAvailabilityStatus_unknownSim_notAvailable() {
whenever(subscriptionInfo.subscriptionId).thenReturn(OTHER_ID)
controller.init(SUB_ID)
assertThat(controller.isAvailable()).isFalse()
}
@Test
fun getAvailabilityStatus_knownEsim_isAvailable() {
controller.init(SUB_ID)
assertThat(controller.isAvailable()).isTrue()
}
@Test
fun onPreferenceClick_startsIntent() {
controller.init(SUB_ID)
controller.displayPreference(preferenceScreen)
controller.handlePreferenceTreeClick(preference)
verify(context, times(1)).startActivity(any())
}
private companion object {
const val PREF_KEY = "delete_profile_key"
const val SUB_ID = 1234
const val OTHER_ID = 5678
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager
import android.telephony.data.ApnSetting
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
import com.google.common.truth.Truth.assertThat
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.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class MmsMessagePreferenceControllerTest {
private val mockTelephonyManager1: TelephonyManager = mock<TelephonyManager> {
on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true
}
private val mockTelephonyManager2: TelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_1_ID) } doReturn mockTelephonyManager1
on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true
}
private val mockTelephonyManager: TelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_1_ID) } doReturn mockTelephonyManager1
on { createForSubscriptionId(SUB_2_ID) } doReturn mockTelephonyManager2
on { createForSubscriptionId(INVALID_SUBSCRIPTION_ID) } doReturn mock
}
private var context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
private var defaultDataSubId = SUB_1_ID
private val controller = MmsMessagePreferenceController(
context = context,
key = KEY,
getDefaultDataSubId = { defaultDataSubId },
).apply { init(SUB_2_ID) }
@Test
fun getAvailabilityStatus_invalidSubscription_unavailable() {
controller.init(INVALID_SUBSCRIPTION_ID)
val availabilityStatus = controller.getAvailabilityStatus(INVALID_SUBSCRIPTION_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_mobileDataOn_unavailable() {
mockTelephonyManager2.stub {
on { isDataEnabled } doReturn true
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_meteredOff_unavailable() {
mockTelephonyManager2.stub {
on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn false
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_isDefaultDataAndDataOnAndAutoDataSwitchOn_unavailable() {
defaultDataSubId = SUB_2_ID
mockTelephonyManager2.stub {
on { isDataEnabled } doReturn true
on {
isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
} doReturn true
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_isDefaultDataAndDataOffAndAutoDataSwitchOn_available() {
defaultDataSubId = SUB_2_ID
mockTelephonyManager2.stub {
on { isDataEnabled } doReturn false
on {
isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
} doReturn true
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID)
assertThat(availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_defaultDataOnAndAutoDataSwitchOn_unavailable() {
mockTelephonyManager1.stub {
on { isDataEnabled } doReturn true
}
mockTelephonyManager2.stub {
on {
isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
} doReturn true
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID)
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_defaultDataOffAndAutoDataSwitchOn_available() {
mockTelephonyManager1.stub {
on { isDataEnabled } doReturn false
}
mockTelephonyManager2.stub {
on {
isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
} doReturn true
}
val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID)
assertThat(availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun isChecked_whenMmsNotAlwaysAllowed_returnFalse() {
mockTelephonyManager2.stub {
on {
isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)
} doReturn false
}
val isChecked = controller.isChecked()
assertThat(isChecked).isFalse()
}
@Test
fun isChecked_whenMmsAlwaysAllowed_returnTrue() {
mockTelephonyManager2.stub {
on {
isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)
} doReturn true
}
val isChecked = controller.isChecked()
assertThat(isChecked).isTrue()
}
@Test
fun setChecked_setTrue_setDataIntoSubscriptionManager() {
controller.setChecked(true)
verify(mockTelephonyManager2).setMobileDataPolicyEnabled(
TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, true
)
}
@Test
fun setChecked_setFalse_setDataIntoSubscriptionManager() {
controller.setChecked(false)
verify(mockTelephonyManager2).setMobileDataPolicyEnabled(
TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, false
)
}
private companion object {
const val KEY = "mms_message"
const val SUB_1_ID = 1
const val SUB_2_ID = 2
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.platform.test.flag.junit.SetFlagsRule
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager
import android.telephony.euicc.EuiccManager
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.core.BasePreferenceController
import com.android.settings.flags.Flags
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.CustomDialogPreferenceCompat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class MobileNetworkEidPreferenceControllerTest {
@get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
private lateinit var mockSession: MockitoSession
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
private val mockFragment = mock<Fragment>{
val viewmodel = mockViewModels
}
private var mockEid = String()
private val mockTelephonyManager = mock<TelephonyManager> {
on {uiccCardsInfo} doReturn listOf()
on { createForSubscriptionId(any()) } doReturn mock
}
private val mockEuiccManager = mock<EuiccManager> {
on {isEnabled} doReturn true
on {eid} doReturn mockEid
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
on { getSystemService(EuiccManager::class.java) } doReturn mockEuiccManager
}
private val controller = MobileNetworkEidPreferenceController(context, TEST_KEY)
private val preference = CustomDialogPreferenceCompat(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SubscriptionUtil::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun refreshData_getEmptyEid_preferenceIsNotVisible() = runBlocking {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2
)
)
var mockSubId = 2
controller.init(mockFragment, mockSubId)
mockEid = String()
controller.refreshData(SUB_INFO_2)
assertThat(preference.isVisible).isEqualTo(false)
}
@Test
fun refreshData_getEmptyEid_preferenceSummaryIsExpected() = runBlocking {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2
)
)
var mockSubId = 2
controller.init(mockFragment, mockSubId)
mockEid = "test eid"
mockEuiccManager.stub {
on {eid} doReturn mockEid
}
controller.refreshData(SUB_INFO_2)
assertThat(preference.summary).isEqualTo(mockEid)
}
@Test
fun getAvailabilityStatus_notSimHardwareVisible() {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
}
private companion object {
const val TEST_KEY = "test_key"
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(1)
setDisplayName(DISPLAY_NAME_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(2)
setDisplayName(DISPLAY_NAME_2)
}.build()
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager
import android.telephony.euicc.EuiccManager
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.telephony.PhoneConstants
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.CustomDialogPreferenceCompat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class MobileNetworkImeiPreferenceControllerTest {
private lateinit var mockSession: MockitoSession
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
private val mockFragment = mock<Fragment>{
val viewmodel = mockViewModels
}
private var mockImei = String()
private val mockTelephonyManager = mock<TelephonyManager> {
on { uiccCardsInfo } doReturn listOf()
on { createForSubscriptionId(any()) } doReturn mock
on { currentPhoneType } doReturn TelephonyManager.PHONE_TYPE_GSM
on { imei } doReturn mockImei
on { meid } doReturn mockImei
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
private val controller = MobileNetworkImeiPreferenceController(context, TEST_KEY)
private val preference = Preference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SubscriptionUtil::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun refreshData_getPhoneNumber_preferenceSummaryIsExpected() = runBlocking {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2
)
)
var mockSubId = 2
controller.init(mockFragment, mockSubId)
mockImei = "test imei"
mockTelephonyManager.stub {
on { imei } doReturn mockImei
}
controller.refreshData(SUB_INFO_2)
assertThat(preference.summary).isEqualTo(mockImei)
}
@Test
fun getAvailabilityStatus_notSimHardwareVisible() {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
}
private companion object {
const val TEST_KEY = "test_key"
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(1)
setDisplayName(DISPLAY_NAME_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(2)
setDisplayName(DISPLAY_NAME_2)
}.build()
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionInfo
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class MobileNetworkPhoneNumberPreferenceControllerTest {
private lateinit var mockSession: MockitoSession
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
private val mockFragment = mock<Fragment>{
val viewmodel = mockViewModels
}
private var mockPhoneNumber = String()
private val context: Context = ApplicationProvider.getApplicationContext()
private val controller = MobileNetworkPhoneNumberPreferenceController(context, TEST_KEY)
private val preference = Preference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SubscriptionUtil::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
whenever(SubscriptionUtil.getBidiFormattedPhoneNumber(any(),any())).thenReturn(mockPhoneNumber)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun refreshData_getEmptyPhoneNumber_preferenceIsNotVisible() = runBlocking {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2
)
)
var mockSubId = 2
controller.init(mockFragment, mockSubId)
mockPhoneNumber = String()
controller.refreshData(SUB_INFO_2)
assertThat(preference.summary).isEqualTo(
context.getString(R.string.device_info_default))
}
@Test
fun refreshData_getPhoneNumber_preferenceSummaryIsExpected() = runBlocking {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2
)
)
var mockSubId = 2
controller.init(mockFragment, mockSubId)
mockPhoneNumber = "test phone number"
whenever(SubscriptionUtil.getBidiFormattedPhoneNumber(any(),any())).thenReturn(mockPhoneNumber)
controller.refreshData(SUB_INFO_2)
assertThat(preference.summary).isEqualTo(mockPhoneNumber)
}
@Test
fun getAvailabilityStatus_notSimHardwareVisible() {
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
val availabilityStatus = controller.availabilityStatus
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
}
private companion object {
const val TEST_KEY = "test_key"
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(1)
setDisplayName(DISPLAY_NAME_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(2)
setDisplayName(DISPLAY_NAME_2)
}.build()
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager
import android.telephony.euicc.EuiccManager
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.SubscriptionUtil
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class MobileNetworkSpnPreferenceControllerTest {
private lateinit var mockSession: MockitoSession
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
private val mockFragment = mock<Fragment>{
val viewmodel = mockViewModels
}
private val context: Context = ApplicationProvider.getApplicationContext()
private val controller = MobileNetworkSpnPreferenceController(context, TEST_KEY)
private val preference = Preference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SubscriptionUtil::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun refreshData_getCarrierName_preferenceSummaryIsExpected() = runBlocking {
var testList = listOf(
SUB_INFO_1,
SUB_INFO_2
)
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(testList)
var mockSubId = 2
controller.init(mockFragment, mockSubId)
controller.refreshData(testList)
assertThat(preference.summary).isEqualTo(CARRIER_NAME_2)
}
private companion object {
const val TEST_KEY = "test_key"
const val CARRIER_NAME_1 = "Sub 1"
const val CARRIER_NAME_2 = "Sub 2"
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(1)
setCarrierName(CARRIER_NAME_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(2)
setCarrierName(CARRIER_NAME_2)
}.build()
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.ServiceState
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
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
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 ServiceStateFlowTest {
private var serviceStateListener: TelephonyCallback.ServiceStateListener? = null
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { registerTelephonyCallback(any(), any()) } doAnswer {
serviceStateListener = it.arguments[1] as TelephonyCallback.ServiceStateListener
serviceStateListener?.onServiceStateChanged(ServiceState())
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
@Test
fun serviceStateFlow_initial_sndInitialValue() = runBlocking {
val flow = context.serviceStateFlow(SUB_ID)
val state = flow.firstWithTimeoutOrNull()
assertThat(state).isNotNull()
}
@Test
fun serviceStateFlow_changed_sendChanged(): Unit = runBlocking {
val listDeferred = async {
context.serviceStateFlow(SUB_ID).toListWithTimeout()
}
delay(100)
serviceStateListener?.onServiceStateChanged(ServiceState())
assertThat(listDeferred.await()).hasSize(2)
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionManager
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
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
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class SubscriptionRepositoryTest {
private var subInfoListener: SubscriptionManager.OnSubscriptionsChangedListener? = null
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer {
subInfoListener = it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
subInfoListener?.onSubscriptionsChanged()
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
}
@Test
fun isSubscriptionEnabledFlow() = runBlocking {
mockSubscriptionManager.stub {
on { isSubscriptionEnabled(SUB_ID) } doReturn true
}
val isEnabled = context.isSubscriptionEnabledFlow(SUB_ID).firstWithTimeoutOrNull()
assertThat(isEnabled).isTrue()
}
@Test
fun subscriptionsChangedFlow_hasInitialValue() = runBlocking {
val initialValue = context.subscriptionsChangedFlow().firstWithTimeoutOrNull()
assertThat(initialValue).isSameInstanceAs(Unit)
}
@Test
fun subscriptionsChangedFlow_changed() = runBlocking {
val listDeferred = async {
context.subscriptionsChangedFlow().toListWithTimeout()
}
delay(100)
subInfoListener?.onSubscriptionsChanged()
assertThat(listDeferred.await()).hasSize(2)
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.settings.network.telephony
import android.content.Context
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat
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
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class TelephonyRepositoryTest {
private var telephonyCallback: TelephonyCallback? = null
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { registerTelephonyCallback(any(), any()) } doAnswer {
telephonyCallback = it.arguments[1] as TelephonyCallback
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
@Test
fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
object : TelephonyCallback() {}
}
flow.firstWithTimeoutOrNull()
assertThat(telephonyCallback).isNotNull()
}
@Test
fun telephonyCallbackFlow_callbackUnregistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
object : TelephonyCallback() {}
}
flow.firstWithTimeoutOrNull()
verify(mockTelephonyManager).unregisterTelephonyCallback(telephonyCallback!!)
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.settings.network.telephony
import android.content.Context
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.core.BasePreferenceController
import com.android.settingslib.spa.testutils.waitUntil
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TelephonyStatusControlSessionTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun init() = runTest {
val controller = TestController(context)
val session = TelephonyStatusControlSession(
controllers = listOf(controller),
lifecycle = TestLifecycleOwner().lifecycle,
)
waitUntil { controller.availabilityStatus == STATUS }
session.close()
}
@Test
fun close() = runTest {
val controller = TestController(context)
val session = TelephonyStatusControlSession(
controllers = listOf(controller),
lifecycle = TestLifecycleOwner().lifecycle,
)
session.close()
assertThat(controller.availabilityStatus).isNull()
}
private companion object {
const val KEY = "key"
const val STATUS = BasePreferenceController.AVAILABLE
}
private class TestController(context: Context) : BasePreferenceController(context, KEY),
TelephonyAvailabilityHandler {
var availabilityStatus: Int? = null
override fun getAvailabilityStatus(): Int = STATUS
override fun setAvailabilityStatus(status: Int) {
availabilityStatus = status
}
override fun unsetAvailabilityStatus() {
availabilityStatus = null
}
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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.settings.network.telephony
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.telecom.PhoneAccountHandle
import android.telecom.TelecomManager
import android.telephony.TelephonyManager
import android.telephony.ims.ImsMmTelManager
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.network.telephony.wificalling.WifiCallingRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
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.stub
@RunWith(AndroidJUnit4::class)
class WifiCallingPreferenceControllerTest {
private val mockTelecomManager = mock<TelecomManager>()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelecomManager::class.java) } doReturn mockTelecomManager
}
private val preferenceIntent = Intent()
private val preference = Preference(context).apply {
key = TEST_KEY
intent = preferenceIntent
}
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private var callState = TelephonyManager.CALL_STATE_IDLE
private val mockWifiCallingRepository = mock<WifiCallingRepository> {
on { getWiFiCallingMode() } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN
on { wifiCallingReadyFlow() } doReturn flowOf(true)
}
private val callingPreferenceCategoryController =
CallingPreferenceCategoryController(context, "calling_category")
private val controller = WifiCallingPreferenceController(
context = context,
key = TEST_KEY,
callStateFlowFactory = { flowOf(callState) },
wifiCallingRepositoryFactory = { mockWifiCallingRepository },
).init(subId = SUB_ID, callingPreferenceCategoryController)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@Test
fun summary_noSimCallManager_setCorrectSummary() = runBlocking {
mockTelecomManager.stub {
on { getSimCallManagerForSubscription(SUB_ID) } doReturn null
}
mockWifiCallingRepository.stub {
on { getWiFiCallingMode() } doReturn ImsMmTelManager.WIFI_MODE_WIFI_ONLY
}
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.summary)
.isEqualTo(context.getString(com.android.internal.R.string.wfc_mode_wifi_only_summary))
}
@Test
fun summary_hasSimCallManager_summaryIsNull() = runBlocking {
mockTelecomManager.stub {
on { getSimCallManagerForSubscription(SUB_ID) } doReturn
PhoneAccountHandle(ComponentName("", ""), "")
}
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.summary).isNull()
}
@Test
fun isEnabled_callIdle_enabled() = runBlocking {
callState = TelephonyManager.CALL_STATE_IDLE
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isEnabled).isTrue()
}
@Test
fun isEnabled_notCallIdle_disabled() = runBlocking {
callState = TelephonyManager.CALL_STATE_RINGING
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.isEnabled).isFalse()
}
@Test
fun displayPreference_setsSubscriptionIdOnIntent() = runBlocking {
assertThat(preference.intent!!.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID)
}
private companion object {
const val TEST_KEY = "test_key"
const val SUB_ID = 2
}
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network.telephony.gsm
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.telephony.CarrierConfigManager
import android.telephony.ServiceState
import android.telephony.TelephonyManager
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.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.core.os.persistableBundleOf
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.Settings.NetworkSelectActivity
import com.android.settings.spa.preference.ComposePreference
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
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.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 AutoSelectPreferenceControllerTest {
@get:Rule
val composeTestRule = createComposeRule()
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { simOperatorName } doReturn OPERATOR_NAME
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
doNothing().whenever(mock).startActivity(any())
}
private val preference = ComposePreference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private val serviceState = ServiceState()
private val carrierConfig = persistableBundleOf()
private val controller = AutoSelectPreferenceController(
context = context,
key = TEST_KEY,
allowedNetworkTypesFlowFactory = { emptyFlow() },
serviceStateFlowFactory = { flowOf(serviceState) },
getConfigForSubId = { carrierConfig },
).init(subId = SUB_ID)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@Test
fun isChecked_isAutoSelection_on() {
serviceState.isManualSelection = false
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsOn()
}
@Test
fun isChecked_isManualSelection_off() {
serviceState.isManualSelection = true
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsOff()
}
@Test
fun isEnabled_isRoaming_enabled() {
serviceState.roaming = true
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsEnabled()
}
@Test
fun isEnabled_notOnlyAutoSelectInHome_enabled() {
serviceState.roaming = false
carrierConfig.putBoolean(
CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false
)
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText(context.getString(R.string.select_automatically))
.assertIsEnabled()
}
@Test
fun isEnabled_onlyAutoSelectInHome_notEnabled() {
serviceState.roaming = false
carrierConfig.putBoolean(
CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, true
)
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onNodeWithText("Unavailable when connected to T-mobile")
.assertIsNotEnabled()
}
@Test
fun onClick_turnOff_startNetworkSelectActivity() {
serviceState.isManualSelection = false
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onRoot().performClick()
val intent = argumentCaptor<Intent> {
verify(context).startActivity(capture())
}.firstValue
assertThat(intent.component!!.className).isEqualTo(NetworkSelectActivity::class.java.name)
assertThat(intent.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID)
}
@Test
fun onClick_turnOn_setNetworkSelectionModeAutomatic() = runBlocking {
serviceState.isManualSelection = true
controller.progressDialog = mock()
composeTestRule.setContent {
controller.Content()
}
composeTestRule.onRoot().performClick()
delay(100)
verify(controller.progressDialog!!).show()
verify(mockTelephonyManager).setNetworkSelectionModeAutomatic()
}
private companion object {
const val TEST_KEY = "test_key"
const val SUB_ID = 2
const val OPERATOR_NAME = "T-mobile"
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.settings.network.telephony.gsm
import android.content.Context
import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
@RunWith(AndroidJUnit4::class)
class OpenNetworkSelectPagePreferenceControllerTest {
private val subscriptionInfo = mock<SubscriptionInfo> {
on { subscriptionId } doReturn SUB_ID
on { carrierName } doReturn OPERATOR_NAME
}
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { createForAllUserProfiles() } doReturn mock
on { getActiveSubscriptionInfo(SUB_ID) } doReturn subscriptionInfo
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
}
private val preference = Preference(context).apply { key = TEST_KEY }
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
private val serviceState = ServiceState()
private val controller = OpenNetworkSelectPagePreferenceController(
context = context,
key = TEST_KEY,
allowedNetworkTypesFlowFactory = { emptyFlow() },
serviceStateFlowFactory = { flowOf(serviceState) },
).init(subId = SUB_ID)
@Before
fun setUp() {
preferenceScreen.addPreference(preference)
controller.displayPreference(preferenceScreen)
}
@Test
fun isEnabled_modeManual_enabled() {
controller.onNetworkSelectModeUpdated(TelephonyManager.NETWORK_SELECTION_MODE_MANUAL)
assertThat(preference.isEnabled).isTrue()
}
@Test
fun isEnabled_modeAuto_disabled() {
controller.onNetworkSelectModeUpdated(TelephonyManager.NETWORK_SELECTION_MODE_AUTO)
assertThat(preference.isEnabled).isFalse()
}
@Test
fun summary_inService_isOperatorName() = runBlocking {
serviceState.state = ServiceState.STATE_IN_SERVICE
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.summary).isEqualTo(OPERATOR_NAME)
}
@Test
fun summary_notInService_isDisconnect() = runBlocking {
serviceState.state = ServiceState.STATE_OUT_OF_SERVICE
controller.onViewCreated(TestLifecycleOwner())
delay(100)
assertThat(preference.summary).isEqualTo(context.getString(R.string.network_disconnected))
}
private companion object {
const val TEST_KEY = "test_key"
const val SUB_ID = 2
const val OPERATOR_NAME = "T-mobile"
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.settings.network.telephony.ims
import android.telephony.ims.ProvisioningManager
import android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback
import android.telephony.ims.feature.MmTelFeature
import android.telephony.ims.stub.ImsRegistrationImplBase
import androidx.test.ext.junit.runners.AndroidJUnit4
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.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.mock
@RunWith(AndroidJUnit4::class)
class ImsFeatureProvisionedFlowTest {
private var callback: FeatureProvisioningCallback? = null
private val mockProvisioningManager = mock<ProvisioningManager> {
on { registerFeatureProvisioningChangedCallback(any(), any()) } doAnswer {
callback = it.arguments[1] as FeatureProvisioningCallback
callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, true)
}
}
@Test
fun imsFeatureProvisionedFlow_sendInitialValue() = runBlocking {
val flow = imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH, mockProvisioningManager)
val state = flow.first()
assertThat(state).isTrue()
}
@Test
fun imsFeatureProvisionedFlow_changed(): Unit = runBlocking {
val listDeferred = async {
imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH, mockProvisioningManager)
.toListWithTimeout()
}
delay(100)
callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, false)
assertThat(listDeferred.await().last()).isFalse()
}
private companion object {
const val SUB_ID = 1
const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE
const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.settings.network.telephony.ims
import android.content.Context
import android.telephony.AccessNetworkConstants
import android.telephony.ims.ImsMmTelManager
import android.telephony.ims.ImsStateCallback
import android.telephony.ims.feature.MmTelFeature
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.toListWithTimeout
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
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.doThrow
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class ImsMmTelRepositoryTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private var stateCallback: ImsStateCallback? = null
private val mockImsMmTelManager = mock<ImsMmTelManager> {
on { isVoWiFiSettingEnabled } doReturn true
on { getVoWiFiRoamingModeSetting() } doReturn ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED
on { getVoWiFiModeSetting() } doReturn ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED
on { registerImsStateCallback(any(), any()) } doAnswer {
stateCallback = it.arguments[1] as ImsStateCallback
stateCallback?.onAvailable()
}
on { isSupported(eq(CAPABILITY), eq(TRANSPORT), any(), any()) } doAnswer {
@Suppress("UNCHECKED_CAST")
val consumer = it.arguments[3] as Consumer<Boolean>
consumer.accept(true)
}
}
private val repository = ImsMmTelRepositoryImpl(context, SUB_ID, mockImsMmTelManager)
@Test
fun getWiFiCallingMode_voWiFiSettingNotEnabled_returnUnknown() {
mockImsMmTelManager.stub {
on { isVoWiFiSettingEnabled } doReturn false
}
val wiFiCallingMode = repository.getWiFiCallingMode(false)
assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_UNKNOWN)
}
@Test
fun getWiFiCallingMode_useRoamingMode_returnRoamingSetting() {
val wiFiCallingMode = repository.getWiFiCallingMode(true)
assertThat(wiFiCallingMode).isEqualTo(mockImsMmTelManager.getVoWiFiRoamingModeSetting())
}
@Test
fun getWiFiCallingMode_notSseRoamingMode_returnHomeSetting() {
val wiFiCallingMode = repository.getWiFiCallingMode(false)
assertThat(wiFiCallingMode).isEqualTo(mockImsMmTelManager.getVoWiFiModeSetting())
}
@Test
fun getWiFiCallingMode_illegalArgumentException_returnUnknown() {
mockImsMmTelManager.stub {
on { isVoWiFiSettingEnabled } doThrow IllegalArgumentException()
}
val wiFiCallingMode = repository.getWiFiCallingMode(false)
assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_UNKNOWN)
}
@Test
fun imsReadyFlow_sendInitialValue() = runBlocking {
val flow = repository.imsReadyFlow()
val state = flow.first()
assertThat(state).isTrue()
}
@Test
fun imsReadyFlow_changed(): Unit = runBlocking {
val listDeferred = async {
repository.imsReadyFlow().toListWithTimeout()
}
delay(100)
stateCallback?.onUnavailable(ImsStateCallback.REASON_IMS_SERVICE_NOT_READY)
assertThat(listDeferred.await().last()).isFalse()
}
@Test
fun isSupported() = runBlocking {
val isSupported = repository.isSupported(CAPABILITY, TRANSPORT)
assertThat(isSupported).isTrue()
}
private companion object {
const val SUB_ID = 1
const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE
const val TRANSPORT = AccessNetworkConstants.TRANSPORT_TYPE_WLAN
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.settings.network.telephony.wificalling
import android.content.Context
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL
import android.telephony.TelephonyManager
import android.telephony.ims.ImsMmTelManager
import androidx.core.os.persistableBundleOf
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.google.common.truth.Truth.assertThat
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
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class WifiCallingRepositoryTest {
private val mockTelephonyManager = mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
}
private val mockCarrierConfigManager = mock<CarrierConfigManager>()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
}
private val mockImsMmTelRepository = mock<ImsMmTelRepository> {
on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN
}
private val repository = WifiCallingRepository(context, SUB_ID, mockImsMmTelRepository)
@Test
fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() {
mockTelephonyManager.stub {
on { isNetworkRoaming } doReturn true
}
mockUseWfcHomeModeForRoaming(false)
mockImsMmTelRepository.stub {
on { getWiFiCallingMode(true) } doReturn ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED
}
val wiFiCallingMode = repository.getWiFiCallingMode()
assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
}
@Test
fun getWiFiCallingMode_roamingAndUseWfcHomeModeForRoaming_returnHomeSetting() {
mockTelephonyManager.stub {
on { isNetworkRoaming } doReturn true
}
mockUseWfcHomeModeForRoaming(true)
mockImsMmTelRepository.stub {
on { getWiFiCallingMode(false) } doReturn ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED
}
val wiFiCallingMode = repository.getWiFiCallingMode()
assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
}
@Test
fun getWiFiCallingMode_notRoaming_returnHomeSetting() {
mockTelephonyManager.stub {
on { isNetworkRoaming } doReturn false
}
mockImsMmTelRepository.stub {
on { getWiFiCallingMode(false) } doReturn ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED
}
val wiFiCallingMode = repository.getWiFiCallingMode()
assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
}
private fun mockUseWfcHomeModeForRoaming(config: Boolean) {
mockCarrierConfigManager.stub {
on {
getConfigForSubId(SUB_ID, KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)
} doReturn persistableBundleOf(
KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL to config,
)
}
}
private companion object {
const val SUB_ID = 1
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.settings.spa
import android.content.Context
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.spa.SpaActivity.Companion.isSuwAndPageBlocked
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.google.android.setupcompat.util.WizardManagerHelper
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class SpaActivityTest {
private lateinit var mockSession: MockitoSession
private val context = mock<Context>()
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(WizardManagerHelper::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.applicationContext).thenReturn(context)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun isSuwAndPageBlocked_regularPage_notBlocked() {
val isBlocked = context.isSuwAndPageBlocked(AllAppListPageProvider.name)
assertThat(isBlocked).isFalse()
}
@Test
fun isSuwAndPageBlocked_suwBlockedPageInSuw_blocked() {
whenever(WizardManagerHelper.isDeviceProvisioned(context)).thenReturn(false)
val isBlocked = context.isSuwAndPageBlocked(AppInfoSettingsProvider.name)
assertThat(isBlocked).isTrue()
}
@Test
fun isSuwAndPageBlocked_SuwBlockedPageNotInSuw_notBlocked() {
whenever(WizardManagerHelper.isDeviceProvisioned(context)).thenReturn(true)
val isBlocked = context.isSuwAndPageBlocked(AppInfoSettingsProvider.name)
assertThat(isBlocked).isFalse()
}
@Test
fun startSpaActivity() {
context.startSpaActivity(DESTINATION)
val intent = argumentCaptor<Intent> {
verify(context).startActivity(capture())
}.firstValue
assertThat(intent.component?.className).isEqualTo(SpaActivity::class.qualifiedName)
assertThat(intent.getStringExtra(KEY_DESTINATION)).isEqualTo(DESTINATION)
}
private companion object {
const val DESTINATION = "Destination"
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.settings.spa
import android.content.Intent
import android.net.Uri
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.spa.SpaAppBridgeActivity.Companion.getDestinationForApp
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SpaAppBridgeActivityTest {
@Test
fun getDestinationForApp_hasPackageName() {
val intent = Intent().apply {
data = Uri.parse("package:${PACKAGE_NAME}")
}
val destination = getDestinationForApp(DESTINATION, intent)
assertThat(destination).isEqualTo("$DESTINATION/$PACKAGE_NAME/${UserHandle.myUserId()}")
}
@Test
fun getDestinationForApp_noPackageName() {
val intent = Intent()
val destination = getDestinationForApp(DESTINATION, intent)
assertThat(destination).isNull()
}
private companion object {
const val DESTINATION = "Destination"
const val PACKAGE_NAME = "package.name"
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.settings.spa
import android.app.Activity
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.SettingsActivity.META_DATA_KEY_HIGHLIGHT_MENU_KEY
import com.android.settings.spa.SpaDestination.Companion.META_DATA_KEY_DESTINATION
import com.android.settings.spa.SpaDestination.Companion.getDestination
import com.google.common.truth.Truth.assertThat
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
@RunWith(AndroidJUnit4::class)
class SpaDestinationTest {
private var activityMetadata: Bundle = bundleOf()
private val mockPackageManager = mock<PackageManager> {
on {
getActivityInfo(
eq(COMPONENT_NAME),
any<PackageManager.ComponentInfoFlags>()
)
} doAnswer {
ActivityInfo().apply { metaData = activityMetadata }
}
}
private val activity = mock<Activity> {
on { componentName } doReturn COMPONENT_NAME
on { packageManager } doReturn mockPackageManager
}
@Test
fun getDestination_noDestination_returnNull() {
activityMetadata = bundleOf()
val destination = activity.getDestination()
assertThat(destination).isNull()
}
@Test
fun getDestination_withoutHighlightMenuKey() {
activityMetadata = bundleOf(META_DATA_KEY_DESTINATION to DESTINATION)
val (destination, highlightMenuKey) = activity.getDestination()!!
assertThat(destination).isEqualTo(DESTINATION)
assertThat(highlightMenuKey).isNull()
}
@Test
fun getDestination_withHighlightMenuKey() {
activityMetadata = bundleOf(
META_DATA_KEY_DESTINATION to DESTINATION,
META_DATA_KEY_HIGHLIGHT_MENU_KEY to HIGHLIGHT_MENU_KEY,
)
val (destination, highlightMenuKey) = activity.getDestination()!!
assertThat(destination).isEqualTo(DESTINATION)
assertThat(highlightMenuKey).isEqualTo(HIGHLIGHT_MENU_KEY)
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val ACTIVITY_NAME = "ActivityName"
val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
const val DESTINATION = "Destination"
const val HIGHLIGHT_MENU_KEY = "apps"
}
}

View File

@@ -0,0 +1,304 @@
/*
* 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.settings.spa.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import androidx.compose.runtime.SideEffect
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.settings.R
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import com.android.settingslib.spaprivileged.template.app.AppListInput
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
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.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class AllAppListTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
private val packageManager = mock<PackageManager> {
on { getPackagesForUid(USER_ID) } doReturn arrayOf(PACKAGE_NAME)
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn packageManager
}
@Test
fun allAppListPageProvider_name() {
assertThat(AllAppListPageProvider.name).isEqualTo("AllAppList")
}
@Test
fun injectEntry_title() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.all_apps)).assertIsDisplayed()
}
@Test
fun injectEntry_onClick_navigate() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.all_apps)).performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith).isEqualTo("AllAppList")
}
private fun setInjectEntry() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
AllAppListPageProvider.buildInjectEntry().build().UiLayout()
}
}
}
@Test
fun title_displayed() {
composeTestRule.setContent {
AllAppListPage {}
}
composeTestRule.onNodeWithText(context.getString(R.string.all_apps)).assertIsDisplayed()
}
@Test
fun showInstantApps_isTrue() {
val input = getAppListInput()
assertThat(input.config.showInstantApps).isTrue()
}
@Test
fun item_labelDisplayed() {
setItemContent()
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun item_summaryDisplayed() {
setItemContent()
composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
}
@Test
fun item_onClick_navigate() {
setItemContent()
composeTestRule.onNodeWithText(LABEL).performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith)
.isEqualTo("AppInfoSettings/package.name/0")
}
@Test
fun allAppListModel_transform() = runTest {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val recordListFlow = listModel.transform(flowOf(USER_ID), flowOf(listOf(APP)))
val recordList = recordListFlow.firstWithTimeoutOrNull()!!
assertThat(recordList).hasSize(1)
assertThat(recordList[0].app).isSameInstanceAs(APP)
}
@Test
fun listModelGetSummary_regular() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
lateinit var summary: () -> String
composeTestRule.setContent {
summary = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
}
assertThat(summary()).isEqualTo(SUMMARY)
}
@Test
fun listModelGetSummary_emptyStorage() {
val listModel = AllAppListModel(context) { stateOf("") }
lateinit var summary: () -> String
composeTestRule.setContent {
summary = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
}
assertThat(summary()).isEqualTo(context.getPlaceholder())
}
@Test
fun listModelGetSummary_disabled() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val disabledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
enabled = false
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = disabledApp))
}
assertThat(summary()).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
}
@Test
fun listModelGetSummary_emptyStorageAndDisabled() {
val listModel = AllAppListModel(context) { stateOf("") }
val disabledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
enabled = false
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = disabledApp))
}
assertThat(summary())
.isEqualTo(context.getString(com.android.settingslib.R.string.disabled))
}
@Test
fun listModelGetSummary_notInstalled() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val notInstalledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = notInstalledApp))
}
assertThat(summary())
.isEqualTo("$SUMMARY${System.lineSeparator()}Not installed for this user")
}
@Test
fun allAppListModel_archivedApp() {
val app = mock<ApplicationInfo> {
on { loadUnbadgedIcon(any()) } doReturn UNBADGED_ICON
on { loadLabel(any()) } doReturn LABEL
}
app.isArchived = true
packageManager.stub {
on {
getApplicationInfoAsUser(PACKAGE_NAME, 0, USER_ID)
} doReturn app
}
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(AllAppListModel(context)) {
AppListItemModel(
record = AppRecordWithSize(app = app),
label = LABEL,
summary = { SUMMARY },
).AppItem()
}
}
}
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun allAppListModel_getSummaryWhenArchived() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val archivedApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = archivedApp))
}
assertThat(summary()).isEqualTo(SUMMARY)
}
private fun getAppListInput(): AppListInput<AppRecordWithSize> {
lateinit var input: AppListInput<AppRecordWithSize>
composeTestRule.setContent {
AllAppListPage {
SideEffect {
input = this
}
}
}
return input
}
private fun setItemContent() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(AllAppListModel(context)) {
AppListItemModel(
record = AppRecordWithSize(app = APP),
label = LABEL,
summary = { SUMMARY },
).AppItem()
}
}
}
}
private companion object {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
const val SUMMARY = "Summary"
val UNBADGED_ICON = mock<Drawable>()
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.settings.spa.app
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.UserHandle
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.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@RunWith(AndroidJUnit4::class)
class AppUtilTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Mock
private lateinit var context: Context
@Test
fun startUninstallActivity() {
context.startUninstallActivity(packageName = PACKAGE_NAME, userHandle = USER_HANDLE)
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), eq(USER_HANDLE))
val intent = intentCaptor.value
assertThat(intent.action).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE)
assertThat(intent.data).isEqualTo(Uri.parse("package:$PACKAGE_NAME"))
assertThat(intent.extras?.getBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS)).isFalse()
}
private companion object {
const val PACKAGE_NAME = "package.name"
val USER_HANDLE: UserHandle = UserHandle.of(0)
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.settings.spa.app
import android.content.Context
import androidx.compose.runtime.LaunchedEffect
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.settings.R
import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ResetAppPreferencesTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Test
fun resetAppPreferences_titleIsDisplayed() {
setResetAppPreferences()
composeTestRule.onNodeWithText(context.getString(R.string.reset_app_preferences))
.assertIsDisplayed()
}
private fun setResetAppPreferences() {
val fakeMoreOptionsScope = object : MoreOptionsScope() {
override fun dismiss() {}
}
composeTestRule.setContent {
fakeMoreOptionsScope.ResetAppPreferences {}
}
}
@Test
fun resetAppDialogPresenter_confirmButtonDisplayed() {
setAndOpenDialog()
composeTestRule.onNodeWithText(context.getString(R.string.reset_app_preferences_button))
.assertIsDisplayed()
}
@Test
fun resetAppDialogPresenter_titleDisplayed() {
setAndOpenDialog()
composeTestRule.onNodeWithText(context.getString(R.string.reset_app_preferences_title))
.assertIsDisplayed()
}
@Test
fun resetAppDialogPresenter_textDisplayed() {
setAndOpenDialog()
composeTestRule.onNodeWithText(context.getString(R.string.reset_app_preferences_desc))
.assertIsDisplayed()
}
private fun setAndOpenDialog() {
composeTestRule.setContent {
val dialogPresenter = rememberResetAppDialogPresenter()
LaunchedEffect(Unit) {
dialogPresenter.open()
}
}
}
}

View File

@@ -0,0 +1,279 @@
/*
* 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.settings.spa.app.specialaccess
import android.Manifest
import android.app.AppOpsManager
import android.content.Context
import android.content.pm.ApplicationInfo
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.model.app.IAppOpsController
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
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.Mockito.`when` as whenever
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class WifiControlAppListModelTest {
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock private lateinit var packageManagers: IPackageManagers
private lateinit var listModel: WifiControlAppListModel
@Before
fun setUp() {
listModel = WifiControlAppListModel(context, packageManagers)
}
@Test
fun transformItem_recordHasCorrectApp() {
val record = listModel.transformItem(APP)
assertThat(record.app).isSameInstanceAs(APP)
}
@Test
fun transformItem_hasRequestPermission() = runTest {
with(packageManagers) {
whenever(APP.hasRequestPermission(PM_CHANGE_WIFI_STATE)).thenReturn(true)
}
val record = listModel.transformItem(APP)
assertThat(record.hasRequestPermission).isTrue()
}
@Test
fun transformItem_notRequestPermission() = runTest {
with(packageManagers) {
whenever(APP.hasRequestPermission(PM_CHANGE_WIFI_STATE)).thenReturn(false)
}
val record = listModel.transformItem(APP)
assertThat(record.hasRequestPermission).isFalse()
}
@Test
fun transformItem_hasRequestNetworkSettingsPermission() = runTest {
with(packageManagers) {
whenever(APP.hasRequestPermission(PM_NETWORK_SETTINGS)).thenReturn(true)
}
val record = listModel.transformItem(APP)
assertThat(record.hasRequestBroaderPermission).isTrue()
}
@Test
fun transformItem_notRequestNetworkSettingsPermission() = runTest {
with(packageManagers) {
whenever(APP.hasRequestPermission(PM_NETWORK_SETTINGS)).thenReturn(false)
}
val record = listModel.transformItem(APP)
assertThat(record.hasRequestBroaderPermission).isFalse()
}
@Test
fun filter() = runTest {
val appNotRequestPermissionRecord =
AppOpPermissionRecord(
app = APP_NOT_REQUEST_PERMISSION,
hasRequestPermission = false,
hasRequestBroaderPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val appRequestedNetworkSettingsRecord =
AppOpPermissionRecord(
app = APP_REQUESTED_NETWORK_SETTINGS,
hasRequestPermission = true,
hasRequestBroaderPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
)
val recordListFlow =
listModel.filter(
flowOf(USER_ID),
flowOf(listOf(appNotRequestPermissionRecord, appRequestedNetworkSettingsRecord))
)
val recordList = checkNotNull(recordListFlow.firstWithTimeoutOrNull())
assertThat(recordList).containsExactly(appRequestedNetworkSettingsRecord)
}
@Test
fun isAllowed_networkSettingsShouldTrump() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = false,
hasRequestBroaderPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isTrue()
}
@Test
fun isAllowed_grantedChangeWifiState() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = true,
hasRequestBroaderPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ALLOWED),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isTrue()
}
@Test
fun isAllowed_notAllowed() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = true,
hasRequestBroaderPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_IGNORED),
)
val isAllowed = getIsAllowed(record)
assertThat(isAllowed).isFalse()
}
@Test
fun isChangeable_noRequestedPermission() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = false,
hasRequestBroaderPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isFalse()
}
@Test
fun isChangeable_notChangableWhenRequestedNetworkSettingPermissions() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = false,
hasRequestBroaderPermission = true,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isFalse()
}
@Test
fun isChangeable_changableWhenRequestedChangeWifiStatePermission() {
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = true,
hasRequestBroaderPermission = false,
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
)
val isChangeable = listModel.isChangeable(record)
assertThat(isChangeable).isTrue()
}
@Test
fun setAllowed_shouldCallController() {
val appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
val record =
AppOpPermissionRecord(
app = APP,
hasRequestPermission = true,
hasRequestBroaderPermission = false,
appOpsController = appOpsController,
)
listModel.setAllowed(record = record, newAllowed = true)
assertThat(appOpsController.setAllowedCalledWith).isTrue()
}
private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? {
lateinit var isAllowedState: () -> Boolean?
composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) }
return isAllowedState()
}
private companion object {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
const val PM_CHANGE_WIFI_STATE = Manifest.permission.CHANGE_WIFI_STATE
const val PM_NETWORK_SETTINGS = Manifest.permission.NETWORK_SETTINGS
val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME }
val APP_NOT_REQUEST_PERMISSION =
ApplicationInfo().apply { packageName = "app1.package.name" }
val APP_REQUESTED_NETWORK_SETTINGS =
ApplicationInfo().apply { packageName = "app2.package.name" }
val APP_REQUESTED_CHANGE_WIFI_STATE =
ApplicationInfo().apply { packageName = "app3.package.name" }
}
}
private class FakeAppOpsController(private val fakeMode: Int) : IAppOpsController {
var setAllowedCalledWith: Boolean? = null
override val mode = flowOf(fakeMode)
override fun setAllowed(allowed: Boolean) {
setAllowedCalledWith = allowed
}
override fun getMode() = fakeMode
}

View File

@@ -0,0 +1,212 @@
/*
* 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.settings.spa.app.appcompat
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER
import android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE
import android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE
import android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasTextExactly
import androidx.compose.ui.test.junit4.createComposeRule
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.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.applications.appcompat.UserAspectRatioDetails
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settings.testutils.TestDeviceConfig
import com.android.settingslib.spa.testutils.delay
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
/**
* To run this test: atest SettingsSpaUnitTests:UserAspectRatioAppPreferenceTest
*/
@RunWith(AndroidJUnit4::class)
class UserAspectRatioAppPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Spy
private val resources = context.resources
private val aspectRatioEnabledConfig =
TestDeviceConfig(NAMESPACE_WINDOW_MANAGER, "enable_app_compat_aspect_ratio_user_settings")
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var launcherApps: LauncherApps
@Mock
private lateinit var launcherActivities: List<LauncherActivityInfo>
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(UserAspectRatioDetails::class.java)
.mockStatic(AppInfoDashboardFragment::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.resources).thenReturn(resources)
whenever(context.packageManager).thenReturn(packageManager)
whenever(context.getSystemService(Context.LAUNCHER_APPS_SERVICE)).thenReturn(launcherApps)
whenever(launcherApps.getActivityList(anyString(), any())).thenReturn(launcherActivities)
// True is ignored but need this here or getBoolean will complain null object
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true)
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true)
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true)
}
@After
fun tearDown() {
aspectRatioEnabledConfig.reset()
mockSession.finishMocking()
}
@Test
fun whenConfigIsFalse_notDisplayed() {
setConfig(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCannotDisplayAspectRatioUi_notDisplayed() {
whenever(launcherActivities.isEmpty()).thenReturn(true)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCanDisplayAspectRatioUiAndConfigFalse_notDisplayed() {
setConfig(false)
whenever(launcherActivities.isEmpty()).thenReturn(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCannotDisplayAspectRatioUiAndConfigTrue_notDisplayed() {
setConfig(true)
whenever(launcherActivities.isEmpty()).thenReturn(true)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCanDisplayAspectRatioUiAndConfigTrue_Displayed() {
setConfig(true)
whenever(launcherActivities.isEmpty()).thenReturn(false)
setContent()
composeTestRule.onNode(
hasTextExactly(
context.getString(R.string.aspect_ratio_experimental_title),
context.getString(R.string.user_aspect_ratio_app_default)
),
).assertIsDisplayed().assertIsEnabled()
}
@Test
fun onClick_startActivity() {
setConfig(true)
whenever(launcherActivities.isEmpty()).thenReturn(false)
setContent()
composeTestRule.onRoot().performClick()
ExtendedMockito.verify {
AppInfoDashboardFragment.startAppInfoFragment(
UserAspectRatioDetails::class.java,
APP,
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}
}
private fun setConfig(enabled: Boolean) {
whenever(resources.getBoolean(
com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled
)).thenReturn(enabled)
aspectRatioEnabledConfig.override(enabled)
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
UserAspectRatioAppPreference(APP)
}
}
composeTestRule.delay()
}
private fun mockProperty(propertyName: String, value: Boolean) {
val prop = PackageManager.Property(
propertyName, value, PACKAGE_NAME, "" /* className */)
whenever(packageManager.getProperty(propertyName, PACKAGE_NAME)).thenReturn(prop)
}
private companion object {
const val PACKAGE_NAME = "com.test.mypackage"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
}
}

View File

@@ -0,0 +1,193 @@
/*
* 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.settings.spa.app.appcompat
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN
import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET
import android.os.Build
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.settings.R
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
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
/**
* To run this test: atest SettingsSpaUnitTests:UserAspectRatioAppsPageProviderTest
*/
@RunWith(AndroidJUnit4::class)
class UserAspectRatioAppsPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
@Test
fun aspectRatioAppsPageProvider_name() {
assertThat(UserAspectRatioAppsPageProvider.name).isEqualTo(EXPECTED_PROVIDER_NAME)
}
@Test
fun injectEntry_title() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_experimental_title))
.assertIsDisplayed()
}
@Test
fun injectEntry_summary() {
setInjectEntry()
composeTestRule
.onNodeWithText(context.getString(R.string.aspect_ratio_summary_text, Build.MODEL))
.assertIsDisplayed()
}
@Test
fun injectEntry_onClick_navigate() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_experimental_title))
.performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith).isEqualTo("UserAspectRatioAppsPage")
}
private fun setInjectEntry() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
UserAspectRatioAppsPageProvider.buildInjectEntry().build().UiLayout()
}
}
}
@Test
fun title_displayed() {
composeTestRule.setContent {
UserAspectRatioAppList {}
}
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_experimental_title))
.assertIsDisplayed()
}
@Test
fun item_labelDisplayed() {
setItemContent()
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun aspectRatioAppListModel_transform() = runTest {
val listModel = UserAspectRatioAppListModel(context)
val recordListFlow = listModel.transform(flowOf(USER_ID), flowOf(listOf(APP)))
val recordList = recordListFlow.firstWithTimeoutOrNull()!!
assertThat(recordList).hasSize(1)
assertThat(recordList[0].app).isSameInstanceAs(APP)
}
@Test
fun aspectRatioAppListModel_filter() = runTest {
val listModel = UserAspectRatioAppListModel(context)
val recordListFlow = listModel.filter(flowOf(USER_ID), 0,
flowOf(listOf(APP_RECORD_NOT_DISPLAYED, APP_RECORD_SUGGESTED)))
val recordList = checkNotNull(recordListFlow.firstWithTimeoutOrNull())
assertThat(recordList).containsExactly(APP_RECORD_SUGGESTED)
}
private fun setItemContent() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(UserAspectRatioAppListModel(context)) {
AppListItemModel(
record = APP_RECORD_SUGGESTED,
label = LABEL,
summary = { SUMMARY }
).AppItem()
}
}
}
}
@Test
fun aspectRatioAppListModel_getSummaryDefault() {
val summary = getSummary(USER_MIN_ASPECT_RATIO_UNSET)
assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
}
@Test
fun aspectRatioAppListModel_getSummaryWhenSplitScreen() {
val summary = getSummary(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
}
private fun getSummary(userOverride: Int): String {
val listModel = UserAspectRatioAppListModel(context)
lateinit var summary: () -> String
composeTestRule.setContent {
summary = listModel.getSummary(option = 0,
record = UserAspectRatioAppListItemModel(
app = APP,
userOverride = userOverride,
suggested = false,
canDisplay = true,
))
}
return summary()
}
private companion object {
private const val EXPECTED_PROVIDER_NAME = "UserAspectRatioAppsPage"
private const val PACKAGE_NAME = "package.name"
private const val USER_ID = 0
private const val LABEL = "Label"
private const val SUMMARY = "Summary"
private val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
private val APP_RECORD_SUGGESTED = UserAspectRatioAppListItemModel(
APP,
userOverride = USER_MIN_ASPECT_RATIO_UNSET,
suggested = true,
canDisplay = true
)
private val APP_RECORD_NOT_DISPLAYED = UserAspectRatioAppListItemModel(
APP,
userOverride = USER_MIN_ASPECT_RATIO_UNSET,
suggested = true,
canDisplay = false
)
}
}

View File

@@ -0,0 +1,193 @@
/*
* 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.settings.spa.app.appinfo
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
import android.content.pm.ResolveInfo
import android.content.res.Resources
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
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.core.os.bundleOf
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.model.app.userId
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.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.doThrow
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppAllServicesPreferenceTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var resources: Resources
@Before
fun setUp() {
whenever(context.packageManager).thenReturn(packageManager)
whenever(packageManager.getResourcesForApplication(APP)).thenReturn(resources)
doThrow(Resources.NotFoundException()).`when`(resources).getString(anyInt())
}
private fun mockResolveActivityAsUser(resolveInfo: ResolveInfo?) {
whenever(
packageManager.resolveActivityAsUser(any(), any<ResolveInfoFlags>(), eq(APP.userId))
).thenReturn(resolveInfo)
}
@Test
fun callResolveActivityAsUser_withIntent() {
mockResolveActivityAsUser(null)
setContent()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(packageManager).resolveActivityAsUser(
intentCaptor.capture(), any<ResolveInfoFlags>(), eq(APP.userId)
)
val intent = intentCaptor.value
assertThat(intent.action).isEqualTo(Intent.ACTION_VIEW_APP_FEATURES)
assertThat(intent.`package`).isEqualTo(PACKAGE_NAME)
}
@Test
fun noResolveInfo_notDisplayed() {
mockResolveActivityAsUser(null)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun noAllServicesActivity_notDisplayed() {
mockResolveActivityAsUser(ResolveInfo())
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun hasAllServicesActivity_displayed() {
mockResolveActivityAsUser(RESOLVE_INFO)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.app_info_all_services_label))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun hasSummary() {
mockResolveActivityAsUser(RESOLVE_INFO)
doReturn(SUMMARY).`when`(resources).getString(SUMMARY_RES_ID)
setContent()
composeTestRule.waitUntilExists(hasText(SUMMARY))
}
@Test
fun whenClick_startActivity() {
mockResolveActivityAsUser(RESOLVE_INFO)
setContent()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
val intent = intentCaptor.value
assertThat(intent.action).isEqualTo(Intent.ACTION_VIEW_APP_FEATURES)
assertThat(intent.component).isEqualTo(ComponentName(PACKAGE_NAME, ACTIVITY_NAME))
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppAllServicesPreference(APP)
}
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "packageName"
const val ACTIVITY_NAME = "activityName"
const val UID = 123
const val SUMMARY_RES_ID = 456
const val SUMMARY = "summary"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
val RESOLVE_INFO = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME
name = ACTIVITY_NAME
metaData = bundleOf(
"app_features_preference_summary" to SUMMARY_RES_ID
)
}
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CloudUpload
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.widget.button.ActionButton
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppArchiveButtonTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
private val packageInfoPresenter = mock<PackageInfoPresenter>()
private val userPackageManager = mock<PackageManager>()
private val packageInstaller = mock<PackageInstaller>()
private val isHibernationSwitchEnabledStateFlow = MutableStateFlow(true)
private lateinit var appArchiveButton: AppArchiveButton
@Before
fun setUp() {
whenever(packageInfoPresenter.context).thenReturn(context)
whenever(packageInfoPresenter.userPackageManager).thenReturn(userPackageManager)
whenever(userPackageManager.packageInstaller).thenReturn(packageInstaller)
whenever(userPackageManager.getApplicationLabel(any())).thenReturn(APP_LABEL)
whenever(packageInfoPresenter.packageName).thenReturn(PACKAGE_NAME)
appArchiveButton =
AppArchiveButton(packageInfoPresenter, isHibernationSwitchEnabledStateFlow)
}
@Test
fun appArchiveButton_whenIsArchived_isDisabled() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
val actionButton = setContent(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun appArchiveButton_whenIsNotAppArchivable_isDisabled() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
}
whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(false)
val actionButton = setContent(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun appArchiveButton_whenIsHibernationSwitchDisabled_isDisabled() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
flags = ApplicationInfo.FLAG_INSTALLED
}
whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
isHibernationSwitchEnabledStateFlow.value = false
val enabledActionButton = setContent(app)
assertThat(enabledActionButton.enabled).isFalse()
}
@Test
fun appArchiveButton_displaysRightTextAndIcon() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
}
whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
val actionButton = setContent(app)
assertThat(actionButton.text).isEqualTo(context.getString(R.string.archive))
assertThat(actionButton.imageVector).isEqualTo(Icons.Outlined.CloudUpload)
}
@Test
fun appArchiveButton_clicked() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
}
whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
val actionButton = setContent(app)
actionButton.onClick()
verify(packageInstaller).requestArchive(
eq(PACKAGE_NAME),
any()
)
}
private fun setContent(app: ApplicationInfo): ActionButton {
lateinit var actionButton: ActionButton
composeTestRule.setContent {
actionButton = appArchiveButton.getActionButton(app)
}
composeTestRule.delay()
return actionButton
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val APP_LABEL = "App label"
}
}

View File

@@ -0,0 +1,189 @@
/*
* 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.settings.spa.app.appinfo
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.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasTextExactly
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.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
import com.android.settingslib.spaprivileged.model.app.userId
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppBatteryPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Spy
private val resources = context.resources
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(BatteryChartPreferenceController::class.java)
.mockStatic(AdvancedPowerUsageDetail::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.resources).thenReturn(resources)
whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
.thenReturn(true)
}
private fun mockBatteryDiffEntry(batteryDiffEntry: BatteryDiffEntry?) {
whenever(BatteryChartPreferenceController.getAppBatteryUsageData(
context, PACKAGE_NAME, APP.userId
)).thenReturn(batteryDiffEntry)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun whenConfigIsFalse_notDisplayed() {
whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
.thenReturn(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenAppNotInstalled_noSummary() {
val notInstalledApp = ApplicationInfo()
setContent(notInstalledApp)
composeTestRule.onNode(hasTextExactly(context.getString(R.string.battery_details_title)))
.assertIsDisplayed()
.assertIsNotEnabled()
}
@Test
fun batteryDiffEntryIsNull() {
mockBatteryDiffEntry(null)
setContent()
composeTestRule.onNode(
hasTextExactly(
context.getString(R.string.battery_details_title),
context.getString(R.string.no_battery_summary),
),
).assertIsDisplayed().assertIsEnabled()
}
@Test
fun noConsumePower() {
val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
mConsumePower = 0.0
}
mockBatteryDiffEntry(batteryDiffEntry)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.no_battery_summary))
.assertIsDisplayed()
}
@Test
fun hasConsumePower() {
val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
mConsumePower = 12.3
}
whenever(batteryDiffEntry.percentage).thenReturn(45.6)
mockBatteryDiffEntry(batteryDiffEntry)
setContent()
composeTestRule.onNodeWithText("46% use since last full charge").assertIsDisplayed()
}
@Test
fun whenClick_openDetailsPage() {
val batteryDiffEntry = mock(BatteryDiffEntry::class.java)
whenever(batteryDiffEntry.percentage).thenReturn(10.0)
mockBatteryDiffEntry(batteryDiffEntry)
setContent()
composeTestRule.onRoot().performClick()
ExtendedMockito.verify {
AdvancedPowerUsageDetail.startBatteryDetailPage(
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
batteryDiffEntry,
"10%",
null,
false,
null,
null
)
}
}
private fun setContent(app: ApplicationInfo = APP) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppBatteryPreference(app)
}
}
}
private companion object {
const val PACKAGE_NAME = "packageName"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
flags = ApplicationInfo.FLAG_INSTALLED
}
}
}

View File

@@ -0,0 +1,209 @@
/*
* 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.settings.spa.app.appinfo
import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.Flags
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.platform.test.annotations.RequiresFlagsEnabled
import androidx.core.os.bundleOf
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
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.Mockito.any
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppButtonRepositoryTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
private lateinit var appButtonRepository: AppButtonRepository
@Before
fun setUp() {
whenever(context.packageManager).thenReturn(packageManager)
appButtonRepository = AppButtonRepository(context)
}
private fun mockGetHomeActivities(
homeActivities: List<ResolveInfo>,
currentDefaultHome: ComponentName? = null,
) {
whenever(packageManager.getHomeActivities(any())).then {
@Suppress("UNCHECKED_CAST")
(it.arguments[0] as ArrayList<ResolveInfo>).addAll(homeActivities)
currentDefaultHome
}
}
@Test
fun getHomePackageInfo_empty() {
mockGetHomeActivities(homeActivities = emptyList())
val homePackageInfo = appButtonRepository.getHomePackageInfo()
assertThat(homePackageInfo.homePackages).isEmpty()
assertThat(homePackageInfo.currentDefaultHome).isNull()
}
@Test
fun getHomePackageInfo_noActivityInfo() {
mockGetHomeActivities(homeActivities = listOf(ResolveInfo()))
val homePackageInfo = appButtonRepository.getHomePackageInfo()
assertThat(homePackageInfo.homePackages).isEmpty()
assertThat(homePackageInfo.currentDefaultHome).isNull()
}
@Test
fun getHomePackageInfo_oneHome() {
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO),
currentDefaultHome = COMPONENT_NAME,
)
val homePackageInfo = appButtonRepository.getHomePackageInfo()
assertThat(homePackageInfo.homePackages).containsExactly(PACKAGE_NAME)
assertThat(homePackageInfo.currentDefaultHome).isSameInstanceAs(COMPONENT_NAME)
}
@Test
fun getHomePackageInfo_homeAlternateSignatureMatch() {
mockGetHomeActivities(homeActivities = listOf(RESOLVE_INFO_WITH_ALTERNATE))
whenever(packageManager.checkSignatures(PACKAGE_NAME_ALTERNATE, PACKAGE_NAME))
.thenReturn(PackageManager.SIGNATURE_MATCH)
val homePackageInfo = appButtonRepository.getHomePackageInfo()
assertThat(homePackageInfo.homePackages).containsExactly(
PACKAGE_NAME, PACKAGE_NAME_ALTERNATE
)
}
@Test
fun getHomePackageInfo_homeAlternateSignatureNoMatch() {
mockGetHomeActivities(homeActivities = listOf(RESOLVE_INFO_WITH_ALTERNATE))
whenever(packageManager.checkSignatures(PACKAGE_NAME_ALTERNATE, PACKAGE_NAME))
.thenReturn(PackageManager.SIGNATURE_NO_MATCH)
val homePackageInfo = appButtonRepository.getHomePackageInfo()
assertThat(homePackageInfo.homePackages).containsExactly(PACKAGE_NAME)
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
fun uninstallDisallowedDueToHomeApp_isNotSystemAndIsCurrentHomeAndHasOnlyOneHomeApp() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO),
currentDefaultHome = COMPONENT_NAME,
)
val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
assertThat(value).isTrue()
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
fun uninstallDisallowedDueToHomeApp_isNotSystemAndIsCurrentHomeAndHasOtherHomeApps() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO, RESOLVE_INFO_FAKE),
currentDefaultHome = COMPONENT_NAME,
)
val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
assertThat(value).isFalse()
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
fun uninstallDisallowedDueToHomeApp_isSystemAndIsCurrentHomeAndHasOtherHomeApps() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_SYSTEM
}
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO, RESOLVE_INFO_FAKE),
currentDefaultHome = COMPONENT_NAME,
)
val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
assertThat(value).isTrue()
}
private companion object {
const val PACKAGE_NAME = "packageName"
const val PACKAGE_NAME_ALTERNATE = "packageName.alternate"
const val PACKAGE_NAME_FAKE = "packageName.fake"
const val ACTIVITY_NAME = "activityName"
val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
val RESOLVE_INFO = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME
}
}
val RESOLVE_INFO_FAKE = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME_FAKE
}
}
val RESOLVE_INFO_WITH_ALTERNATE = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME
metaData = bundleOf(
ActivityManager.META_HOME_ALTERNATE to PACKAGE_NAME_ALTERNATE,
)
}
}
}
}

View File

@@ -0,0 +1,204 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.FakeFeatureFlagsImpl
import android.content.pm.Flags
import android.content.pm.PackageInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.platform.test.flag.junit.SetFlagsRule
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.flags.Flags as SettingsFlags
import com.android.settingslib.applications.AppUtils
import com.android.settingslib.spa.testutils.delay
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.kotlin.any
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppButtonsTest {
@get:Rule
val composeTestRule = createComposeRule()
@get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageInfoPresenter: PackageInfoPresenter
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var packageInstaller: PackageInstaller
private val featureFlags = FakeFeatureFlagsImpl()
private val isHibernationSwitchEnabledStateFlow = MutableStateFlow(true)
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(AppUtils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(packageInfoPresenter.context).thenReturn(context)
whenever(packageInfoPresenter.packageName).thenReturn(PACKAGE_NAME)
whenever(packageInfoPresenter.userPackageManager).thenReturn(packageManager)
whenever(packageManager.getApplicationLabel(any())).thenReturn(APP_LABEL)
whenever(packageManager.packageInstaller).thenReturn(packageInstaller)
whenever(packageManager.getPackageInfo(PACKAGE_NAME, 0)).thenReturn(PACKAGE_INFO)
whenever(AppUtils.isMainlineModule(packageManager, PACKAGE_NAME)).thenReturn(false)
featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun isMainlineModule_notDisplayed() {
whenever(AppUtils.isMainlineModule(packageManager, PACKAGE_NAME)).thenReturn(true)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun isNormalApp_displayed() {
setContent()
composeTestRule.onRoot().assertIsDisplayed()
}
@Test
fun launchButton_displayed_archivingDisabled() {
whenever(packageManager.getLaunchIntentForPackage(PACKAGE_NAME)).thenReturn(Intent())
featureFlags.setFlag(Flags.FLAG_ARCHIVING, false)
setFlagsRule.disableFlags(SettingsFlags.FLAG_APP_ARCHIVING)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.launch_instant_app))
.assertIsDisplayed()
}
@Test
fun launchButton_notDisplayed_archivingEnabled() {
whenever(packageManager.getLaunchIntentForPackage(PACKAGE_NAME)).thenReturn(Intent())
featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.launch_instant_app))
.assertIsNotDisplayed()
}
@Test
fun uninstallButton_enabled_whenAppIsArchived() {
whenever(packageManager.getLaunchIntentForPackage(PACKAGE_NAME)).thenReturn(Intent())
featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
val packageInfo = PackageInfo().apply {
applicationInfo = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
packageName = PACKAGE_NAME
}
setContent(packageInfo)
composeTestRule.onNodeWithText(context.getString(R.string.uninstall_text)).assertIsEnabled()
}
@Test
fun archiveButton_displayed_whenAppIsNotArchived() {
featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
val packageInfo = PackageInfo().apply {
applicationInfo = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
}
packageName = PACKAGE_NAME
}
setContent(packageInfo)
composeTestRule.onNodeWithText(context.getString(R.string.archive)).assertIsDisplayed()
composeTestRule.onNodeWithText(context.getString(R.string.restore)).assertIsNotDisplayed()
}
@Test
fun restoreButton_displayed_whenAppIsArchived() {
featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
val packageInfo = PackageInfo().apply {
applicationInfo = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
packageName = PACKAGE_NAME
}
setContent(packageInfo)
composeTestRule.onNodeWithText(context.getString(R.string.restore)).assertIsDisplayed()
composeTestRule.onNodeWithText(context.getString(R.string.archive)).assertIsNotDisplayed()
}
private fun setContent(packageInfo: PackageInfo = PACKAGE_INFO) {
whenever(packageInfoPresenter.flow).thenReturn(MutableStateFlow(packageInfo))
composeTestRule.setContent {
AppButtons(packageInfoPresenter, isHibernationSwitchEnabledStateFlow, featureFlags)
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val APP_LABEL = "App label"
val PACKAGE_INFO = PackageInfo().apply {
applicationInfo = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
packageName = PACKAGE_NAME
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ApplicationInfo
import android.net.NetworkTemplate
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasText
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.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.datausage.AppDataUsage
import com.android.settings.datausage.lib.IAppDataUsageSummaryRepository
import com.android.settings.datausage.lib.INetworkTemplates
import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppDataUsagePreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
private val context: Context = ApplicationProvider.getApplicationContext()
private var networkUsageData: NetworkUsageData? = null
private inner class TestRepository : IAppDataUsageSummaryRepository {
override suspend fun querySummary(uid: Int): NetworkUsageData? = when (uid) {
UID -> networkUsageData
else -> null
}
}
@Before
fun setUp() {
mockSession = mockitoSession()
.initMocks(this)
.mockStatic(Utils::class.java)
.mockStatic(AppInfoDashboardFragment::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(Utils.isBandwidthControlEnabled()).thenReturn(true)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun whenBandwidthControlDisabled_notDisplayed() {
whenever(Utils.isBandwidthControlEnabled()).thenReturn(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenAppNotInstalled_disabled() {
val notInstalledApp = ApplicationInfo()
setContent(notInstalledApp)
composeTestRule.onNodeWithText(context.getString(R.string.cellular_data_usage))
.assertIsDisplayed()
.assertIsNotEnabled()
}
@Test
fun whenAppInstalled_enabled() {
setContent(APP)
composeTestRule.onNodeWithText(context.getString(R.string.cellular_data_usage))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun whenNoDataUsage() {
networkUsageData = null
setContent()
composeTestRule.waitUntilExists(hasText(context.getString(R.string.no_data_usage)))
}
@Test
fun whenHasDataUsage() {
networkUsageData = NetworkUsageData(
startTime = 1666666666666L,
endTime = 1666666666666L,
usage = 123L,
)
setContent()
composeTestRule.waitUntilExists(hasText("123 B used since Oct 25, 2022"))
}
@Test
fun whenClick_startActivity() {
setContent()
composeTestRule.onRoot().performClick()
ExtendedMockito.verify {
AppInfoDashboardFragment.startAppInfoFragment(
AppDataUsage::class.java,
APP,
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}
}
private fun setContent(app: ApplicationInfo = APP) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppDataUsagePreference(app, TestNetworkTemplates) { _, _ ->
TestRepository()
}
}
}
composeTestRule.delay()
}
private object TestNetworkTemplates : INetworkTemplates {
override fun getDefaultTemplate(context: Context): NetworkTemplate =
NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE).build()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
flags = ApplicationInfo.FLAG_INSTALLED
}
}
}

View File

@@ -0,0 +1,181 @@
/*
* 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.settings.spa.app.appinfo
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.compose.ui.test.junit4.createComposeRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.Utils
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settingslib.spa.widget.button.ActionButton
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.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import com.android.settingslib.Utils as SettingsLibUtils
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppDisableButtonTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageInfoPresenter: PackageInfoPresenter
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var userManager: UserManager
@Mock
private lateinit var devicePolicyManager: DevicePolicyManager
private val fakeFeatureFactory = FakeFeatureFactory()
private val appFeatureProvider = fakeFeatureFactory.mockApplicationFeatureProvider
private lateinit var appDisableButton: AppDisableButton
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(SettingsLibUtils::class.java)
.mockStatic(Utils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(packageInfoPresenter.context).thenReturn(context)
whenever(context.packageManager).thenReturn(packageManager)
whenever(context.userManager).thenReturn(userManager)
whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
whenever(appFeatureProvider.keepEnabledPackages).thenReturn(emptySet())
whenever(
SettingsLibUtils.isEssentialPackage(context.resources, packageManager, PACKAGE_NAME)
).thenReturn(false)
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(false)
appDisableButton = AppDisableButton(packageInfoPresenter)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun getActionButton_signedWithPlatformKey_cannotDisable() {
val app = enabledSystemApp {
privateFlags = privateFlags or ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
}
val actionButton = setDisableButton(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun getActionButton_isResourceOverlay_cannotDisable() {
val app = enabledSystemApp {
privateFlags = privateFlags or ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY
}
val actionButton = setDisableButton(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun getActionButton_isKeepEnabledPackages_cannotDisable() {
whenever(appFeatureProvider.keepEnabledPackages).thenReturn(setOf(PACKAGE_NAME))
val app = enabledSystemApp()
val actionButton = setDisableButton(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun getActionButton_isEssentialPackage_cannotDisable() {
whenever(
SettingsLibUtils.isEssentialPackage(context.resources, packageManager, PACKAGE_NAME)
).thenReturn(true)
val app = enabledSystemApp()
val actionButton = setDisableButton(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun getActionButton_isProfileOrDeviceOwner_cannotDisable() {
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(true)
val app = enabledSystemApp()
val actionButton = setDisableButton(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun getActionButton_regularEnabledSystemApp_canDisable() {
val app = enabledSystemApp()
val actionButton = setDisableButton(app)
assertThat(actionButton.enabled).isTrue()
}
private fun setDisableButton(app: ApplicationInfo): ActionButton {
lateinit var actionButton: ActionButton
composeTestRule.setContent {
actionButton = appDisableButton.getActionButton(app)!!
}
return actionButton
}
private fun enabledSystemApp(builder: ApplicationInfo.() -> Unit = {}) =
ApplicationInfo().apply {
packageName = PACKAGE_NAME
enabled = true
flags = ApplicationInfo.FLAG_SYSTEM
}.apply(builder)
private companion object {
const val PACKAGE_NAME = "package.name"
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.settings.spa.app.appinfo
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.UserHandle
import android.os.UserManager
import androidx.compose.material3.Button
import androidx.compose.material3.Text
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.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.model.app.userId
import com.google.common.truth.Truth.assertThat
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
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class AppForceStopButtonTest {
@get:Rule
val composeTestRule = createComposeRule()
private val mockPackageManager = mock<PackageManager>()
private val mockDevicePolicyManager = mock<DevicePolicyManager>()
private val mockUserManager = mock<UserManager> {
on { getUserRestrictionSources(any(), any()) } doReturn emptyList()
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn mockPackageManager
on { devicePolicyManager } doReturn mockDevicePolicyManager
on { getSystemService(Context.DEVICE_POLICY_SERVICE) } doReturn mockDevicePolicyManager
on { getSystemService(Context.USER_SERVICE) } doReturn mockUserManager
}
private val packageInfoPresenter = mock<PackageInfoPresenter> {
on { context } doReturn context
}
private val appForceStopButton = AppForceStopButton(packageInfoPresenter)
@Test
fun getActionButton_isActiveAdmin_buttonDisabled() {
val app = createApp()
mockDevicePolicyManager.stub {
on { packageHasActiveAdmins(PACKAGE_NAME, app.userId) } doReturn true
}
setForceStopButton(app)
composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsNotEnabled()
}
@Test
fun getActionButton_isUninstallInQueue_buttonDisabled() {
val app = createApp()
mockDevicePolicyManager.stub {
on { isUninstallInQueue(PACKAGE_NAME) } doReturn true
}
setForceStopButton(app)
composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsNotEnabled()
}
@Test
fun getActionButton_isStopped_buttonDisabled() {
val app = createApp {
flags = ApplicationInfo.FLAG_STOPPED
}
setForceStopButton(app)
composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsNotEnabled()
}
@Test
fun getActionButton_regularApp_buttonEnabled() {
val app = createApp()
setForceStopButton(app)
composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsEnabled()
}
@Test
fun getAdminRestriction_packageNotProtected() {
mockPackageManager.stub {
on { isPackageStateProtected(PACKAGE_NAME, UserHandle.getUserId(UID)) } doReturn false
}
val admin = appForceStopButton.getAdminRestriction(createApp())
assertThat(admin).isNull()
}
@Test
fun getAdminRestriction_packageProtectedAndHaveOwner() {
mockPackageManager.stub {
on { isPackageStateProtected(PACKAGE_NAME, UserHandle.getUserId(UID)) } doReturn true
}
mockDevicePolicyManager.stub {
on { deviceOwnerComponentOnAnyUser } doReturn DEVICE_OWNER
}
val admin = appForceStopButton.getAdminRestriction(createApp())!!
assertThat(admin.component).isEqualTo(DEVICE_OWNER)
}
@Test
fun getAdminRestriction_packageProtectedAndNotHaveOwner() {
mockPackageManager.stub {
on { isPackageStateProtected(PACKAGE_NAME, UserHandle.getUserId(UID)) } doReturn true
}
mockDevicePolicyManager.stub {
on { deviceOwnerComponentOnAnyUser } doReturn null
}
val admin = appForceStopButton.getAdminRestriction(createApp())!!
assertThat(admin.component).isNull()
}
private fun setForceStopButton(app: ApplicationInfo) {
composeTestRule.setContent {
val actionButton = appForceStopButton.getActionButton(app)
Button(onClick = {}, enabled = actionButton.enabled) {
Text(actionButton.text)
}
}
composeTestRule.delay()
}
private fun createApp(builder: ApplicationInfo.() -> Unit = {}) =
ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
enabled = true
}.apply(builder)
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 10000
val DEVICE_OWNER = ComponentName("device", "Owner")
}
}

View File

@@ -0,0 +1,244 @@
/*
* 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.settings.spa.app.appinfo
import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.ecm.EnhancedConfirmationManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.os.UserManager
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
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.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.Utils
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.userId
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppInfoSettingsMoreOptionsTest {
@get:Rule
val composeTestRule = createComposeRule()
@get:Rule
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageInfoPresenter: PackageInfoPresenter
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var userManager: UserManager
@Mock
private lateinit var devicePolicyManager: DevicePolicyManager
@Mock
private lateinit var appOpsManager: AppOpsManager
@Mock
private lateinit var enhancedConfirmationManager: EnhancedConfirmationManager
@Mock
private lateinit var keyguardManager: KeyguardManager
@Spy
private var resources = context.resources
@Mock
private lateinit var packageManagers: IPackageManagers
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(Utils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(packageInfoPresenter.context).thenReturn(context)
whenever(context.packageManager).thenReturn(packageManager)
whenever(context.userManager).thenReturn(userManager)
whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
whenever(context.appOpsManager).thenReturn(appOpsManager)
whenever(context.getSystemService(KeyguardManager::class.java)).thenReturn(keyguardManager)
whenever(context.getSystemService(EnhancedConfirmationManager::class.java))
.thenReturn(enhancedConfirmationManager)
whenever(keyguardManager.isKeyguardSecure).thenReturn(false)
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(false)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun whenProfileOrDeviceOwner_notDisplayed() {
whenever(Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, PACKAGE_NAME))
.thenReturn(true)
setContent(ApplicationInfo())
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun uninstallUpdates_updatedSystemAppAndUserAdmin_displayed() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
flags = ApplicationInfo.FLAG_SYSTEM or ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
}
whenever(userManager.isUserAdmin(app.userId)).thenReturn(true)
whenever(resources.getBoolean(R.bool.config_disable_uninstall_update)).thenReturn(false)
setContent(app)
composeTestRule.onRoot().performClick()
composeTestRule.waitUntilExists(hasText(context.getString(R.string.app_factory_reset)))
}
@Test
fun uninstallForAllUsers_regularAppAndPrimaryUser_displayed() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
whenever(userManager.aliveUsers).thenReturn(listOf(OTHER_USER))
whenever(packageManagers.isPackageInstalledAsUser(PACKAGE_NAME, OTHER_USER_ID))
.thenReturn(true)
setContent(app)
composeTestRule.onRoot().performClick()
composeTestRule.waitUntilExists(
hasText(context.getString(R.string.uninstall_all_users_text))
)
}
@Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
fun shouldShowAccessRestrictedSettings_appOp() {
whenever(
appOpsManager.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, UID, PACKAGE_NAME, null, null
)
).thenReturn(AppOpsManager.MODE_IGNORED)
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
setContent(app)
composeTestRule.onRoot().performClick()
composeTestRule.waitUntilExists(
hasText(context.getString(R.string.app_restricted_settings_lockscreen_title))
)
composeTestRule
.onNodeWithText(context.getString(R.string.app_restricted_settings_lockscreen_title))
.performClick()
verify(appOpsManager).setMode(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
UID,
PACKAGE_NAME,
AppOpsManager.MODE_ALLOWED,
)
}
@Test
@RequiresFlagsEnabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
fun shouldShowAccessRestrictedSettings() {
whenever(
enhancedConfirmationManager.isClearRestrictionAllowed(PACKAGE_NAME)
).thenReturn(true)
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
setContent(app)
composeTestRule.onRoot().performClick()
composeTestRule.waitUntilExists(
hasText(context.getString(R.string.app_restricted_settings_lockscreen_title))
)
composeTestRule
.onNodeWithText(context
.getString(R.string.app_restricted_settings_lockscreen_title))
.performClick()
verify(enhancedConfirmationManager).clearRestriction(PACKAGE_NAME)
}
private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppInfoSettingsMoreOptions(packageInfoPresenter, app, packageManagers)
}
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 123
const val OTHER_USER_ID = 10
val OTHER_USER = UserInfo(OTHER_USER_ID, "Other user", 0)
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.isEnabled
import androidx.compose.ui.test.isNotEnabled
import androidx.compose.ui.test.junit4.createComposeRule
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.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.applications.AppStoreUtil
import com.android.settingslib.applications.AppUtils
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.model.app.userHandle
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.eq
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class AppInstallerInfoPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivityAsUser(any(), any())
}
@Before
fun setUp() {
mockSession = mockitoSession()
.initMocks(this)
.mockStatic(AppStoreUtil::class.java)
.mockStatic(Utils::class.java)
.mockStatic(AppUtils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(AppStoreUtil.getInstallerPackageName(any(), eq(PACKAGE_NAME)))
.thenReturn(INSTALLER_PACKAGE_NAME)
whenever(AppStoreUtil.getAppStoreLink(context, INSTALLER_PACKAGE_NAME, PACKAGE_NAME))
.thenReturn(STORE_LINK)
whenever(Utils.getApplicationLabel(context, INSTALLER_PACKAGE_NAME))
.thenReturn(INSTALLER_PACKAGE_LABEL)
whenever(AppUtils.isMainlineModule(any(), eq(PACKAGE_NAME))).thenReturn(false)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun whenNoInstaller_notDisplayed() {
whenever(AppStoreUtil.getInstallerPackageName(any(), eq(PACKAGE_NAME))).thenReturn(null)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenInstallerLabelIsNull_notDisplayed() {
whenever(Utils.getApplicationLabel(context, INSTALLER_PACKAGE_NAME)).thenReturn(null)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenIsMainlineModule_notDisplayed() {
whenever(AppUtils.isMainlineModule(any(), eq(PACKAGE_NAME))).thenReturn(true)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenStoreLinkIsNull_disabled() {
whenever(AppStoreUtil.getAppStoreLink(context, INSTALLER_PACKAGE_NAME, PACKAGE_NAME))
.thenReturn(null)
setContent()
composeTestRule.waitUntilExists(preferenceNode.and(isNotEnabled()))
}
@Test
fun whenIsInstantApp_hasSummaryForInstant() {
val instantApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
}
setContent(instantApp)
composeTestRule.waitUntilExists(hasText("More info on installer label").and(isEnabled()))
}
@Test
fun whenNotInstantApp() {
setContent()
composeTestRule.waitUntilExists(hasText("App installed from installer label"))
composeTestRule.waitUntilExists(preferenceNode.and(isEnabled()))
}
@Test
fun whenClick_startActivity() {
setContent()
composeTestRule.delay()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
verify(context).startActivityAsUser(STORE_LINK, APP.userHandle)
}
private fun setContent(app: ApplicationInfo = APP) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppInstallerInfoPreference(app)
}
}
}
private val preferenceNode = hasText(context.getString(R.string.app_install_details_title))
private companion object {
const val PACKAGE_NAME = "package.name"
const val INSTALLER_PACKAGE_NAME = "installer"
const val INSTALLER_PACKAGE_LABEL = "installer label"
val STORE_LINK = Intent("store/link")
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
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.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.settings.R
import com.android.settings.applications.AppInfoBase
import com.android.settings.applications.AppLocaleUtil
import com.android.settings.applications.appinfo.AppLocaleDetails
import com.android.settings.localepicker.AppLocalePickerActivity
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppLocalePreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
@Before
fun setUp() {
mockSession = mockitoSession()
.initMocks(this)
.mockStatic(AppLocaleUtil::class.java)
.mockStatic(AppLocaleDetails::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.packageManager).thenReturn(packageManager)
whenever(AppLocaleUtil.canDisplayLocaleUi(any(), eq(APP), any())).thenReturn(true)
whenever(AppLocaleDetails.getSummary(any(), eq(APP))).thenReturn(SUMMARY)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun whenCanNotDisplayLocalUi_notDisplayed() {
whenever(AppLocaleUtil.canDisplayLocaleUi(any(), eq(APP), any())).thenReturn(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCanDisplayLocalUi_displayed() {
setContent()
composeTestRule.waitUntilExists(
hasText(context.getString(R.string.app_locale_preference_title))
)
composeTestRule.waitUntilExists(hasText(SUMMARY))
}
@Test
fun whenCanDisplayLocalUi_click_startActivity() {
doNothing().`when`(context).startActivityAsUser(any(), any())
setContent()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
val intent = intentCaptor.value
assertThat(intent.component?.className)
.isEqualTo(AppLocalePickerActivity::class.qualifiedName)
assertThat(intent.getIntExtra(AppInfoBase.ARG_PACKAGE_UID, -1)).isEqualTo(UID)
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppLocalePreference(APP)
}
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
const val SUMMARY = "summary"
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.settings.spa.app.appinfo
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.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.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.notification.app.AppNotificationSettings
import com.android.settings.spa.notification.IAppNotificationRepository
import com.android.settingslib.spa.testutils.delay
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class AppNotificationPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
private val repository = object : IAppNotificationRepository {
override fun getNotificationSummary(app: ApplicationInfo) = SUMMARY
}
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(AppInfoDashboardFragment::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun title_displayed() {
setContent(APP)
composeTestRule.onNodeWithText(context.getString(R.string.notifications_label))
.assertIsDisplayed()
}
@Test
fun summary_displayed() {
setContent(APP)
composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
}
@Test
fun whenNotInstalled_disable() {
setContent(ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
})
composeTestRule.onNodeWithText(context.getString(R.string.notifications_label))
.assertIsNotEnabled()
}
@Test
fun onClick_startActivity() {
setContent(APP)
composeTestRule.onRoot().performClick()
composeTestRule.delay()
ExtendedMockito.verify {
AppInfoDashboardFragment.startAppInfoFragment(
AppNotificationSettings::class.java,
APP,
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}
}
private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppNotificationPreference(app = app, repository = repository)
}
}
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
flags = ApplicationInfo.FLAG_INSTALLED
}
const val SUMMARY = "Summary"
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationUserState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
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.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.testutils.mockAsUser
import com.android.settingslib.spaprivileged.framework.common.domainVerificationManager
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppOpenByDefaultPreferenceTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var domainVerificationManager: DomainVerificationManager
@Mock
private lateinit var allowedUserState: DomainVerificationUserState
@Mock
private lateinit var notAllowedUserState: DomainVerificationUserState
@Before
fun setUp() {
whenever(context.packageManager).thenReturn(packageManager)
context.mockAsUser()
whenever(context.domainVerificationManager).thenReturn(domainVerificationManager)
whenever(allowedUserState.isLinkHandlingAllowed).thenReturn(true)
whenever(notAllowedUserState.isLinkHandlingAllowed).thenReturn(false)
}
@Test
fun instantApp_notDisplay() {
val instantApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
}
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppOpenByDefaultPreference(instantApp)
}
}
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun browserApp_notDisplay() {
val browserApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
}
val resolveInfo = ResolveInfo().apply {
activityInfo = ActivityInfo()
handleAllWebDataURI = true
}
whenever(packageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt()))
.thenReturn(listOf(resolveInfo))
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppOpenByDefaultPreference(browserApp)
}
}
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun allowedUserState_alwaysOpen() {
whenever(domainVerificationManager.getDomainVerificationUserState(PACKAGE_NAME))
.thenReturn(allowedUserState)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppOpenByDefaultPreference(INSTALLED_ENABLED_APP)
}
}
composeTestRule.onNodeWithText(context.getString(R.string.launch_by_default))
.assertIsDisplayed()
.assertIsEnabled()
composeTestRule.onNodeWithText(context.getString(R.string.app_link_open_always))
.assertIsDisplayed()
}
@Test
fun notAllowedUserState_neverOpen() {
whenever(domainVerificationManager.getDomainVerificationUserState(PACKAGE_NAME))
.thenReturn(notAllowedUserState)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppOpenByDefaultPreference(INSTALLED_ENABLED_APP)
}
}
composeTestRule.onNodeWithText(context.getString(R.string.launch_by_default))
.assertIsDisplayed()
.assertIsEnabled()
composeTestRule.onNodeWithText(context.getString(R.string.app_link_open_never))
.assertIsDisplayed()
}
@Test
fun notInstalledApp_disabled() {
val notInstalledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppOpenByDefaultPreference(notInstalledApp)
}
}
composeTestRule.onNodeWithText(context.getString(R.string.launch_by_default))
.assertIsNotEnabled()
}
@Test
fun notEnabledApp_disabled() {
val notEnabledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
enabled = false
}
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppOpenByDefaultPreference(notEnabledApp)
}
}
composeTestRule.onNodeWithText(context.getString(R.string.launch_by_default))
.assertIsNotEnabled()
}
private companion object {
const val PACKAGE_NAME = "package name"
val INSTALLED_ENABLED_APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
enabled = true
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.app.appinfo
import android.content.Context
import android.content.Intent
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.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
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.doNothing
import org.mockito.kotlin.eq
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppPermissionPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivityAsUser(any(), any())
}
@Test
fun title_display() {
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.permissions_label))
.assertIsDisplayed()
}
@Test
fun whenClick_startActivity() {
setContent()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
val intent = argumentCaptor {
verify(context).startActivityAsUser(capture(), eq(APP.userHandle))
}.firstValue
assertThat(intent.action).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSIONS)
assertThat(intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)).isEqualTo(PACKAGE_NAME)
assertThat(intent.getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)).isEqualTo(true)
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppPermissionPreference(
app = APP,
summaryFlow = flowOf(
AppPermissionSummaryState(summary = SUMMARY, enabled = true)
),
)
}
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val SUMMARY = "Summary"
private const val EXTRA_HIDE_INFO_BUTTON = "hideInfoButton"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.settings.spa.app.appinfo
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.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.settings.R
import com.android.settings.testutils.mockAsUser
import com.android.settingslib.applications.PermissionsSummaryHelper
import com.android.settingslib.applications.PermissionsSummaryHelper.PermissionsResultCallback
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoSession
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@RunWith(AndroidJUnit4::class)
class AppPermissionSummaryTest {
private lateinit var mockSession: MockitoSession
private val mockPackageManager = mock<PackageManager>()
private var context: Context = spy(ApplicationProvider.getApplicationContext()) {
mock.mockAsUser()
on { packageManager } doReturn mockPackageManager
}
private val summaryRepository = AppPermissionSummaryRepository(context, APP)
@Before
fun setUp() {
mockSession = mockitoSession()
.mockStatic(PermissionsSummaryHelper::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
}
private fun mockGetPermissionSummary(
requestedPermissionCount: Int = 0,
additionalGrantedPermissionCount: Int = 0,
grantedGroupLabels: List<CharSequence> = emptyList(),
) {
whenever(PermissionsSummaryHelper.getPermissionSummary(any(), eq(PACKAGE_NAME), any()))
.thenAnswer {
val callback = it.arguments[2] as PermissionsResultCallback
callback.onPermissionSummaryResult(
requestedPermissionCount,
additionalGrantedPermissionCount,
grantedGroupLabels,
)
}
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun summary_noPermissionsRequested() = runBlocking {
mockGetPermissionSummary(requestedPermissionCount = 0)
val (summary, enabled) = summaryRepository.flow.first()
assertThat(summary).isEqualTo(
context.getString(R.string.runtime_permissions_summary_no_permissions_requested)
)
assertThat(enabled).isFalse()
}
@Test
fun summary_noPermissionsGranted() = runBlocking {
mockGetPermissionSummary(requestedPermissionCount = 1, grantedGroupLabels = emptyList())
val (summary, enabled) = summaryRepository.flow.first()
assertThat(summary).isEqualTo(
context.getString(R.string.runtime_permissions_summary_no_permissions_granted)
)
assertThat(enabled).isTrue()
}
@Test
fun summary_hasRuntimePermission_usePermissionAsSummary() = runBlocking {
mockGetPermissionSummary(
requestedPermissionCount = 1,
grantedGroupLabels = listOf(PERMISSION),
)
val (summary, enabled) = summaryRepository.flow.first()
assertThat(summary).isEqualTo(PERMISSION)
assertThat(enabled).isTrue()
}
@Test
fun summary_hasAdditionalPermission_containsAdditionalSummary() = runBlocking {
mockGetPermissionSummary(
requestedPermissionCount = 5,
additionalGrantedPermissionCount = 2,
grantedGroupLabels = listOf(PERMISSION),
)
val (summary, enabled) = summaryRepository.flow.first()
assertThat(summary).isEqualTo("Storage and 2 additional permissions")
assertThat(enabled).isTrue()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val PERMISSION = "Storage"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CloudDownload
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spa.widget.button.ActionButton
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.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppRestoreButtonTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
private val packageInfoPresenter = mock<PackageInfoPresenter>()
private val userPackageManager = mock<PackageManager>()
private val packageInstaller = mock<PackageInstaller>()
private lateinit var appRestoreButton: AppRestoreButton
@Before
fun setUp() {
whenever(packageInfoPresenter.context).thenReturn(context)
whenever(packageInfoPresenter.userPackageManager).thenReturn(userPackageManager)
whenever(userPackageManager.packageInstaller).thenReturn(packageInstaller)
whenever(packageInfoPresenter.packageName).thenReturn(PACKAGE_NAME)
appRestoreButton = AppRestoreButton(packageInfoPresenter)
}
@Test
fun appRestoreButton_whenIsNotArchived_isDisabled() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
}
val actionButton = setContent(app)
assertThat(actionButton.enabled).isFalse()
}
@Test
fun appRestoreButton_whenIsArchived_isEnabled() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
val actionButton = setContent(app)
assertThat(actionButton.enabled).isTrue()
}
@Test
fun appRestoreButton_displaysRightTextAndIcon() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = false
}
val actionButton = setContent(app)
assertThat(actionButton.text).isEqualTo(context.getString(R.string.restore))
assertThat(actionButton.imageVector).isEqualTo(Icons.Outlined.CloudDownload)
}
@Test
@UiThreadTest
fun appRestoreButton_clicked() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
val actionButton = setContent(app)
actionButton.onClick()
verify(packageInstaller).requestUnarchive(
eq(PACKAGE_NAME),
any()
)
}
private fun setContent(app: ApplicationInfo): ActionButton {
lateinit var actionButton: ActionButton
composeTestRule.setContent {
actionButton = appRestoreButton.getActionButton(app)
}
return actionButton
}
private companion object {
const val PACKAGE_NAME = "package.name"
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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.settings.spa.app.appinfo
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
import android.content.pm.ResolveInfo
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
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.settings.R
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.model.app.userId
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.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppSettingsPreferenceTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
@Before
fun setUp() {
whenever(context.packageManager).thenReturn(packageManager)
}
private fun mockResolveActivityAsUser(resolveInfo: ResolveInfo?) {
whenever(
packageManager.resolveActivityAsUser(any(), any<ResolveInfoFlags>(), eq(APP.userId))
).thenReturn(resolveInfo)
}
@Test
fun callResolveActivityAsUser_withIntent() {
mockResolveActivityAsUser(null)
setContent()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(packageManager).resolveActivityAsUser(
intentCaptor.capture(), any<ResolveInfoFlags>(), eq(APP.userId)
)
val intent = intentCaptor.value
assertThat(intent.action).isEqualTo(Intent.ACTION_APPLICATION_PREFERENCES)
assertThat(intent.`package`).isEqualTo(PACKAGE_NAME)
}
@Test
fun noResolveInfo_notDisplayed() {
mockResolveActivityAsUser(null)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun noSettingsActivity_notDisplayed() {
mockResolveActivityAsUser(ResolveInfo())
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun hasSettingsActivity_displayed() {
mockResolveActivityAsUser(RESOLVE_INFO)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.app_settings_link))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun whenClick_startActivity() {
mockResolveActivityAsUser(RESOLVE_INFO)
setContent()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
val intent = intentCaptor.value
assertThat(intent.action).isEqualTo(Intent.ACTION_APPLICATION_PREFERENCES)
assertThat(intent.component).isEqualTo(ComponentName(PACKAGE_NAME, ACTIVITY_NAME))
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppSettingsPreference(APP)
}
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "packageName"
const val ACTIVITY_NAME = "activityName"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
val RESOLVE_INFO = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME
name = ACTIVITY_NAME
}
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* 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.settings.spa.app.appinfo
import android.app.usage.StorageStats
import android.app.usage.StorageStatsManager
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.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
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.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppStoragePreferenceTest {
@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
@Before
fun setUp() {
whenever(context.storageStatsManager).thenReturn(storageStatsManager)
whenever(
storageStatsManager.queryStatsForPackage(eq(STORAGE_UUID), eq(PACKAGE_NAME), any())
).thenReturn(STATS)
}
@Test
fun notInstalledApp_notDisplayed() {
val notInstalledApp = ApplicationInfo()
setContent(notInstalledApp)
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun internalApp_displayed() {
val internalApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
storageUuid = STORAGE_UUID
}
setContent(internalApp)
composeTestRule.onNodeWithText(context.getString(R.string.storage_settings_for_app))
.assertIsDisplayed()
composeTestRule.waitUntilExists(hasText("120 B used in internal storage"))
}
@Test
fun externalApp_displayed() {
val externalApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED or ApplicationInfo.FLAG_EXTERNAL_STORAGE
storageUuid = STORAGE_UUID
}
setContent(externalApp)
composeTestRule.onNodeWithText(context.getString(R.string.storage_settings_for_app))
.assertIsDisplayed()
composeTestRule.waitUntilExists(hasText("120 B used in external storage"))
}
private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppStoragePreference(app)
}
}
}
companion object {
private const val PACKAGE_NAME = "package name"
private val STORAGE_UUID = UUID.randomUUID()
private val STATS = StorageStats().apply {
codeBytes = 100
dataBytes = 20
cacheBytes = 3
}
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
import android.content.pm.ResolveInfo
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
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.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.testutils.FakeFeatureFactory
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AppTimeSpentPreferenceTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
private val fakeFeatureFactory = FakeFeatureFactory()
private val appFeatureProvider = fakeFeatureFactory.mockApplicationFeatureProvider
@Before
fun setUp() {
whenever(context.packageManager).thenReturn(packageManager)
whenever(appFeatureProvider.getTimeSpentInApp(PACKAGE_NAME)).thenReturn(TIME_SPENT)
}
private fun mockActivitiesQueryResult(resolveInfos: List<ResolveInfo>) {
whenever(
packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), anyInt())
).thenReturn(resolveInfos)
}
@Test
fun noIntentHandler_notDisplay() {
mockActivitiesQueryResult(emptyList())
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppTimeSpentPreference(INSTALLED_APP)
}
}
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun hasIntentHandler_notSystemApp_notDisplay() {
mockActivitiesQueryResult(listOf(ResolveInfo()))
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppTimeSpentPreference(INSTALLED_APP)
}
}
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun installedApp_enabled() {
mockActivitiesQueryResult(listOf(MATCHED_RESOLVE_INFO))
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppTimeSpentPreference(INSTALLED_APP)
}
}
composeTestRule.onNodeWithText(context.getString(R.string.time_spent_in_app_pref_title))
.assertIsDisplayed()
.assertIsEnabled()
composeTestRule.onNodeWithText(TIME_SPENT).assertIsDisplayed()
}
@Test
fun notInstalledApp_disabled() {
mockActivitiesQueryResult(listOf(MATCHED_RESOLVE_INFO))
val notInstalledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AppTimeSpentPreference(notInstalledApp)
}
}
composeTestRule.onNodeWithText(context.getString(R.string.time_spent_in_app_pref_title))
.assertIsNotEnabled()
}
companion object {
private const val PACKAGE_NAME = "package name"
private const val TIME_SPENT = "15 minutes"
private val INSTALLED_APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
}
private val MATCHED_RESOLVE_INFO = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
applicationInfo = ApplicationInfo().apply {
flags = ApplicationInfo.FLAG_SYSTEM
}
}
}
}
}

View File

@@ -0,0 +1,177 @@
/*
* 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.settings.spa.app.appinfo
import android.app.role.RoleManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.os.UserManager
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
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.settings.R
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class DefaultAppShortcutPreferenceTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var userManager: UserManager
@Mock
private lateinit var roleManager: RoleManager
@Before
fun setUp() {
whenever(context.userManager).thenReturn(userManager)
whenever(userManager.isManagedProfile(anyInt())).thenReturn(false)
whenever(context.getSystemService(RoleManager::class.java)).thenReturn(roleManager)
mockIsRoleVisible(true)
mockIsApplicationVisibleForRole(true)
}
private fun mockIsRoleVisible(visible: Boolean) {
doAnswer {
@Suppress("UNCHECKED_CAST")
(it.arguments[2] as Consumer<Boolean>).accept(visible)
}.`when`(roleManager).isRoleVisible(eq(ROLE), any(), any())
}
private fun mockIsApplicationVisibleForRole(visible: Boolean) {
doAnswer {
@Suppress("UNCHECKED_CAST")
(it.arguments[3] as Consumer<Boolean>).accept(visible)
}.`when`(roleManager).isApplicationVisibleForRole(eq(ROLE), eq(PACKAGE_NAME), any(), any())
}
@Test
fun isManagedProfile_notDisplay() {
whenever(userManager.isManagedProfile(anyInt())).thenReturn(true)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun roleNotVisible_notDisplay() {
mockIsRoleVisible(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun applicationVisibleForRole_notDisplay() {
mockIsApplicationVisibleForRole(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun isRoleHolder_summaryIsYes() {
whenever(roleManager.getRoleHoldersAsUser(eq(ROLE), any())).thenReturn(listOf(PACKAGE_NAME))
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.yes))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun notRoleHolder_summaryIsNo() {
whenever(roleManager.getRoleHoldersAsUser(eq(ROLE), any())).thenReturn(emptyList())
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.no))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun onClick_startManageDefaultAppIntent() {
whenever(roleManager.getRoleHoldersAsUser(eq(ROLE), any())).thenReturn(emptyList())
doNothing().`when`(context).startActivityAsUser(any(), any())
setContent()
composeTestRule.onRoot().performClick()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
verify(context).startActivityAsUser(intentCaptor.capture(), any())
val intent = intentCaptor.value
assertThat(intent.action).isEqualTo(Intent.ACTION_MANAGE_DEFAULT_APP)
assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(ROLE)
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
DefaultAppShortcutPreference(SHORTCUT, App)
}
}
}
private companion object {
const val ROLE = RoleManager.ROLE_HOME
val SHORTCUT = DefaultAppShortcut(roleName = ROLE, titleResId = R.string.home_app)
const val PACKAGE_NAME = "package name"
val App = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
}
}

View File

@@ -0,0 +1,262 @@
/*
* 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.settings.spa.app.appinfo
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.MODE_DEFAULT
import android.app.AppOpsManager.MODE_IGNORED
import android.app.AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
import android.apphibernation.AppHibernationManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.Flags as PmFlags
import android.os.Build
import android.os.SystemProperties
import android.permission.PermissionControllerManager
import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_ELIGIBLE
import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
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.isToggleable
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.settings.R
import com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED
import com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS
import com.android.settings.flags.Flags
import com.android.settings.testutils.TestDeviceConfig
import com.android.settings.testutils.mockAsUser
import com.android.settingslib.spaprivileged.framework.common.appHibernationManager
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
import com.android.settingslib.spaprivileged.framework.common.permissionControllerManager
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import java.util.function.IntConsumer
import kotlinx.coroutines.flow.MutableStateFlow
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class HibernationSwitchPreferenceTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule
val composeTestRule = createComposeRule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var permissionControllerManager: PermissionControllerManager
@Mock
private lateinit var appOpsManager: AppOpsManager
@Mock
private lateinit var appHibernationManager: AppHibernationManager
private val hibernationEnabledConfig =
TestDeviceConfig(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED)
private val hibernationTargetsPreSConfig =
TestDeviceConfig(NAMESPACE_APP_HIBERNATION, PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS)
private val isHibernationSwitchEnabledStateFlow = MutableStateFlow(true)
@Before
fun setUp() {
hibernationEnabledConfig.override(true)
hibernationTargetsPreSConfig.override(false)
context.mockAsUser()
whenever(context.permissionControllerManager).thenReturn(permissionControllerManager)
whenever(context.appOpsManager).thenReturn(appOpsManager)
whenever(context.appHibernationManager).thenReturn(appHibernationManager)
mockHibernationEligibility(HIBERNATION_ELIGIBILITY_ELIGIBLE)
}
@After
fun cleanUp() {
hibernationEnabledConfig.reset()
hibernationTargetsPreSConfig.reset()
}
private fun mockHibernationEligibility(eligibility: Int) {
doAnswer {
@Suppress("UNCHECKED_CAST")
(it.arguments[2] as IntConsumer).accept(eligibility)
}.`when`(permissionControllerManager).getHibernationEligibility(
eq(PACKAGE_NAME), any(), any()
)
}
private fun mockOpsMode(mode: Int) {
whenever(
appOpsManager.checkOpNoThrow(OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, UID, PACKAGE_NAME)
).thenReturn(mode)
}
@Test
fun `Hibernation disabled - not display`() {
hibernationEnabledConfig.override(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun `Not eligible - displayed but disabled`() {
mockHibernationEligibility(HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM)
setContent()
val text = if (isArchivingEnabled()) {
context.getString(R.string.unused_apps_switch_v2)
} else {
context.getString(R.string.unused_apps_switch)
}
composeTestRule.onNodeWithText(text)
.assertIsDisplayed()
.assertIsNotEnabled()
.assertIsOff()
}
private fun isArchivingEnabled() =
PmFlags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false)
|| Flags.appArchiving()
@Test
fun `An app targets Q with ops mode default when hibernation targets pre S - not exempted`() {
mockOpsMode(MODE_DEFAULT)
hibernationTargetsPreSConfig.override(true)
setContent(TARGET_Q_APP)
composeTestRule.onNode(isToggleable()).assertIsEnabled().assertIsOn()
}
@Test
fun `An app targets Q with ops mode default when hibernation targets R - exempted`() {
mockOpsMode(MODE_DEFAULT)
hibernationTargetsPreSConfig.override(false)
setContent(TARGET_Q_APP)
composeTestRule.onNode(isToggleable()).assertIsEnabled().assertIsOff()
}
@Test
fun `An app targets R with ops mode default - not exempted`() {
mockOpsMode(MODE_DEFAULT)
setContent(TARGET_R_APP)
composeTestRule.onNode(isToggleable()).assertIsEnabled().assertIsOn()
}
@Test
fun `An app with ops mode allowed - not exempted`() {
mockOpsMode(MODE_ALLOWED)
setContent()
composeTestRule.onNode(isToggleable()).assertIsEnabled().assertIsOn()
}
@Test
fun `An app with ops mode ignored - exempted`() {
mockOpsMode(MODE_IGNORED)
setContent()
composeTestRule.onNode(isToggleable()).assertIsEnabled().assertIsOff()
}
@Test
fun `An app is exempted - on click`() {
mockOpsMode(MODE_IGNORED)
setContent()
composeTestRule.onRoot().performClick()
verify(appOpsManager).setUidMode(OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, UID, MODE_ALLOWED)
verify(appHibernationManager, never()).setHibernatingForUser(anyString(), anyBoolean())
verify(appHibernationManager, never()).setHibernatingGlobally(anyString(), anyBoolean())
}
@Test
fun `An app is not exempted - on click`() {
mockOpsMode(MODE_ALLOWED)
setContent()
composeTestRule.onRoot().performClick()
verify(appOpsManager).setUidMode(OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, UID, MODE_IGNORED)
verify(appHibernationManager).setHibernatingForUser(PACKAGE_NAME, false)
verify(appHibernationManager).setHibernatingGlobally(PACKAGE_NAME, false)
}
private fun setContent(app: ApplicationInfo = TARGET_R_APP) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
HibernationSwitchPreference(app, isHibernationSwitchEnabledStateFlow)
}
}
}
private companion object {
const val PACKAGE_NAME = "package name"
const val UID = 123
val TARGET_R_APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
targetSdkVersion = Build.VERSION_CODES.R
}
val TARGET_Q_APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
targetSdkVersion = Build.VERSION_CODES.Q
}
}
}

View File

@@ -0,0 +1,166 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ApplicationInfo
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.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
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.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.Utils
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.onDialogText
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class InstantAppDomainsPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(Utils::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.packageManager).thenReturn(packageManager)
Mockito.doReturn(context).`when`(context).createContextAsUser(any(), anyInt())
mockDomains(emptySet())
}
@After
fun tearDown() {
mockSession.finishMocking()
}
private fun mockDomains(domains: Set<String>) {
whenever(Utils.getHandledDomains(packageManager, PACKAGE_NAME)).thenReturn(domains)
}
@Test
fun notInstantApp_notDisplayed() {
val app = ApplicationInfo()
setContent(app)
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun title_displayed() {
setContent()
composeTestRule
.onNodeWithText(context.getString(R.string.app_launch_supported_domain_urls_title))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun noDomain() {
mockDomains(emptySet())
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.domain_urls_summary_none))
.assertIsDisplayed()
}
@Test
fun oneDomain() {
mockDomains(setOf("abc"))
setContent()
composeTestRule.onNodeWithText("Open abc").assertIsDisplayed()
}
@Test
fun twoDomains() {
mockDomains(setOf("abc", "def"))
setContent()
composeTestRule.onNodeWithText("Open abc and other URLs").assertIsDisplayed()
}
@Test
fun whenClicked() {
mockDomains(setOf("abc", "def"))
setContent()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
composeTestRule.onDialogText(
context.getString(R.string.app_launch_supported_domain_urls_title)
).assertIsDisplayed()
composeTestRule.onDialogText("abc").assertIsDisplayed()
composeTestRule.onDialogText("def").assertIsDisplayed()
}
private fun setContent(app: ApplicationInfo = INSTANT_APP) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
InstantAppDomainsPreference(app)
}
}
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 123
val INSTANT_APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
}
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.app.appinfo
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.CrossProfileApps
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasText
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.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetails
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class InteractAcrossProfilesDetailsPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var crossProfileApps: CrossProfileApps
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(InteractAcrossProfilesDetails::class.java)
.mockStatic(AppInfoDashboardFragment::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.crossProfileApps).thenReturn(crossProfileApps)
whenever(InteractAcrossProfilesDetails.getPreferenceSummary(context, PACKAGE_NAME))
.thenReturn("")
}
@After
fun tearDown() {
mockSession.finishMocking()
}
private fun mockCanConfig(canConfig: Boolean) {
whenever(crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(PACKAGE_NAME))
.thenReturn(canConfig)
}
@Test
fun cannotConfig_notDisplayed() {
mockCanConfig(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun canConfig_displayed() {
mockCanConfig(true)
setContent()
composeTestRule.onNodeWithText(context.getString(R.string.interact_across_profiles_title))
.assertIsDisplayed()
.assertIsEnabled()
}
@Test
fun hasSummary() {
mockCanConfig(true)
whenever(InteractAcrossProfilesDetails.getPreferenceSummary(context, PACKAGE_NAME))
.thenReturn(SUMMARY)
setContent()
composeTestRule.waitUntilExists(hasText(SUMMARY))
}
@Ignore
@Test
fun whenClick_startActivity() {
mockCanConfig(true)
setContent()
composeTestRule.onRoot().performClick()
composeTestRule.delay()
ExtendedMockito.verify {
AppInfoDashboardFragment.startAppInfoFragment(
InteractAcrossProfilesDetails::class.java,
APP,
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
InteractAcrossProfilesDetailsPreference(APP)
}
}
}
private companion object {
const val PACKAGE_NAME = "packageName"
const val UID = 123
const val SUMMARY = "summary"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
}
}

View File

@@ -0,0 +1,187 @@
/*
* 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.settings.spa.app.appinfo
import android.app.ActivityManager
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settings.testutils.mockAsUser
import com.android.settingslib.spaprivileged.framework.common.activityManager
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
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 PackageInfoPresenterTest {
private val mockPackageManager = mock<PackageManager>()
private val mockActivityManager = mock<ActivityManager>()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn mockPackageManager
on { activityManager } doReturn mockActivityManager
doNothing().whenever(mock).startActivityAsUser(any(), any())
mock.mockAsUser()
}
private val packageManagers = mock<IPackageManagers>()
private val fakeFeatureFactory = FakeFeatureFactory()
private val metricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider
private val packageInfoPresenter =
PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
@Test
fun isInterestedAppChange_packageChanged_isInterested() {
val intent = Intent(Intent.ACTION_PACKAGE_CHANGED).apply {
data = Uri.parse("package:$PACKAGE_NAME")
}
val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
assertThat(isInterestedAppChange).isTrue()
}
@Test
fun isInterestedAppChange_fullyRemoved_notInterested() {
val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
data = Uri.parse("package:$PACKAGE_NAME")
putExtra(Intent.EXTRA_DATA_REMOVED, true)
}
val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
assertThat(isInterestedAppChange).isFalse()
}
@Test
fun isInterestedAppChange_removedBeforeReplacing_notInterested() {
val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
data = Uri.parse("package:$PACKAGE_NAME")
putExtra(Intent.EXTRA_REPLACING, true)
}
val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
assertThat(isInterestedAppChange).isFalse()
}
@Test
fun isInterestedAppChange_archived_interested() {
val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
data = Uri.parse("package:$PACKAGE_NAME")
putExtra(Intent.EXTRA_ARCHIVAL, true)
}
val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
assertThat(isInterestedAppChange).isTrue()
}
@Test
fun enable() = runBlocking {
packageInfoPresenter.enable()
delay(100)
verifyAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP)
verify(mockPackageManager).setApplicationEnabledSetting(
PACKAGE_NAME, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0
)
}
@Test
fun disable() = runBlocking {
packageInfoPresenter.disable()
delay(100)
verifyAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP)
verify(mockPackageManager).setApplicationEnabledSetting(
PACKAGE_NAME, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0
)
}
@Test
fun startUninstallActivity() = runBlocking {
packageInfoPresenter.startUninstallActivity()
verifyAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP)
val intent = argumentCaptor<Intent> {
verify(context).startActivityAsUser(capture(), any())
}.firstValue
with(intent) {
assertThat(action).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE)
assertThat(data?.schemeSpecificPart).isEqualTo(PACKAGE_NAME)
assertThat(getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, true)).isEqualTo(false)
}
}
@Test
fun clearInstantApp() = runBlocking {
packageInfoPresenter.clearInstantApp()
delay(100)
verifyAction(SettingsEnums.ACTION_SETTINGS_CLEAR_INSTANT_APP)
verify(mockPackageManager).deletePackageAsUser(PACKAGE_NAME, null, 0, USER_ID)
}
@Test
fun forceStop() = runBlocking {
packageInfoPresenter.forceStop()
delay(100)
verifyAction(SettingsEnums.ACTION_APP_FORCE_STOP)
verify(mockActivityManager).forceStopPackageAsUser(PACKAGE_NAME, USER_ID)
}
@Test
fun logAction() = runBlocking {
packageInfoPresenter.logAction(123)
verifyAction(123)
}
private fun verifyAction(category: Int) {
verify(metricsFeatureProvider).action(context, category, PACKAGE_NAME)
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val USER_ID = 0
val PACKAGE_INFO = PackageInfo()
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.settings.spa.app.appinfo
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.R
import com.android.settingslib.spa.testutils.waitUntilExists
import com.android.settingslib.spaprivileged.model.app.userHandle
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class TopBarAppLaunchButtonTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageInfoPresenter: PackageInfoPresenter
@Mock
private lateinit var userPackageManager: PackageManager
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(packageInfoPresenter.context).thenReturn(context)
whenever(packageInfoPresenter.userPackageManager).thenReturn(userPackageManager)
val intent = Intent()
whenever(userPackageManager.getLaunchIntentForPackage(PACKAGE_NAME)).thenReturn(intent)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun topBarAppLaunchButton_isDisplayed() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
setContent(app)
composeTestRule.waitUntilExists(
hasContentDescription(context.getString(R.string.launch_instant_app))
)
}
@Test
fun topBarAppLaunchButton_opensApp() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
setContent(app)
composeTestRule.onNodeWithContentDescription(context.getString(R.string.launch_instant_app))
.performClick()
verify(context).startActivityAsUser(any(), eq(app.userHandle))
}
private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
TopBarAppLaunchButton(packageInfoPresenter, app)
}
}
}
private companion object {
const val PACKAGE_NAME = "package.name"
}
}

View File

@@ -0,0 +1,284 @@
/*
* 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.settings.spa.app.backgroundinstall
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.IBackgroundInstallControlService
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.ParceledListSlice
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.settings.R
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
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.Before
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.whenever
@RunWith(AndroidJUnit4::class)
class BackgroundInstalledAppsPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var mockContext: Context
@Mock
private lateinit var mockPackageManager: PackageManager
@Mock
private lateinit var mockBackgroundInstallControlService: IBackgroundInstallControlService
private var packageInfoFlagsCaptor = argumentCaptor<PackageManager.PackageInfoFlags>()
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
@Before
fun setup() {
whenever(mockContext.packageManager).thenReturn(mockPackageManager)
}
@Test
fun allAppListPageProvider_name() {
assertThat(BackgroundInstalledAppsPageProvider.name)
.isEqualTo(EXPECTED_PROVIDER_NAME)
}
@Test
fun injectEntry_title() {
whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(any(), any()))
.thenReturn(ParceledListSlice(listOf()))
setInjectEntry(false)
composeTestRule.onNodeWithText(
context.getString(R.string.background_install_title)).assertIsDisplayed()
}
@Test
fun injectEntry_title_disabled() {
setInjectEntry(true)
composeTestRule.onNodeWithText(
context.getString(R.string.background_install_title)).assertDoesNotExist()
}
@Test
fun injectEntry_summary() {
whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(any(), any()))
.thenReturn(ParceledListSlice(listOf()))
setInjectEntry(false)
composeTestRule.onNodeWithText("0 apps").assertIsDisplayed()
}
@Test
fun injectEntry_summary_disabled() {
setInjectEntry(true)
composeTestRule.onNodeWithText("0 apps").assertDoesNotExist()
}
@Test
fun injectEntry_onClick_navigate() {
whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(any(), any()))
.thenReturn(ParceledListSlice(listOf()))
setInjectEntry(false)
composeTestRule.onNodeWithText(
context.getString(R.string.background_install_title)).performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith)
.isEqualTo(EXPECTED_PROVIDER_NAME)
}
private fun setInjectEntry(disableFeature: Boolean = false) {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
BackgroundInstalledAppsPageProvider
.setBackgroundInstallControlService(mockBackgroundInstallControlService)
.setDisableFeature(disableFeature)
.buildInjectEntry().build().UiLayout()
}
}
}
@Test
fun title_displayed() {
composeTestRule.setContent {
BackgroundInstalledAppList()
}
composeTestRule.onNodeWithText(
context.getString(R.string.background_install_title)).assertIsDisplayed()
}
@Test
fun item_labelDisplayed() {
setItemContent()
composeTestRule.onNodeWithText(TEST_LABEL).assertIsDisplayed()
}
@Test
fun item_onClick_navigate() {
setItemContent()
composeTestRule.onNodeWithText(TEST_LABEL).performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith)
.isEqualTo("AppInfoSettings/package.name/0")
}
@Test
fun backgroundInstalledAppsWithGroupingListModel_getGroupTitleOne() = runTest {
val listModel = BackgroundInstalledAppsWithGroupingListModel(context)
val actualGroupTitle = listModel
.getGroupTitle(0,
BackgroundInstalledAppListWithGroupingAppRecord(
APP,
System.currentTimeMillis()
))
assertThat(actualGroupTitle).isEqualTo("Apps installed in the last 6 months")
}
@Test
fun backgroundInstalledAppsWithGroupingListModel_getGroupTitleTwo() = runTest {
val listModel = BackgroundInstalledAppsWithGroupingListModel(context)
val actualGroupTitle = listModel
.getGroupTitle(0,
BackgroundInstalledAppListWithGroupingAppRecord(
APP,
0L
))
assertThat(actualGroupTitle).isEqualTo("Apps installed more than 6 months ago")
}
@Test
fun backgroundInstalledAppsWithGroupingListModel_transform() = runTest {
val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext)
whenever(mockPackageManager.getPackageInfoAsUser(
eq(TEST_PACKAGE_NAME),
packageInfoFlagsCaptor.capture(),
eq(TEST_USER_ID))
)
.thenReturn(PACKAGE_INFO)
val recordListFlow = listModel.transform(flowOf(TEST_USER_ID), flowOf(listOf(APP)))
val recordList = recordListFlow.first()
assertThat(recordList).hasSize(1)
assertThat(recordList[0].app).isSameInstanceAs(APP)
assertThat(packageInfoFlagsCaptor.firstValue.value).isEqualTo(EXPECTED_PACKAGE_INFO_FLAG)
}
@Test
fun backgroundInstalledAppsWithGroupingListModel_filter() = runTest {
val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext)
listModel.setBackgroundInstallControlService(mockBackgroundInstallControlService)
whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(
PackageManager.MATCH_ALL.toLong(),
TEST_USER_ID
)).thenReturn(ParceledListSlice(listOf(PACKAGE_INFO)))
val recordListFlow = listModel.filter(
flowOf(TEST_USER_ID),
0,
flowOf(listOf(APP_RECORD_WITH_PACKAGE_MATCH, APP_RECORD_WITHOUT_PACKAGE_MATCH))
)
val recordList = recordListFlow.first()
assertThat(recordList).hasSize(1)
assertThat(recordList[0]).isSameInstanceAs(APP_RECORD_WITH_PACKAGE_MATCH)
}
private fun setItemContent() {
composeTestRule.setContent {
BackgroundInstalledAppList {
fakeNavControllerWrapper.Wrapper {
with(BackgroundInstalledAppsWithGroupingListModel(context)) {
AppListItemModel(
record = BackgroundInstalledAppListWithGroupingAppRecord(
app = APP,
dateOfInstall = TEST_FIRST_INSTALL_TIME),
label = TEST_LABEL,
summary = { TEST_SUMMARY },
).AppItem()
}
}
}
}
}
private companion object {
private const val TEST_USER_ID = 0
private const val TEST_PACKAGE_NAME = "package.name"
private const val TEST_NO_MATCH_PACKAGE_NAME = "no.match"
private const val TEST_LABEL = "Label"
private const val TEST_SUMMARY = "Summary"
private const val TEST_FIRST_INSTALL_TIME = 0L
private const val EXPECTED_PROVIDER_NAME = "BackgroundInstalledAppsPage"
private const val EXPECTED_PACKAGE_INFO_FLAG = 0L
val APP = ApplicationInfo().apply {
packageName = TEST_PACKAGE_NAME
}
val APP_NO_RECORD = ApplicationInfo().apply {
packageName = TEST_NO_MATCH_PACKAGE_NAME
}
val APP_RECORD_WITH_PACKAGE_MATCH = BackgroundInstalledAppListWithGroupingAppRecord(
APP,
TEST_FIRST_INSTALL_TIME
)
val APP_RECORD_WITHOUT_PACKAGE_MATCH = BackgroundInstalledAppListWithGroupingAppRecord(
APP_NO_RECORD,
TEST_FIRST_INSTALL_TIME
)
val PACKAGE_INFO = PackageInfo().apply {
packageName = TEST_PACKAGE_NAME
applicationInfo = APP
firstInstallTime = TEST_FIRST_INSTALL_TIME
}
}
}

View File

@@ -0,0 +1,295 @@
/*
* 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.settings.spa.app
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import androidx.compose.runtime.SideEffect
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.settings.DisplaySettings
import com.android.settings.R
import com.android.settings.SettingsActivity
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
import com.android.settings.spa.app.battery.BatteryOptimizationModeAppList
import com.android.settings.spa.app.battery.BatteryOptimizationModeAppListModel
import com.android.settings.spa.app.battery.BatteryOptimizationModeAppListPageProvider
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import com.android.settingslib.spaprivileged.template.app.AppListInput
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
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.argumentCaptor
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.doReturn
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 BatteryOptimizationModeAppListPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
private val packageManager = mock<PackageManager> {
on { getPackagesForUid(USER_ID) } doReturn arrayOf(PACKAGE_NAME)
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn packageManager
}
@Test
fun batteryOptimizationModeAppListPageProvider_name() {
assertThat(BatteryOptimizationModeAppListPageProvider.name)
.isEqualTo("BatteryOptimizationModeAppList")
}
@Test
fun injectEntry_title() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.app_battery_usage_title))
.assertIsDisplayed()
}
@Test
fun injectEntry_onClick_navigate() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.app_battery_usage_title))
.performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith)
.isEqualTo("BatteryOptimizationModeAppList")
}
@Test
fun title_displayed() {
composeTestRule.setContent {
BatteryOptimizationModeAppList {}
}
composeTestRule.onNodeWithText(context.getString(R.string.app_battery_usage_title))
.assertIsDisplayed()
}
@Test
fun showInstantApps_isFalse() {
val input = getAppListInput()
assertThat(input.config.showInstantApps).isFalse()
}
@Test
fun item_labelDisplayed() {
setItemContent()
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun item_summaryDisplayed() {
setItemContent()
composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
}
@Test
fun item_onClick_navigate() {
setItemContent()
doNothing().whenever(context).startActivity(any())
composeTestRule.onNodeWithText(LABEL).performClick()
val intent = argumentCaptor<Intent> {
verify(context).startActivity(capture())
}.firstValue
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))!!
.isEqualTo(AdvancedPowerUsageDetail::class.java.name)
val arguments = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
assertThat(arguments.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME))
.isEqualTo(PACKAGE_NAME)
}
@Test
fun BatteryOptimizationModeAppListModel_transform() = runTest {
val listModel = BatteryOptimizationModeAppListModel(context)
val recordListFlow = listModel.transform(flowOf(USER_ID), flowOf(listOf(APP)))
val recordList = recordListFlow.firstWithTimeoutOrNull()!!
assertThat(recordList).hasSize(1)
assertThat(recordList[0].app).isSameInstanceAs(APP)
}
@Test
fun listModelGetSummary_regular() {
val listModel = BatteryOptimizationModeAppListModel(context)
lateinit var summary: () -> String
composeTestRule.setContent {
summary = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
}
assertThat(summary()).isEmpty()
}
@Test
fun listModelGetSummary_disabled() {
val listModel = BatteryOptimizationModeAppListModel(context)
val disabledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
enabled = false
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = disabledApp))
}
assertThat(summary())
.isEqualTo(context.getString(com.android.settingslib.R.string.disabled))
}
@Test
fun listModelGetSummary_notInstalled() {
val listModel = BatteryOptimizationModeAppListModel(context)
val notInstalledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = notInstalledApp))
}
assertThat(summary()).isEqualTo(context.getString(R.string.not_installed))
}
@Test
fun batteryOptimizationModeAppListModel_archivedApp() {
val app = mock<ApplicationInfo> {
on { loadUnbadgedIcon(any()) } doReturn UNBADGED_ICON
on { loadLabel(any()) } doReturn LABEL
}
app.isArchived = true
packageManager.stub {
on {
getApplicationInfoAsUser(PACKAGE_NAME, 0, USER_ID)
} doReturn app
}
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(BatteryOptimizationModeAppListModel(context)) {
AppListItemModel(
record = AppRecordWithSize(app = app),
label = LABEL,
summary = { SUMMARY },
).AppItem()
}
}
}
composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
}
@Test
fun batteryOptimizationModeAppListModel_NoStorageSummary() {
val listModel = BatteryOptimizationModeAppListModel(context)
val archivedApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
isArchived = true
}
lateinit var summary: () -> String
composeTestRule.setContent {
summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = archivedApp))
}
assertThat(summary()).isEmpty()
}
private fun setInjectEntry() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
BatteryOptimizationModeAppListPageProvider.buildInjectEntry().build().UiLayout()
}
}
}
private fun getAppListInput(): AppListInput<AppRecordWithSize> {
lateinit var input: AppListInput<AppRecordWithSize>
composeTestRule.setContent {
BatteryOptimizationModeAppList {
SideEffect {
input = this
}
}
}
return input
}
private fun setItemContent() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(BatteryOptimizationModeAppListModel(context)) {
AppListItemModel(
record = AppRecordWithSize(app = APP),
label = LABEL,
summary = { SUMMARY },
).AppItem()
}
}
}
}
private companion object {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
const val SUMMARY = "Summary"
val UNBADGED_ICON = mock<Drawable>()
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
}
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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.settings.spa.app.specialaccess
import android.Manifest
import android.app.compat.CompatChanges
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.PowerExemptionManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AlarmsAndRemindersAppListTest {
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var powerExemptionManager: PowerExemptionManager
@Mock
private lateinit var packageManagers: IPackageManagers
private lateinit var listModel: AlarmsAndRemindersAppListModel
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(CompatChanges::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(CompatChanges.isChangeEnabled(anyLong(), anyString(), any())).thenReturn(true)
whenever(context.getSystemService(PowerExemptionManager::class.java))
.thenReturn(powerExemptionManager)
with(packageManagers) {
whenever(APP.hasRequestPermission(Manifest.permission.SCHEDULE_EXACT_ALARM))
.thenReturn(true)
}
listModel = AlarmsAndRemindersAppListModel(context, packageManagers)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun transformItem_recordHasCorrectApp() {
val record = listModel.transformItem(APP)
assertThat(record.app).isSameInstanceAs(APP)
}
@Test
fun transformItem_whenNotRequestScheduleExactAlarm_recordHasCorrectState() {
with(packageManagers) {
whenever(APP.hasRequestPermission(Manifest.permission.SCHEDULE_EXACT_ALARM))
.thenReturn(false)
}
val record = listModel.transformItem(APP)
assertThat(record.isTrumped).isFalse()
assertThat(record.isChangeable).isFalse()
}
@Test
fun transformItem_whenRequestUseExactAlarm_recordHasCorrectState() {
with(packageManagers) {
whenever(APP.hasRequestPermission(Manifest.permission.USE_EXACT_ALARM))
.thenReturn(true)
}
val record = listModel.transformItem(APP)
assertThat(record.isTrumped).isTrue()
assertThat(record.isChangeable).isFalse()
}
@Test
fun transformItem_whenPowerAllowListed_recordHasCorrectState() {
whenever(powerExemptionManager.isAllowListed(PACKAGE_NAME, true)).thenReturn(true)
val record = listModel.transformItem(APP)
assertThat(record.isTrumped).isTrue()
assertThat(record.isChangeable).isFalse()
}
@Test
fun transformItem_whenNotTrumped_recordHasCorrectState() {
val record = listModel.transformItem(APP)
assertThat(record.isTrumped).isFalse()
assertThat(record.isChangeable).isTrue()
}
private companion object {
const val PACKAGE_NAME = "package.name"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.settings.spa.app.specialaccess
import android.Manifest
import android.app.AppOpsManager
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AllFilesAccessTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val listModel = AllFilesAccessListModel(context)
@Test
fun modelResourceIdAndProperties() {
assertThat(listModel.pageTitleResId).isEqualTo(R.string.manage_external_storage_title)
assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_manage_external_storage)
assertThat(listModel.footerResId).isEqualTo(R.string.allow_manage_external_storage_description)
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE)
assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
assertThat(listModel.setModeByUid).isTrue()
}
}

Some files were not shown because too many files have changed in this diff Show More