Fix DataUsageListTest

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

Bug: 315449973
Test: manual - on DataUsageList
Test: unit test
Change-Id: Ief373becb4ac8ab1ba93b8ff3c594b5682c4821e
This commit is contained in:
Chaohui Wang
2023-12-19 12:32:09 +08:00
parent 40f6264056
commit 83c46eb1b6
4 changed files with 156 additions and 199 deletions

View File

@@ -30,6 +30,7 @@ import androidx.annotation.VisibleForTesting
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.preference.Preference import androidx.preference.Preference
import com.android.settings.R import com.android.settings.R
import com.android.settings.dashboard.DashboardFragment
import com.android.settings.datausage.lib.BillingCycleRepository import com.android.settings.datausage.lib.BillingCycleRepository
import com.android.settings.datausage.lib.NetworkUsageData import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settings.network.MobileNetworkRepository 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]. * to inspect based on usage cycle and control through [NetworkPolicy].
*/ */
@OpenForTesting @OpenForTesting
open class DataUsageList : DataUsageBaseFragment() { open class DataUsageList : DashboardFragment() {
@JvmField
@VisibleForTesting @VisibleForTesting
var template: NetworkTemplate? = null var template: NetworkTemplate? = null
private set
@JvmField
@VisibleForTesting @VisibleForTesting
var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID 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 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 private val viewModel: DataUsageListViewModel by viewModels()
var dataUsageListHeaderController: DataUsageListHeaderController? = null
override fun getMetricsCategory() = SettingsEnums.DATA_USAGE_LIST override fun getMetricsCategory() = SettingsEnums.DATA_USAGE_LIST
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
billingCycleRepository = BillingCycleRepository(requireContext())
if (requireContext().userManager.isGuestUser) { if (requireContext().userManager.isGuestUser) {
Log.e(TAG, "This setting isn't available for guest user") Log.e(TAG, "This setting isn't available for guest user")
EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user") EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user")
finish() finish()
return return
} }
billingCycleRepository = createBillingCycleRepository()
if (!billingCycleRepository.isBandwidthControlEnabled()) { if (!billingCycleRepository.isBandwidthControlEnabled()) {
Log.w(TAG, "No bandwidth control; leaving") Log.w(TAG, "No bandwidth control; leaving")
finish() finish()
return return
} }
usageAmount = findPreference(KEY_USAGE_AMOUNT)!! usageAmount = findPreference(KEY_USAGE_AMOUNT)
processArgument() processArgument()
val template = template val template = template
if (template == null) { if (template == null) {
@@ -94,12 +94,9 @@ open class DataUsageList : DataUsageBaseFragment() {
init(template) init(template)
} }
chartDataUsagePreferenceController = use(ChartDataUsagePreferenceController::class.java) chartDataUsagePreferenceController = use(ChartDataUsagePreferenceController::class.java)
chartDataUsagePreferenceController.init(template) .apply { init(template) }
} }
@VisibleForTesting
open fun createBillingCycleRepository() = BillingCycleRepository(requireContext())
override fun onViewCreated(v: View, savedInstanceState: Bundle?) { override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
super.onViewCreated(v, savedInstanceState) super.onViewCreated(v, savedInstanceState)
@@ -117,10 +114,10 @@ open class DataUsageList : DataUsageBaseFragment() {
::updateSelectedCycle, ::updateSelectedCycle,
) )
viewModel.cyclesFlow.collectLatestWithLifecycle(viewLifecycleOwner) { cycles -> viewModel.cyclesFlow.collectLatestWithLifecycle(viewLifecycleOwner) { cycles ->
dataUsageListAppsController.updateCycles(cycles) dataUsageListAppsController?.updateCycles(cycles)
} }
viewModel.chartDataFlow.collectLatestWithLifecycle(viewLifecycleOwner) { chartData -> viewModel.chartDataFlow.collectLatestWithLifecycle(viewLifecycleOwner) { chartData ->
chartDataUsagePreferenceController.update(chartData) chartDataUsagePreferenceController?.update(chartData)
} }
} }
@@ -128,7 +125,7 @@ open class DataUsageList : DataUsageBaseFragment() {
override fun getLogTag() = TAG override fun getLogTag() = TAG
fun processArgument() { private fun processArgument() {
arguments?.let { arguments?.let {
subId = it.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID) subId = it.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID)
template = it.getParcelable(EXTRA_NETWORK_TEMPLATE, NetworkTemplate::class.java) template = it.getParcelable(EXTRA_NETWORK_TEMPLATE, NetworkTemplate::class.java)
@@ -145,8 +142,7 @@ open class DataUsageList : DataUsageBaseFragment() {
} }
} }
@VisibleForTesting private fun updateSubscriptionInfoEntity() {
open fun updateSubscriptionInfoEntity() {
ThreadUtils.postOnBackgroundThread { ThreadUtils.postOnBackgroundThread {
subscriptionInfoEntity = subscriptionInfoEntity =
MobileNetworkRepository.getInstance(context).getSubInfoById(subId.toString()) 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]. */ /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
@VisibleForTesting private fun updatePolicy() {
fun updatePolicy() {
val isBillingCycleModifiable = isBillingCycleModifiable() val isBillingCycleModifiable = isBillingCycleModifiable()
dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable) dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable)
chartDataUsagePreferenceController.setBillingCycleModifiable(isBillingCycleModifiable) chartDataUsagePreferenceController?.setBillingCycleModifiable(isBillingCycleModifiable)
} }
@VisibleForTesting private fun isBillingCycleModifiable(): Boolean =
open fun isBillingCycleModifiable(): Boolean { billingCycleRepository.isModifiable(subId) &&
return (billingCycleRepository.isModifiable(subId) &&
requireContext().getSystemService(SubscriptionManager::class.java)!! requireContext().getSystemService(SubscriptionManager::class.java)!!
.getActiveSubscriptionInfo(subId) != null) .getActiveSubscriptionInfo(subId) != null
}
/** /**
* Updates the chart and detail data when initial loaded or selected cycle changed. * 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) { private fun updateSelectedCycle(usageData: NetworkUsageData) {
Log.d(TAG, "showing cycle $usageData") Log.d(TAG, "showing cycle $usageData")
usageAmount.title = usageData.getDataUsedString(requireContext()) usageAmount?.title = usageData.getDataUsedString(requireContext())
viewModel.selectedCycleFlow.value = usageData viewModel.selectedCycleFlow.value = usageData
updateApps(usageData) updateApps(usageData)
@@ -182,7 +175,7 @@ open class DataUsageList : DataUsageBaseFragment() {
/** Updates applications data usage. */ /** Updates applications data usage. */
private fun updateApps(usageData: NetworkUsageData) { private fun updateApps(usageData: NetworkUsageData) {
dataUsageListAppsController.update( dataUsageListAppsController?.update(
carrierId = subscriptionInfoEntity?.carrierId, carrierId = subscriptionInfoEntity?.carrierId,
startTime = usageData.startTime, startTime = usageData.startTime,
endTime = usageData.endTime, endTime = usageData.endTime,

View File

@@ -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>(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
}
}

View File

@@ -34,6 +34,7 @@ android_test {
"androidx.compose.runtime_runtime", "androidx.compose.runtime_runtime",
"androidx.test.ext.junit", "androidx.test.ext.junit",
"androidx.test.runner", "androidx.test.runner",
"androidx.fragment_fragment-testing",
"flag-junit", "flag-junit",
"mockito-target-extended-minus-junit4", "mockito-target-extended-minus-junit4",
], ],

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datausage
import android.content.Context
import android.content.Intent
import android.net.NetworkTemplate
import android.os.UserManager
import android.provider.Settings
import android.telephony.SubscriptionManager
import androidx.core.os.bundleOf
import androidx.fragment.app.testing.launchFragment
import androidx.fragment.app.testing.withFragment
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
private val mockUserManager: UserManager = mock<UserManager>()
private val mockContext: Context = spy(ApplicationProvider.getApplicationContext()) {
on { userManager } doReturn mockUserManager
}
private var fakeIntent = Intent()
@RunWith(AndroidJUnit4::class)
class DataUsageListTest {
@Before
fun setUp() {
mockUserManager.stub {
on { isGuestUser } doReturn false
}
fakeIntent = Intent()
}
@Test
fun launchFragment_withoutArguments_finish() {
val scenario = launchFragment<TestDataUsageList>(initialState = Lifecycle.State.CREATED)
scenario.withFragment {
assertThat(template).isNull()
assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
assertThat(activity!!.isFinishing).isTrue()
}
}
@Test
fun launchFragment_isGuestUser_finish() {
mockUserManager.stub {
on { isGuestUser } doReturn true
}
val fragmentArgs = bundleOf(
DataUsageList.EXTRA_NETWORK_TEMPLATE to mock<NetworkTemplate>(),
DataUsageList.EXTRA_SUB_ID to 3,
)
val scenario = launchFragment<TestDataUsageList>(
fragmentArgs = fragmentArgs,
initialState = Lifecycle.State.CREATED,
)
scenario.withFragment {
assertThat(activity!!.isFinishing).isTrue()
}
}
@Test
fun launchFragment_withArguments_getTemplateFromArgument() {
val fragmentArgs = bundleOf(
DataUsageList.EXTRA_NETWORK_TEMPLATE to mock<NetworkTemplate>(),
DataUsageList.EXTRA_SUB_ID to 3,
)
val scenario = launchFragment<TestDataUsageList>(
fragmentArgs = fragmentArgs,
initialState = Lifecycle.State.CREATED,
)
scenario.withFragment {
assertThat(template).isNotNull()
assertThat(subId).isEqualTo(3)
assertThat(activity!!.isFinishing).isFalse()
}
}
@Test
fun launchFragment_withIntent_getTemplateFromIntent() {
fakeIntent = Intent().apply {
putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mock<NetworkTemplate>())
putExtra(Settings.EXTRA_SUB_ID, 2)
}
val scenario = launchFragment<TestDataUsageList>(initialState = Lifecycle.State.CREATED)
scenario.withFragment {
assertThat(template).isNotNull()
assertThat(subId).isEqualTo(2)
assertThat(activity!!.isFinishing).isFalse()
}
}
}
class TestDataUsageList : DataUsageList() {
override fun getContext() = mockContext
override fun getIntent() = fakeIntent
}