Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83c46eb1 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Fix DataUsageListTest

Using androidx.fragment.app.testing.launchFragment to rewrite the test.

Bug: 315449973
Test: manual - on DataUsageList
Test: unit test
Change-Id: Ief373becb4ac8ab1ba93b8ff3c594b5682c4821e
parent 40f62640
Loading
Loading
Loading
Loading
+24 −31
Original line number Diff line number Diff line
@@ -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,
+0 −168
Original line number Diff line number Diff line
/*
 * 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>(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 <T : AbstractPreferenceController?> use(clazz: Class<T>): T = mock(clazz)

        @Suppress("UNCHECKED_CAST")
        override fun <T : Preference?> findPreference(key: CharSequence): T =
            mock(Preference::class.java) as T

        public override fun getIntent() = Intent()

        override fun createBillingCycleRepository() = billingCycleRepository

        override fun isBillingCycleModifiable() = true
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -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",
    ],
+131 −0
Original line number Diff line number Diff line
/*
 * 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
}