Fix DataUsageSummaryPreferenceController ANR
By off load data loading to background. Fix: 295260929 Test: manual - on Mobile Settings Test: unit test Change-Id: Ib2ef19301b1e97af8a7f3861829779c3b70da4a4
This commit is contained in:
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.SubscriptionPlan;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.RecurrenceRule;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settingslib.net.DataUsageController;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowEntityHeaderController.class)
|
||||
public class DataUsageSummaryPreferenceControllerTest {
|
||||
|
||||
private static final long UPDATE_BACKOFF_MS = TimeUnit.MINUTES.toMillis(13);
|
||||
private static final long CYCLE_BACKOFF_MS = TimeUnit.DAYS.toMillis(6);
|
||||
private static final long CYCLE_LENGTH_MS = TimeUnit.DAYS.toMillis(30);
|
||||
private static final long USAGE1 = 373 * BillingCycleSettings.MIB_IN_BYTES;
|
||||
private static final long LIMIT1 = BillingCycleSettings.GIB_IN_BYTES;
|
||||
private static final String CARRIER_NAME = "z-mobile";
|
||||
private static final String PERIOD = "Feb";
|
||||
|
||||
@Mock
|
||||
private DataUsageController mDataUsageController;
|
||||
@Mock
|
||||
private DataUsageSummaryPreference mSummaryPreference;
|
||||
@Mock
|
||||
private NetworkTemplate mNetworkTemplate;
|
||||
@Mock
|
||||
private SubscriptionInfo mSubscriptionInfo;
|
||||
@Mock
|
||||
private SubscriptionPlan mSubscriptionPlan;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private EntityHeaderController mHeaderController;
|
||||
@Mock
|
||||
private TelephonyManager mTelephonyManager;
|
||||
@Mock
|
||||
private PackageManager mPm;
|
||||
|
||||
private DataUsageInfoController mDataInfoController;
|
||||
|
||||
private FakeFeatureFactory mFactory;
|
||||
private FragmentActivity mActivity;
|
||||
private Context mContext;
|
||||
private DataUsageSummaryPreferenceController mController;
|
||||
private int mDefaultSubscriptionId;
|
||||
private List<SubscriptionPlan> mSubscriptionPlans;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
|
||||
doReturn("%1$s %2%s").when(mContext)
|
||||
.getString(com.android.internal.R.string.fileSizeSuffix);
|
||||
|
||||
mDefaultSubscriptionId = 1234;
|
||||
mSubscriptionPlans = new ArrayList<SubscriptionPlan>();
|
||||
|
||||
mFactory = FakeFeatureFactory.setupForTest();
|
||||
when(mFactory.metricsFeatureProvider.getMetricsCategory(any(Object.class)))
|
||||
.thenReturn(MetricsProto.MetricsEvent.SETTINGS_APP_NOTIF_CATEGORY);
|
||||
ShadowEntityHeaderController.setUseMock(mHeaderController);
|
||||
|
||||
mDataInfoController = spy(new DataUsageInfoController());
|
||||
doReturn(-1L).when(mDataInfoController).getSummaryLimit(any());
|
||||
|
||||
mActivity = spy(Robolectric.buildActivity(FragmentActivity.class).get());
|
||||
doReturn(mTelephonyManager).when(mActivity).getSystemService(TelephonyManager.class);
|
||||
doReturn(mTelephonyManager).when(mTelephonyManager)
|
||||
.createForSubscriptionId(mDefaultSubscriptionId);
|
||||
doReturn(mPm).when(mActivity).getPackageManager();
|
||||
doReturn(TelephonyManager.SIM_STATE_READY).when(mTelephonyManager).getSimState();
|
||||
|
||||
mController = spy(new DataUsageSummaryPreferenceController(
|
||||
mDataUsageController,
|
||||
mDataInfoController,
|
||||
mNetworkTemplate,
|
||||
mActivity, mDefaultSubscriptionId));
|
||||
doReturn(null).when(mController).getSubscriptionInfo(
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
doReturn(null).when(mController).getSubscriptionPlans(
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
|
||||
doReturn(CARRIER_NAME).when(mSubscriptionInfo).getCarrierName();
|
||||
doReturn(mSubscriptionInfo).when(mController).getSubscriptionInfo(mDefaultSubscriptionId);
|
||||
doReturn(mSubscriptionPlans).when(mController).getSubscriptionPlans(mDefaultSubscriptionId);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowEntityHeaderController.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_onePlan_basic() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
|
||||
createTestDataPlan(info.cycleStart, info.cycleEnd);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
|
||||
|
||||
// TODO (b/170330084): return intent instead of null for mSummaryPreference
|
||||
verify(mSummaryPreference).setUsageInfo((info.cycleEnd / 1000) * 1000,
|
||||
now - UPDATE_BACKOFF_MS,
|
||||
CARRIER_NAME, 1 /* numPlans */);
|
||||
verify(mSummaryPreference).setChartEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_noPlan_basic() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
|
||||
|
||||
verify(mSummaryPreference).setUsageInfo(
|
||||
info.cycleEnd,
|
||||
-1L /* snapshotTime */,
|
||||
CARRIER_NAME,
|
||||
0 /* numPlans */);
|
||||
verify(mSummaryPreference).setChartEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_noCarrier_basic() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
doReturn(null).when(mSubscriptionInfo).getCarrierName();
|
||||
setupTestDataUsage(LIMIT1, USAGE1, -1L /* snapshotTime */);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
|
||||
|
||||
verify(mSummaryPreference).setUsageInfo(
|
||||
info.cycleEnd,
|
||||
-1L /* snapshotTime */,
|
||||
null /* carrierName */,
|
||||
0 /* numPlans */);
|
||||
verify(mSummaryPreference).setChartEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_noPlanData_basic() {
|
||||
final long now = System.currentTimeMillis();
|
||||
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
doReturn(null).when(mSubscriptionInfo).getCarrierName();
|
||||
setupTestDataUsage(-1L /* dataPlanSize */, USAGE1, -1L /* snapshotTime */);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
|
||||
verify(mSummaryPreference).setUsageInfo(
|
||||
info.cycleEnd,
|
||||
-1L /* snapshotTime */,
|
||||
null /* carrierName */,
|
||||
0 /* numPlans */);
|
||||
verify(mSummaryPreference).setChartEnabled(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_noLimitNoWarning() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
info.warningLevel = 0L;
|
||||
info.limitLevel = 0L;
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
verify(mSummaryPreference).setLimitInfo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_warningOnly() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
info.warningLevel = BillingCycleSettings.MIB_IN_BYTES;
|
||||
info.limitLevel = 0L;
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("1.00 MB data warning");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_limitOnly() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
info.warningLevel = 0L;
|
||||
info.limitLevel = BillingCycleSettings.MIB_IN_BYTES;
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("1.00 MB data limit");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryUpdate_limitAndWarning() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
|
||||
info.warningLevel = BillingCycleSettings.MIB_IN_BYTES;
|
||||
info.limitLevel = BillingCycleSettings.MIB_IN_BYTES;
|
||||
|
||||
doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
|
||||
setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
|
||||
|
||||
mController.updateState(mSummaryPreference);
|
||||
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mSummaryPreference).setLimitInfo(captor.capture());
|
||||
CharSequence value = captor.getValue();
|
||||
assertThat(value.toString()).isEqualTo("1.00 MB data warning / 1.00 MB data limit");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMobileData_preferenceAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMobileData_noSim_preferenceDisabled() {
|
||||
final int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
mController.init(subscriptionId);
|
||||
mController.mDataUsageController = mDataUsageController;
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
private DataUsageController.DataUsageInfo createTestDataUsageInfo(long now) {
|
||||
DataUsageController.DataUsageInfo info = new DataUsageController.DataUsageInfo();
|
||||
info.carrier = CARRIER_NAME;
|
||||
info.period = PERIOD;
|
||||
info.startDate = now;
|
||||
info.limitLevel = LIMIT1;
|
||||
info.warningLevel = LIMIT1 >> 1;
|
||||
info.usageLevel = USAGE1;
|
||||
info.cycleStart = now - CYCLE_BACKOFF_MS;
|
||||
info.cycleEnd = info.cycleStart + CYCLE_LENGTH_MS;
|
||||
return info;
|
||||
}
|
||||
|
||||
private void setupTestDataUsage(long dataPlanSize, long dataUsageSize, long snapshotTime) {
|
||||
doReturn(dataPlanSize).when(mSubscriptionPlan).getDataLimitBytes();
|
||||
doReturn(dataUsageSize).when(mSubscriptionPlan).getDataUsageBytes();
|
||||
doReturn(snapshotTime).when(mSubscriptionPlan).getDataUsageTime();
|
||||
|
||||
doReturn(dataPlanSize).when(mDataInfoController).getSummaryLimit(any());
|
||||
}
|
||||
|
||||
private void createTestDataPlan(long startTime, long endTime) {
|
||||
final RecurrenceRule recurrenceRule = new RecurrenceRule(
|
||||
Instant.ofEpochMilli(startTime).atZone(ZoneId.systemDefault()),
|
||||
Instant.ofEpochMilli(endTime).atZone(ZoneId.systemDefault()),
|
||||
null);
|
||||
doReturn(recurrenceRule).when(mSubscriptionPlan).getCycleRule();
|
||||
mSubscriptionPlans.add(mSubscriptionPlan);
|
||||
}
|
||||
}
|
||||
@@ -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,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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,8 @@ 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 java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -96,9 +94,6 @@ class NetworkCycleDataRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.testutils
|
||||
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
fun zonedDateTime(epochMilli: Long): ZonedDateTime? =
|
||||
ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault())
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.datausage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -90,7 +91,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
|
||||
@Test
|
||||
public void testSetUsageInfo_withNoDataPlans_carrierInfoNotShown() {
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getCarrierInfo(mHolder).getVisibility())
|
||||
@@ -197,7 +198,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
|
||||
@Test
|
||||
public void testSetUsageInfo_withNoDataPlans_usageTitleNotShown() {
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getUsageTitle(mHolder).getVisibility()).isEqualTo(View.GONE);
|
||||
@@ -216,7 +217,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
public void testSetUsageInfo_cycleRemainingTimeIsLessOneDay() {
|
||||
// just under one day
|
||||
final long cycleEnd = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(23);
|
||||
mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
|
||||
@@ -229,7 +230,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
@Test
|
||||
public void testSetUsageInfo_cycleRemainingTimeNegativeDaysLeft_shouldDisplayNoneLeft() {
|
||||
final long cycleEnd = System.currentTimeMillis() - 1L;
|
||||
mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
|
||||
@@ -243,7 +244,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
final int daysLeft = 3;
|
||||
final long cycleEnd = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(daysLeft)
|
||||
+ TimeUnit.HOURS.toMillis(1);
|
||||
mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
|
||||
@@ -329,8 +330,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
|
||||
mSummaryPreference.setUsageNumbers(
|
||||
BillingCycleSettings.MIB_IN_BYTES,
|
||||
10 * BillingCycleSettings.MIB_IN_BYTES,
|
||||
true /* hasMobileData */);
|
||||
10 * BillingCycleSettings.MIB_IN_BYTES);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
|
||||
@@ -349,8 +349,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
|
||||
mSummaryPreference.setUsageNumbers(
|
||||
11 * BillingCycleSettings.MIB_IN_BYTES,
|
||||
10 * BillingCycleSettings.MIB_IN_BYTES,
|
||||
true /* hasMobileData */);
|
||||
10 * BillingCycleSettings.MIB_IN_BYTES);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
|
||||
@@ -364,9 +363,9 @@ public class DataUsageSummaryPreferenceTest {
|
||||
|
||||
@Test
|
||||
public void testSetUsageInfo_withUsageInfo_dataUsageShown() {
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
|
||||
mSummaryPreference.setUsageNumbers(
|
||||
BillingCycleSettings.MIB_IN_BYTES, -1L, true /* hasMobileData */);
|
||||
BillingCycleSettings.MIB_IN_BYTES, -1L);
|
||||
|
||||
mSummaryPreference.onBindViewHolder(mHolder);
|
||||
assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
|
||||
@@ -383,8 +382,7 @@ public class DataUsageSummaryPreferenceTest {
|
||||
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
|
||||
mSummaryPreference.setUsageNumbers(
|
||||
BillingCycleSettings.MIB_IN_BYTES,
|
||||
10 * BillingCycleSettings.MIB_IN_BYTES,
|
||||
true /* hasMobileData */);
|
||||
10 * BillingCycleSettings.MIB_IN_BYTES);
|
||||
|
||||
int data_used_formatted_id = ResourcesUtils.getResourcesId(
|
||||
mContext, "string", "data_used_formatted");
|
||||
|
||||
Reference in New Issue
Block a user