1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.datausage;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.spy;
23 import static org.mockito.Mockito.when;
24 
25 import android.content.Context;
26 import android.content.res.Resources;
27 import android.graphics.Typeface;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.View.MeasureSpec;
31 import android.widget.TextView;
32 
33 import androidx.preference.PreferenceViewHolder;
34 import androidx.test.core.app.ApplicationProvider;
35 import androidx.test.ext.junit.runners.AndroidJUnit4;
36 
37 import com.android.settings.testutils.ResourcesUtils;
38 import com.android.settingslib.Utils;
39 
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.MockitoAnnotations;
44 
45 import java.util.concurrent.TimeUnit;
46 
47 @RunWith(AndroidJUnit4.class)
48 public class DataUsageSummaryPreferenceTest {
49 
50     private static final long CYCLE_DURATION_MILLIS = 1000000000L;
51     private static final long UPDATE_LAG_MILLIS = 10000000L;
52     private static final String FAKE_CARRIER = "z-mobile";
53 
54     private Context mContext;
55     private Resources mResources;
56     private PreferenceViewHolder mHolder;
57     private DataUsageSummaryPreference mSummaryPreference;
58 
59     private long mCycleEnd;
60     private long mUpdateTime;
61 
62 
63     @Before
setUp()64     public void setUp() {
65         MockitoAnnotations.initMocks(this);
66         mContext = spy(ApplicationProvider.getApplicationContext());
67         mResources = spy(mContext.getResources());
68         when(mContext.getResources()).thenReturn(mResources);
69         mSummaryPreference = spy(new DataUsageSummaryPreference(mContext, null /* attrs */));
70         LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
71         View view = inflater.inflate(
72                 mSummaryPreference.getLayoutResource(),
73                 null /* root */, false /* attachToRoot */);
74 
75         mHolder = spy(PreferenceViewHolder.createInstanceForTests(view));
76         assertThat(mSummaryPreference.getDataUsed(mHolder)).isNotNull();
77 
78         final long now = System.currentTimeMillis();
79         mCycleEnd = now + CYCLE_DURATION_MILLIS;
80         mUpdateTime = now - UPDATE_LAG_MILLIS;
81     }
82 
83     @Test
testSetUsageInfo_withDataPlans_carrierInfoShown()84     public void testSetUsageInfo_withDataPlans_carrierInfoShown() {
85         mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
86 
87         mSummaryPreference.onBindViewHolder(mHolder);
88         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getVisibility())
89                 .isEqualTo(View.VISIBLE);
90     }
91 
92     @Test
testSetUsageInfo_withNoDataPlans_carrierInfoNotShown()93     public void testSetUsageInfo_withNoDataPlans_carrierInfoNotShown() {
94         mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
95 
96         mSummaryPreference.onBindViewHolder(mHolder);
97         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getVisibility())
98                 .isEqualTo(View.GONE);
99     }
100 
101     @Test
testCarrierUpdateTime_shouldFormatDaysCorrectly()102     public void testCarrierUpdateTime_shouldFormatDaysCorrectly() {
103         int baseUnit = 2;
104         int smudge = 6;
105         final long updateTime = System.currentTimeMillis()
106                 - TimeUnit.DAYS.toMillis(baseUnit) - TimeUnit.HOURS.toMillis(smudge);
107         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, FAKE_CARRIER, 1 /* numPlans */);
108 
109         mSummaryPreference.onBindViewHolder(mHolder);
110         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getText().toString())
111                 .isEqualTo("Updated by " + FAKE_CARRIER + " " + baseUnit + " days ago");
112     }
113 
114     @Test
testCarrierUpdateTime_shouldFormatHoursCorrectly()115     public void testCarrierUpdateTime_shouldFormatHoursCorrectly() {
116         int baseUnit = 2;
117         int smudge = 6;
118         final long updateTime = System.currentTimeMillis()
119                 - TimeUnit.HOURS.toMillis(baseUnit) - TimeUnit.MINUTES.toMillis(smudge);
120         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, FAKE_CARRIER, 1 /* numPlans */);
121 
122         mSummaryPreference.onBindViewHolder(mHolder);
123         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getText().toString())
124                 .isEqualTo("Updated by " + FAKE_CARRIER + " " + baseUnit + " hr ago");
125     }
126 
127     @Test
testCarrierUpdateTime_shouldFormatMinutesCorrectly()128     public void testCarrierUpdateTime_shouldFormatMinutesCorrectly() {
129         int baseUnit = 2;
130         int smudge = 6;
131         final long updateTime = System.currentTimeMillis()
132                 - TimeUnit.MINUTES.toMillis(baseUnit) - TimeUnit.SECONDS.toMillis(smudge);
133         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, FAKE_CARRIER, 1 /* numPlans */);
134 
135         mSummaryPreference.onBindViewHolder(mHolder);
136         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getText().toString())
137                 .isEqualTo("Updated by " + FAKE_CARRIER + " " + baseUnit + " min ago");
138     }
139 
140     @Test
testCarrierUpdateTime_shouldFormatLessThanMinuteCorrectly()141     public void testCarrierUpdateTime_shouldFormatLessThanMinuteCorrectly() {
142         final long updateTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(45);
143         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, FAKE_CARRIER, 1 /* numPlans */);
144 
145         mSummaryPreference.onBindViewHolder(mHolder);
146         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getText().toString())
147                 .isEqualTo("Updated by " + FAKE_CARRIER + " just now");
148     }
149 
150     @Test
testCarrierUpdateTimeWithNoCarrier_shouldSayJustNow()151     public void testCarrierUpdateTimeWithNoCarrier_shouldSayJustNow() {
152         final long updateTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(45);
153         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, null /* carrier */,
154                 1 /* numPlans */);
155 
156         mSummaryPreference.onBindViewHolder(mHolder);
157         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getText().toString())
158                 .isEqualTo("Updated just now");
159     }
160 
161     @Test
testCarrierUpdateTimeWithNoCarrier_shouldFormatTime()162     public void testCarrierUpdateTimeWithNoCarrier_shouldFormatTime() {
163         final long updateTime = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2);
164         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, null /* carrier */,
165                 1 /* numPlans */);
166 
167         mSummaryPreference.onBindViewHolder(mHolder);
168         assertThat(mSummaryPreference.getCarrierInfo(mHolder).getText().toString())
169                 .isEqualTo("Updated 2 min ago");
170     }
171 
172     @Test
setUsageInfo_withRecentCarrierUpdate_doesNotSetCarrierInfoWarningColorAndFont()173     public void setUsageInfo_withRecentCarrierUpdate_doesNotSetCarrierInfoWarningColorAndFont() {
174         final long updateTime = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
175         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, FAKE_CARRIER, 1 /* numPlans */);
176 
177         mSummaryPreference.onBindViewHolder(mHolder);
178         TextView carrierInfo = mSummaryPreference.getCarrierInfo(mHolder);
179         assertThat(carrierInfo.getVisibility()).isEqualTo(View.VISIBLE);
180         assertThat(carrierInfo.getCurrentTextColor()).isEqualTo(
181                 Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary));
182         assertThat(carrierInfo.getTypeface()).isEqualTo(Typeface.SANS_SERIF);
183     }
184 
185     @Test
testSetUsageInfo_withStaleCarrierUpdate_setsCarrierInfoWarningColorAndFont()186     public void testSetUsageInfo_withStaleCarrierUpdate_setsCarrierInfoWarningColorAndFont() {
187         final long updateTime = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(7);
188         mSummaryPreference.setUsageInfo(mCycleEnd, updateTime, FAKE_CARRIER, 1 /* numPlans */);
189 
190         mSummaryPreference.onBindViewHolder(mHolder);
191         TextView carrierInfo = mSummaryPreference.getCarrierInfo(mHolder);
192         assertThat(carrierInfo.getVisibility()).isEqualTo(View.VISIBLE);
193         assertThat(carrierInfo.getCurrentTextColor()).isEqualTo(
194                 Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
195         assertThat(carrierInfo.getTypeface()).isEqualTo(
196                 DataUsageSummaryPreference.SANS_SERIF_MEDIUM);
197     }
198 
199     @Test
testSetUsageInfo_withNoDataPlans_usageTitleNotShown()200     public void testSetUsageInfo_withNoDataPlans_usageTitleNotShown() {
201         mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
202 
203         mSummaryPreference.onBindViewHolder(mHolder);
204         assertThat(mSummaryPreference.getUsageTitle(mHolder).getVisibility()).isEqualTo(View.GONE);
205     }
206 
207     @Test
testSetUsageInfo_withMultipleDataPlans_usageTitleShown()208     public void testSetUsageInfo_withMultipleDataPlans_usageTitleShown() {
209         mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 2 /* numPlans */);
210 
211         mSummaryPreference.onBindViewHolder(mHolder);
212         assertThat(mSummaryPreference.getUsageTitle(mHolder).getVisibility())
213                 .isEqualTo(View.VISIBLE);
214     }
215 
216     @Test
testSetUsageInfo_cycleRemainingTimeIsLessOneDay()217     public void testSetUsageInfo_cycleRemainingTimeIsLessOneDay() {
218         // just under one day
219         final long cycleEnd = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(23);
220         mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
221 
222         mSummaryPreference.onBindViewHolder(mHolder);
223         assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
224                 .isEqualTo(View.VISIBLE);
225         assertThat(mSummaryPreference.getCycleTime(mHolder).getText()).isEqualTo(
226                 ResourcesUtils.getResourcesString(
227                         mContext, "billing_cycle_less_than_one_day_left"));
228     }
229 
230     @Test
testSetUsageInfo_cycleRemainingTimeNegativeDaysLeft_shouldDisplayNoneLeft()231     public void testSetUsageInfo_cycleRemainingTimeNegativeDaysLeft_shouldDisplayNoneLeft() {
232         final long cycleEnd = System.currentTimeMillis() - 1L;
233         mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
234 
235         mSummaryPreference.onBindViewHolder(mHolder);
236         assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
237                 .isEqualTo(View.VISIBLE);
238         assertThat(mSummaryPreference.getCycleTime(mHolder).getText()).isEqualTo(
239                 ResourcesUtils.getResourcesString(mContext, "billing_cycle_none_left"));
240     }
241 
242     @Test
testSetUsageInfo_cycleRemainingTimeDaysLeft_shouldUsePlurals()243     public void testSetUsageInfo_cycleRemainingTimeDaysLeft_shouldUsePlurals() {
244         final int daysLeft = 3;
245         final long cycleEnd = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(daysLeft)
246                 + TimeUnit.HOURS.toMillis(1);
247         mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
248 
249         mSummaryPreference.onBindViewHolder(mHolder);
250         assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
251                 .isEqualTo(View.VISIBLE);
252         assertThat(mSummaryPreference.getCycleTime(mHolder).getText())
253                 .isEqualTo(daysLeft + " days left");
254     }
255 
256     @Test
testSetLimitInfo_withLimitInfo_dataLimitsShown()257     public void testSetLimitInfo_withLimitInfo_dataLimitsShown() {
258         final String limitText = "test limit text";
259         mSummaryPreference.setLimitInfo(limitText);
260 
261         mSummaryPreference.onBindViewHolder(mHolder);
262         assertThat(mSummaryPreference.getDataLimits(mHolder).getVisibility())
263                 .isEqualTo(View.VISIBLE);
264         assertThat(mSummaryPreference.getDataLimits(mHolder).getText()).isEqualTo(limitText);
265     }
266 
267     @Test
testSetLimitInfo_withNullLimitInfo_dataLimitsNotShown()268     public void testSetLimitInfo_withNullLimitInfo_dataLimitsNotShown() {
269         mSummaryPreference.setLimitInfo(null);
270 
271         mSummaryPreference.onBindViewHolder(mHolder);
272         assertThat(mSummaryPreference.getDataLimits(mHolder).getVisibility()).isEqualTo(View.GONE);
273     }
274 
275     @Test
testSetLimitInfo_withEmptyLimitInfo_dataLimitsNotShown()276     public void testSetLimitInfo_withEmptyLimitInfo_dataLimitsNotShown() {
277         final String emptyLimitText = "";
278         mSummaryPreference.setLimitInfo(emptyLimitText);
279 
280         mSummaryPreference.onBindViewHolder(mHolder);
281         assertThat(mSummaryPreference.getDataLimits(mHolder).getVisibility()).isEqualTo(View.GONE);
282     }
283 
284     @Test
testSetChartEnabledFalse_hidesLabelBar()285     public void testSetChartEnabledFalse_hidesLabelBar() {
286         setValidLabels();
287         mSummaryPreference.setChartEnabled(false);
288 
289         mSummaryPreference.onBindViewHolder(mHolder);
290         assertThat(mSummaryPreference.getLabelBar(mHolder).getVisibility()).isEqualTo(View.GONE);
291         assertThat(mSummaryPreference.getProgressBar(mHolder).getVisibility()).isEqualTo(View.GONE);
292     }
293 
294     @Test
testSetEmptyLabels_hidesLabelBar()295     public void testSetEmptyLabels_hidesLabelBar() {
296         mSummaryPreference.setLabels("", "");
297 
298         mSummaryPreference.onBindViewHolder(mHolder);
299         assertThat(mSummaryPreference.getLabelBar(mHolder).getVisibility()).isEqualTo(View.GONE);
300         assertThat(mSummaryPreference.getProgressBar(mHolder).getVisibility()).isEqualTo(View.GONE);
301     }
302 
303     @Test
testLabelBar_isVisible_whenLabelsSet()304     public void testLabelBar_isVisible_whenLabelsSet() {
305         setValidLabels();
306         //mChartEnabled defaults to true
307 
308         mSummaryPreference.onBindViewHolder(mHolder);
309         assertThat(mSummaryPreference.getLabelBar(mHolder).getVisibility())
310                 .isEqualTo(View.VISIBLE);
311         assertThat(mSummaryPreference.getProgressBar(mHolder).getVisibility())
312                 .isEqualTo(View.VISIBLE);
313     }
314 
315     @Test
testSetProgress_updatesProgressBar()316     public void testSetProgress_updatesProgressBar() {
317         setValidLabels();
318         mSummaryPreference.setProgress(.5f);
319 
320         mSummaryPreference.onBindViewHolder(mHolder);
321         assertThat(mSummaryPreference.getProgressBar(mHolder).getProgress()).isEqualTo(50);
322     }
323 
setValidLabels()324     private void setValidLabels() {
325         mSummaryPreference.setLabels("0.0 GB", "5.0 GB");
326     }
327 
328     @Test
testSetUsageAndRemainingInfo_withUsageInfo_dataUsageAndRemainingShown()329     public void testSetUsageAndRemainingInfo_withUsageInfo_dataUsageAndRemainingShown() {
330         mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
331         mSummaryPreference.setUsageNumbers(
332                 BillingCycleSettings.MIB_IN_BYTES,
333                 10 * BillingCycleSettings.MIB_IN_BYTES);
334 
335         mSummaryPreference.onBindViewHolder(mHolder);
336         assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
337                 .isEqualTo("1.00 MB used");
338         assertThat(mSummaryPreference.getDataRemaining(mHolder).getText().toString())
339                 .isEqualTo("9.00 MB left");
340         assertThat(mSummaryPreference.getDataRemaining(mHolder).getVisibility())
341                 .isEqualTo(View.VISIBLE);
342         final int colorId = Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorAccent);
343         assertThat(mSummaryPreference.getDataRemaining(mHolder).getCurrentTextColor())
344                 .isEqualTo(colorId);
345     }
346 
347     @Test
testSetUsageInfo_withDataOverusage()348     public void testSetUsageInfo_withDataOverusage() {
349         mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
350         mSummaryPreference.setUsageNumbers(
351                 11 * BillingCycleSettings.MIB_IN_BYTES,
352                 10 * BillingCycleSettings.MIB_IN_BYTES);
353 
354         mSummaryPreference.onBindViewHolder(mHolder);
355         assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
356                 .isEqualTo("11.00 MB used");
357         assertThat(mSummaryPreference.getDataRemaining(mHolder).getText().toString())
358                 .isEqualTo("1.00 MB over");
359         final int colorId = Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError);
360         assertThat(mSummaryPreference.getDataRemaining(mHolder).getCurrentTextColor())
361                 .isEqualTo(colorId);
362     }
363 
364     @Test
testSetUsageInfo_withUsageInfo_dataUsageShown()365     public void testSetUsageInfo_withUsageInfo_dataUsageShown() {
366         mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
367         mSummaryPreference.setUsageNumbers(
368                 BillingCycleSettings.MIB_IN_BYTES, -1L);
369 
370         mSummaryPreference.onBindViewHolder(mHolder);
371         assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
372                 .isEqualTo("1.00 MB used");
373         assertThat(mSummaryPreference.getDataRemaining(mHolder).getText()).isEqualTo("");
374     }
375 
376     @Test
testSetUsageInfo_withOverflowStrings_dataRemainingNotShown()377     public void testSetUsageInfo_withOverflowStrings_dataRemainingNotShown() {
378         LayoutInflater inflater = LayoutInflater.from(mContext);
379         View view = inflater.inflate(mSummaryPreference.getLayoutResource(), null /* root */,
380                 false /* attachToRoot */);
381 
382         mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
383         mSummaryPreference.setUsageNumbers(
384                 BillingCycleSettings.MIB_IN_BYTES,
385                 10 * BillingCycleSettings.MIB_IN_BYTES);
386 
387         int data_used_formatted_id = ResourcesUtils.getResourcesId(
388                 mContext, "string", "data_used_formatted");
389         int data_remaining_id = ResourcesUtils.getResourcesId(
390                 mContext, "string", "data_remaining");
391         CharSequence data_used_formatted_cs = "^1 ^2 used with long trailing text";
392         CharSequence data_remaining_cs = "^1 left";
393         doReturn(data_used_formatted_cs).when(mResources).getText(data_used_formatted_id);
394         doReturn(data_remaining_cs).when(mResources).getText(data_remaining_id);
395 
396         mSummaryPreference.onBindViewHolder(mHolder);
397 
398         TextView dataUsed = mSummaryPreference.getDataUsed(mHolder);
399         TextView dataRemaining = mSummaryPreference.getDataRemaining(mHolder);
400         int width = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
401         dataUsed.measure(width, MeasureSpec.UNSPECIFIED);
402         dataRemaining.measure(width, MeasureSpec.UNSPECIFIED);
403 
404         assertThat(dataRemaining.getVisibility()).isEqualTo(View.VISIBLE);
405 
406         MeasurableLinearLayout layout = mSummaryPreference.getLayout(mHolder);
407         layout.measure(
408                 MeasureSpec.makeMeasureSpec(800, View.MeasureSpec.EXACTLY),
409                 MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
410 
411         assertThat(dataUsed.getText().toString()).isEqualTo("1.00 MB used with long trailing text");
412         // TODO(b/175389659): re-enable this line once cuttlefish device specs are verified.
413         // assertThat(dataRemaining.getVisibility()).isEqualTo(View.GONE);
414     }
415 }
416