fix: 引入Settings的Module
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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>()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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>()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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>"
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.spa.app.specialaccess
|
||||
|
||||
import android.content.Context
|
||||
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.preference.Preference
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import com.android.settings.flags.Flags
|
||||
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.doNothing
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class BackupTasksAppsPreferenceControllerTest {
|
||||
|
||||
@get:Rule
|
||||
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||
doNothing().whenever(mock).startActivity(any())
|
||||
}
|
||||
|
||||
private val matchedPreference = Preference(context).apply { key = preferenceKey }
|
||||
|
||||
private val misMatchedPreference = Preference(context).apply { key = testPreferenceKey }
|
||||
|
||||
private val controller = BackupTasksAppsPreferenceController(context, preferenceKey)
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_PERFORM_BACKUP_TASKS_IN_SETTINGS)
|
||||
fun getAvailabilityStatus_enableBackupTasksApps_returnAvailable() {
|
||||
assertThat(controller.isAvailable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PERFORM_BACKUP_TASKS_IN_SETTINGS)
|
||||
fun getAvailableStatus_disableBackupTasksApps_returnConditionallyUnavailable() {
|
||||
assertThat(controller.isAvailable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handlePreferenceTreeClick_keyMatched_returnTrue() {
|
||||
assertThat(controller.handlePreferenceTreeClick(matchedPreference)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handlePreferenceTreeClick_keyMisMatched_returnFalse() {
|
||||
assertThat(controller.handlePreferenceTreeClick(misMatchedPreference)).isFalse()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val preferenceKey: String = "backup_tasks_apps"
|
||||
private const val testPreferenceKey: String = "test_key"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.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.google.common.truth.Truth.assertThat
|
||||
import com.android.settings.R
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class BackupTasksAppsTest {
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val listModel = BackupTasksAppsListModel(context)
|
||||
|
||||
@Test
|
||||
fun modelResourceIdAndProperties() {
|
||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.run_backup_tasks_title)
|
||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.run_backup_tasks_switch_title)
|
||||
assertThat(listModel.footerResId).isEqualTo(R.string.run_backup_tasks_footer_title)
|
||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_BACKUP_JOBS)
|
||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_BACKUP_JOBS)
|
||||
assertThat(listModel.setModeByUid).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.AppOpsManager
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.android.settings.R
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LongBackgroundTasksAppsTest {
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val listModel = LongBackgroundTasksAppsListModel(context)
|
||||
|
||||
@Test
|
||||
fun modelResourceIdAndProperties() {
|
||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.long_background_tasks_title)
|
||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.long_background_tasks_switch_title)
|
||||
assertThat(listModel.footerResId).isEqualTo(R.string.long_background_tasks_footer_title)
|
||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_USER_INITIATED_JOBS)
|
||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_USER_INITIATED_JOBS)
|
||||
assertThat(listModel.setModeByUid).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -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 MediaManagementAppsTest {
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val listModel = MediaManagementAppsListModel(context)
|
||||
|
||||
@Test
|
||||
fun modelResourceIdAndProperties() {
|
||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_management_apps_title)
|
||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.media_management_apps_toggle_label)
|
||||
assertThat(listModel.footerResId).isEqualTo(R.string.media_management_apps_description)
|
||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MANAGE_MEDIA)
|
||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_MEDIA)
|
||||
assertThat(listModel.setModeByUid).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.AppOpsManager
|
||||
import android.app.role.RoleManager
|
||||
import android.app.settings.SettingsEnums
|
||||
import android.companion.AssociationRequest
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.media.flags.Flags
|
||||
import com.android.settings.R
|
||||
import com.android.settings.testutils.FakeFeatureFactory
|
||||
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
|
||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||
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.Mock
|
||||
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 MediaRoutingControlTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule();
|
||||
|
||||
@Spy
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private lateinit var listModel: MediaRoutingControlAppsListModel
|
||||
|
||||
@Mock
|
||||
private lateinit var mockRoleManager: RoleManager
|
||||
|
||||
private val fakeFeatureFactory = FakeFeatureFactory()
|
||||
private val metricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(context.getSystemService(RoleManager::class.java))
|
||||
.thenReturn(mockRoleManager)
|
||||
listModel = MediaRoutingControlAppsListModel(context)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun modelResourceIdAndProperties() {
|
||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_routing_control_title)
|
||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.allow_media_routing_control)
|
||||
assertThat(listModel.footerResId).isEqualTo(R.string.allow_media_routing_description)
|
||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MEDIA_ROUTING_CONTROL)
|
||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.MEDIA_ROUTING_CONTROL)
|
||||
assertThat(listModel.setModeByUid).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsTrue_shouldChangeAppControllerModeToAllowed() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, true)
|
||||
|
||||
assertThat(fakeAppOpController.getMode()).isEqualTo(AppOpsManager.MODE_ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsTrue_shouldLogPermissionToggleActionAsAllowed() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, true)
|
||||
|
||||
verify(metricsFeatureProvider)
|
||||
.action(context, SettingsEnums.MEDIA_ROUTING_CONTROL, VALUE_LOGGING_ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsFalse_shouldChangeAppControllerModeToErrored() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, false)
|
||||
|
||||
assertThat(fakeAppOpController.getMode()).isEqualTo(AppOpsManager.MODE_ERRORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsFalse_shouldLogPermissionToggleActionAsDenied() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, false)
|
||||
|
||||
verify(metricsFeatureProvider)
|
||||
.action(context, SettingsEnums.MEDIA_ROUTING_CONTROL, VALUE_LOGGING_DENIED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_permissionRequestedByAppAndWatchCompanionRoleAssigned_shouldReturnTrue() {
|
||||
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf(PACKAGE_NAME))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_permissionNotRequestedByAppButWatchCompanionRoleAssigned_shouldReturnFalse() {
|
||||
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
||||
val permissionNotRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = false,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf(PACKAGE_NAME))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionNotRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_permissionRequestedByAppButWatchCompanionRoleNotAssigned_shouldReturnFalse() {
|
||||
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf("other.package.name"))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_withFlagDisabled_shouldReturnFalse() {
|
||||
setFlagsRule.disableFlags(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf(PACKAGE_NAME))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isFalse()
|
||||
}
|
||||
|
||||
private class FakeAppOpsController(fakeMode: Int) : IAppOpsController {
|
||||
|
||||
override val mode = MutableStateFlow(fakeMode)
|
||||
|
||||
override fun setAllowed(allowed: Boolean) {
|
||||
mode.value = if (allowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
|
||||
}
|
||||
|
||||
override fun getMode(): Int = mode.value
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PACKAGE_NAME = "test.package.name"
|
||||
const val VALUE_LOGGING_ALLOWED = 1
|
||||
const val VALUE_LOGGING_DENIED = 0
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user