diff --git a/src/com/android/settings/datausage/DataUsageList.kt b/src/com/android/settings/datausage/DataUsageList.kt index 6a187d8caf8..3083fb75443 100644 --- a/src/com/android/settings/datausage/DataUsageList.kt +++ b/src/com/android/settings/datausage/DataUsageList.kt @@ -30,6 +30,7 @@ import androidx.annotation.VisibleForTesting import androidx.fragment.app.viewModels import androidx.preference.Preference import com.android.settings.R +import com.android.settings.dashboard.DashboardFragment import com.android.settings.datausage.lib.BillingCycleRepository import com.android.settings.datausage.lib.NetworkUsageData import com.android.settings.network.MobileNetworkRepository @@ -45,43 +46,42 @@ import kotlin.jvm.optionals.getOrNull * to inspect based on usage cycle and control through [NetworkPolicy]. */ @OpenForTesting -open class DataUsageList : DataUsageBaseFragment() { - @JvmField +open class DataUsageList : DashboardFragment() { @VisibleForTesting var template: NetworkTemplate? = null + private set - @JvmField @VisibleForTesting var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID + private set - private lateinit var usageAmount: Preference - private var subscriptionInfoEntity: SubscriptionInfoEntity? = null - private lateinit var dataUsageListAppsController: DataUsageListAppsController - private lateinit var chartDataUsagePreferenceController: ChartDataUsagePreferenceController private lateinit var billingCycleRepository: BillingCycleRepository - private val viewModel: DataUsageListViewModel by viewModels() + private var usageAmount: Preference? = null + private var subscriptionInfoEntity: SubscriptionInfoEntity? = null + private var dataUsageListAppsController: DataUsageListAppsController? = null + private var chartDataUsagePreferenceController: ChartDataUsagePreferenceController? = null + private var dataUsageListHeaderController: DataUsageListHeaderController? = null - @VisibleForTesting - var dataUsageListHeaderController: DataUsageListHeaderController? = null + private val viewModel: DataUsageListViewModel by viewModels() override fun getMetricsCategory() = SettingsEnums.DATA_USAGE_LIST override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + billingCycleRepository = BillingCycleRepository(requireContext()) if (requireContext().userManager.isGuestUser) { Log.e(TAG, "This setting isn't available for guest user") EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user") finish() return } - billingCycleRepository = createBillingCycleRepository() if (!billingCycleRepository.isBandwidthControlEnabled()) { Log.w(TAG, "No bandwidth control; leaving") finish() return } - usageAmount = findPreference(KEY_USAGE_AMOUNT)!! + usageAmount = findPreference(KEY_USAGE_AMOUNT) processArgument() val template = template if (template == null) { @@ -94,12 +94,9 @@ open class DataUsageList : DataUsageBaseFragment() { init(template) } chartDataUsagePreferenceController = use(ChartDataUsagePreferenceController::class.java) - chartDataUsagePreferenceController.init(template) + .apply { init(template) } } - @VisibleForTesting - open fun createBillingCycleRepository() = BillingCycleRepository(requireContext()) - override fun onViewCreated(v: View, savedInstanceState: Bundle?) { super.onViewCreated(v, savedInstanceState) @@ -117,10 +114,10 @@ open class DataUsageList : DataUsageBaseFragment() { ::updateSelectedCycle, ) viewModel.cyclesFlow.collectLatestWithLifecycle(viewLifecycleOwner) { cycles -> - dataUsageListAppsController.updateCycles(cycles) + dataUsageListAppsController?.updateCycles(cycles) } viewModel.chartDataFlow.collectLatestWithLifecycle(viewLifecycleOwner) { chartData -> - chartDataUsagePreferenceController.update(chartData) + chartDataUsagePreferenceController?.update(chartData) } } @@ -128,7 +125,7 @@ open class DataUsageList : DataUsageBaseFragment() { override fun getLogTag() = TAG - fun processArgument() { + private fun processArgument() { arguments?.let { subId = it.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID) template = it.getParcelable(EXTRA_NETWORK_TEMPLATE, NetworkTemplate::class.java) @@ -145,8 +142,7 @@ open class DataUsageList : DataUsageBaseFragment() { } } - @VisibleForTesting - open fun updateSubscriptionInfoEntity() { + private fun updateSubscriptionInfoEntity() { ThreadUtils.postOnBackgroundThread { subscriptionInfoEntity = MobileNetworkRepository.getInstance(context).getSubInfoById(subId.toString()) @@ -154,19 +150,16 @@ open class DataUsageList : DataUsageBaseFragment() { } /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */ - @VisibleForTesting - fun updatePolicy() { + private fun updatePolicy() { val isBillingCycleModifiable = isBillingCycleModifiable() dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable) - chartDataUsagePreferenceController.setBillingCycleModifiable(isBillingCycleModifiable) + chartDataUsagePreferenceController?.setBillingCycleModifiable(isBillingCycleModifiable) } - @VisibleForTesting - open fun isBillingCycleModifiable(): Boolean { - return (billingCycleRepository.isModifiable(subId) && + private fun isBillingCycleModifiable(): Boolean = + billingCycleRepository.isModifiable(subId) && requireContext().getSystemService(SubscriptionManager::class.java)!! - .getActiveSubscriptionInfo(subId) != null) - } + .getActiveSubscriptionInfo(subId) != null /** * Updates the chart and detail data when initial loaded or selected cycle changed. @@ -174,7 +167,7 @@ open class DataUsageList : DataUsageBaseFragment() { private fun updateSelectedCycle(usageData: NetworkUsageData) { Log.d(TAG, "showing cycle $usageData") - usageAmount.title = usageData.getDataUsedString(requireContext()) + usageAmount?.title = usageData.getDataUsedString(requireContext()) viewModel.selectedCycleFlow.value = usageData updateApps(usageData) @@ -182,7 +175,7 @@ open class DataUsageList : DataUsageBaseFragment() { /** Updates applications data usage. */ private fun updateApps(usageData: NetworkUsageData) { - dataUsageListAppsController.update( + dataUsageListAppsController?.update( carrierId = subscriptionInfoEntity?.carrierId, startTime = usageData.startTime, endTime = usageData.endTime, diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt deleted file mode 100644 index 39b844680d2..00000000000 --- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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.Bundle -import android.os.UserManager -import android.provider.Settings -import androidx.preference.Preference -import androidx.test.core.app.ApplicationProvider -import com.android.settings.datausage.DataUsageListTest.ShadowDataUsageBaseFragment -import com.android.settings.datausage.TemplatePreference.NetworkServices -import com.android.settings.datausage.lib.BillingCycleRepository -import com.android.settings.testutils.FakeFeatureFactory -import com.android.settingslib.NetworkPolicyEditor -import com.android.settingslib.core.AbstractPreferenceController -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.doNothing -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.Spy -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import org.robolectric.annotation.Implementation -import org.robolectric.annotation.Implements -import org.robolectric.util.ReflectionHelpers - -@RunWith(RobolectricTestRunner::class) -@Config(shadows = [ShadowDataUsageBaseFragment::class]) -class DataUsageListTest { - @get:Rule - val mockito: MockitoRule = MockitoJUnit.rule() - - @Mock - private lateinit var networkServices: NetworkServices - - @Mock - private lateinit var userManager: UserManager - - @Mock - private lateinit var billingCycleRepository: BillingCycleRepository - - @Mock - private lateinit var dataUsageListHeaderController: DataUsageListHeaderController - - @Spy - private val context: Context = ApplicationProvider.getApplicationContext() - - @Spy - private val dataUsageList = TestDataUsageList() - - @Before - fun setUp() { - FakeFeatureFactory.setupForTest() - networkServices.mPolicyEditor = mock(NetworkPolicyEditor::class.java) - doReturn(context).`when`(dataUsageList).context - doReturn(userManager).`when`(context).getSystemService(UserManager::class.java) - doReturn(false).`when`(userManager).isGuestUser - ReflectionHelpers.setField(dataUsageList, "services", networkServices) - doNothing().`when`(dataUsageList).updateSubscriptionInfoEntity() - `when`(billingCycleRepository.isBandwidthControlEnabled()).thenReturn(true) - dataUsageList.dataUsageListHeaderController = dataUsageListHeaderController - } - - @Test - fun onCreate_isNotGuestUser_shouldNotFinish() { - dataUsageList.template = mock(NetworkTemplate::class.java) - doReturn(false).`when`(userManager).isGuestUser - doNothing().`when`(dataUsageList).processArgument() - dataUsageList.onCreate(null) - verify(dataUsageList, never()).finish() - } - - @Test - fun onCreate_isGuestUser_shouldFinish() { - doReturn(true).`when`(userManager).isGuestUser - dataUsageList.onCreate(null) - verify(dataUsageList).finish() - } - - @Test - fun processArgument_shouldGetTemplateFromArgument() { - val args = Bundle() - args.putParcelable( - DataUsageList.EXTRA_NETWORK_TEMPLATE, mock( - NetworkTemplate::class.java - ) - ) - args.putInt(DataUsageList.EXTRA_SUB_ID, 3) - dataUsageList.arguments = args - dataUsageList.processArgument() - assertThat(dataUsageList.template).isNotNull() - assertThat(dataUsageList.subId).isEqualTo(3) - } - - @Test - fun processArgument_fromIntent_shouldGetTemplateFromIntent() { - val intent = Intent() - intent.putExtra( - Settings.EXTRA_NETWORK_TEMPLATE, mock( - NetworkTemplate::class.java - ) - ) - intent.putExtra(Settings.EXTRA_SUB_ID, 3) - doReturn(intent).`when`(dataUsageList).intent - dataUsageList.processArgument() - assertThat(dataUsageList.template).isNotNull() - assertThat(dataUsageList.subId).isEqualTo(3) - } - - @Test - fun updatePolicy_setConfigButtonVisible() { - dataUsageList.template = mock(NetworkTemplate::class.java) - dataUsageList.onCreate(null) - - dataUsageList.updatePolicy() - - verify(dataUsageListHeaderController).setConfigButtonVisible(true) - } - - @Implements(DataUsageBaseFragment::class) - class ShadowDataUsageBaseFragment { - @Implementation - fun onCreate(@Suppress("UNUSED_PARAMETER") icicle: Bundle?) { - // do nothing - } - } - - open inner class TestDataUsageList : DataUsageList() { - override fun use(clazz: Class): T = mock(clazz) - - @Suppress("UNCHECKED_CAST") - override fun findPreference(key: CharSequence): T = - mock(Preference::class.java) as T - - public override fun getIntent() = Intent() - - override fun createBillingCycleRepository() = billingCycleRepository - - override fun isBillingCycleModifiable() = true - } -} diff --git a/tests/spa_unit/Android.bp b/tests/spa_unit/Android.bp index c3e99f75dcf..4df625420eb 100644 --- a/tests/spa_unit/Android.bp +++ b/tests/spa_unit/Android.bp @@ -34,6 +34,7 @@ android_test { "androidx.compose.runtime_runtime", "androidx.test.ext.junit", "androidx.test.runner", + "androidx.fragment_fragment-testing", "flag-junit", "mockito-target-extended-minus-junit4", ], diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListTest.kt new file mode 100644 index 00000000000..29ec0eed352 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListTest.kt @@ -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() + +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(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(), + DataUsageList.EXTRA_SUB_ID to 3, + ) + + val scenario = launchFragment( + 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(), + DataUsageList.EXTRA_SUB_ID to 3, + ) + + val scenario = launchFragment( + 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()) + putExtra(Settings.EXTRA_SUB_ID, 2) + } + + val scenario = launchFragment(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 +}