• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.settings.fuelgauge;
17 
18 import android.app.Activity;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.PowerManager;
23 import android.support.v7.preference.PreferenceScreen;
24 import android.text.TextUtils;
25 import android.text.format.DateUtils;
26 import android.view.Menu;
27 import android.view.MenuInflater;
28 import android.view.MenuItem;
29 import android.widget.TextView;
30 
31 import com.android.internal.logging.nano.MetricsProto;
32 import com.android.internal.os.BatterySipper;
33 import com.android.internal.os.BatteryStatsHelper;
34 import com.android.internal.os.BatteryStatsImpl;
35 import com.android.settings.R;
36 import com.android.settings.SettingsActivity;
37 import com.android.settings.SettingsRobolectricTestRunner;
38 import com.android.settings.TestConfig;
39 import com.android.settings.Utils;
40 import com.android.settings.applications.LayoutPreference;
41 import com.android.settings.core.PreferenceController;
42 import com.android.settings.testutils.FakeFeatureFactory;
43 import com.android.settings.testutils.shadow.SettingsShadowResources;
44 import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
45 import com.android.settings.testutils.XmlTestUtils;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.Answers;
51 import org.mockito.Mock;
52 import org.mockito.MockitoAnnotations;
53 import org.robolectric.RuntimeEnvironment;
54 import org.robolectric.annotation.Config;
55 
56 import java.util.ArrayList;
57 import java.util.List;
58 
59 import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADDITIONAL_BATTERY_INFO;
60 import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_HIGH_POWER_APPS;
61 import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_TOGGLE_APPS;
62 
63 import static com.google.common.truth.Truth.assertThat;
64 
65 import static org.mockito.Matchers.anyInt;
66 import static org.mockito.Matchers.any;
67 import static org.mockito.Matchers.anyLong;
68 import static org.mockito.Matchers.eq;
69 import static org.mockito.Mockito.doNothing;
70 import static org.mockito.Mockito.doReturn;
71 import static org.mockito.Mockito.never;
72 import static org.mockito.Mockito.spy;
73 import static org.mockito.Mockito.verify;
74 import static org.mockito.Mockito.when;
75 
76 /**
77  * Unit tests for {@link PowerUsageSummary}.
78  */
79 // TODO: Improve this test class so that it starts up the real activity and fragment.
80 @RunWith(SettingsRobolectricTestRunner.class)
81 @Config(manifest = TestConfig.MANIFEST_PATH,
82         sdk = TestConfig.SDK_VERSION,
83         shadows = {
84                 SettingsShadowResources.class,
85                 SettingsShadowResources.SettingsShadowTheme.class,
86                 ShadowDynamicIndexableContentMonitor.class
87         })
88 public class PowerUsageSummaryTest {
89     private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
90     private static final String STUB_STRING = "stub_string";
91     private static final int UID = 123;
92     private static final int POWER_MAH = 100;
93     private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
94     private static final long TIME_SINCE_LAST_FULL_CHARGE_US =
95             TIME_SINCE_LAST_FULL_CHARGE_MS * 1000;
96     private static final int DISCHARGE_AMOUNT = 100;
97     private static final long USAGE_TIME_MS = 65 * 60 * 1000;
98     private static final double TOTAL_POWER = 200;
99     private static final double BATTERY_SCREEN_USAGE = 300;
100     private static final double BATTERY_SYSTEM_USAGE = 600;
101     private static final double BATTERY_OVERCOUNTED_USAGE = 500;
102     private static final double PRECISION = 0.001;
103     private static final double POWER_USAGE_PERCENTAGE = 50;
104     private static final Intent ADDITIONAL_BATTERY_INFO_INTENT =
105             new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
106 
107     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
108     private Context mContext;
109     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
110     private Menu mMenu;
111     @Mock
112     private MenuItem mAdditionalBatteryInfoMenu;
113     @Mock
114     private MenuItem mToggleAppsMenu;
115     @Mock
116     private MenuItem mHighPowerMenu;
117     @Mock
118     private MenuInflater mMenuInflater;
119     @Mock
120     private BatterySipper mNormalBatterySipper;
121     @Mock
122     private BatterySipper mScreenBatterySipper;
123     @Mock
124     private BatterySipper mCellBatterySipper;
125     @Mock
126     private LayoutPreference mBatteryLayoutPref;
127     @Mock
128     private TextView mBatteryPercentText;
129     @Mock
130     private TextView mSummary1;
131     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
132     private BatteryStatsHelper mBatteryHelper;
133     @Mock
134     private PowerManager mPowerManager;
135     @Mock
136     private SettingsActivity mSettingsActivity;
137     @Mock
138     private PreferenceScreen mPreferenceScreen;
139 
140     private List<BatterySipper> mUsageList;
141     private Context mRealContext;
142     private TestFragment mFragment;
143     private FakeFeatureFactory mFeatureFactory;
144     private BatteryMeterView mBatteryMeterView;
145     private PowerGaugePreference mPreference;
146     private PowerGaugePreference mScreenUsagePref;
147     private PowerGaugePreference mLastFullChargePref;
148 
149     @Before
setUp()150     public void setUp() {
151         MockitoAnnotations.initMocks(this);
152 
153         mRealContext = RuntimeEnvironment.application;
154         FakeFeatureFactory.setupForTest(mContext);
155         mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
156         when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
157 
158         mPreference = new PowerGaugePreference(mRealContext);
159         mScreenUsagePref = new PowerGaugePreference(mRealContext);
160         mLastFullChargePref = new PowerGaugePreference(mRealContext);
161         mFragment = spy(new TestFragment(mContext));
162         mFragment.initFeatureProvider();
163         mBatteryMeterView = new BatteryMeterView(mRealContext);
164         mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0);
165         doNothing().when(mFragment).restartBatteryStatsLoader();
166 
167         when(mFragment.getActivity()).thenReturn(mSettingsActivity);
168         when(mAdditionalBatteryInfoMenu.getItemId())
169                 .thenReturn(MENU_ADDITIONAL_BATTERY_INFO);
170         when(mToggleAppsMenu.getItemId()).thenReturn(MENU_TOGGLE_APPS);
171         when(mHighPowerMenu.getItemId()).thenReturn(MENU_HIGH_POWER_APPS);
172         when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
173                 .thenReturn(ADDITIONAL_BATTERY_INFO_INTENT);
174         when(mBatteryHelper.getTotalPower()).thenReturn(TOTAL_POWER);
175         when(mBatteryHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())).thenReturn(
176                 TIME_SINCE_LAST_FULL_CHARGE_US);
177 
178         when(mNormalBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
179         when(mNormalBatterySipper.getUid()).thenReturn(UID);
180         mNormalBatterySipper.totalPowerMah = POWER_MAH;
181         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
182 
183         mCellBatterySipper.drainType = BatterySipper.DrainType.CELL;
184         mCellBatterySipper.totalPowerMah = POWER_MAH;
185 
186         when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1);
187         when(mBatteryLayoutPref.findViewById(R.id.battery_percent)).thenReturn(mBatteryPercentText);
188         when(mBatteryLayoutPref.findViewById(R.id.battery_header_icon))
189                 .thenReturn(mBatteryMeterView);
190         mFragment.setBatteryLayoutPreference(mBatteryLayoutPref);
191 
192         mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
193         mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS;
194 
195         mUsageList = new ArrayList<>();
196         mUsageList.add(mNormalBatterySipper);
197         mUsageList.add(mScreenBatterySipper);
198         mUsageList.add(mCellBatterySipper);
199 
200         mFragment.mStatsHelper = mBatteryHelper;
201         when(mBatteryHelper.getUsageList()).thenReturn(mUsageList);
202         mFragment.mScreenUsagePref = mScreenUsagePref;
203         mFragment.mLastFullChargePref = mLastFullChargePref;
204         mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext));
205     }
206 
207     @Test
testOptionsMenu_additionalBatteryInfoEnabled()208     public void testOptionsMenu_additionalBatteryInfoEnabled() {
209         when(mFeatureFactory.powerUsageFeatureProvider.isAdditionalBatteryInfoEnabled())
210                 .thenReturn(true);
211 
212         mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
213 
214         verify(mMenu).add(Menu.NONE, MENU_ADDITIONAL_BATTERY_INFO,
215                 Menu.NONE, R.string.additional_battery_info);
216 
217         mFragment.onOptionsItemSelected(mAdditionalBatteryInfoMenu);
218 
219         assertThat(mFragment.mStartActivityCalled).isTrue();
220         assertThat(mFragment.mStartActivityIntent).isEqualTo(ADDITIONAL_BATTERY_INFO_INTENT);
221     }
222 
223     @Test
testOptionsMenu_additionalBatteryInfoDisabled()224     public void testOptionsMenu_additionalBatteryInfoDisabled() {
225         when(mFeatureFactory.powerUsageFeatureProvider.isAdditionalBatteryInfoEnabled())
226                 .thenReturn(false);
227 
228         mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
229 
230         verify(mMenu, never()).add(Menu.NONE, MENU_ADDITIONAL_BATTERY_INFO,
231                 Menu.NONE, R.string.additional_battery_info);
232     }
233 
234     @Test
testOptionsMenu_menuHighPower_metricEventInvoked()235     public void testOptionsMenu_menuHighPower_metricEventInvoked() {
236         mFragment.onOptionsItemSelected(mHighPowerMenu);
237 
238         verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
239                 MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION);
240     }
241 
242     @Test
testOptionsMenu_menuAdditionalBattery_metricEventInvoked()243     public void testOptionsMenu_menuAdditionalBattery_metricEventInvoked() {
244         mFragment.onOptionsItemSelected(mAdditionalBatteryInfoMenu);
245 
246         verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
247                 MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_USAGE_ALERTS);
248     }
249 
250     @Test
testOptionsMenu_menuAppToggle_metricEventInvoked()251     public void testOptionsMenu_menuAppToggle_metricEventInvoked() {
252         mFragment.onOptionsItemSelected(mToggleAppsMenu);
253         mFragment.mShowAllApps = false;
254 
255         verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
256                 MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE, true);
257     }
258 
259     @Test
testOptionsMenu_toggleAppsEnabled()260     public void testOptionsMenu_toggleAppsEnabled() {
261         when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
262                 .thenReturn(true);
263         mFragment.mShowAllApps = false;
264 
265         mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
266 
267         verify(mMenu).add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE, R.string.show_all_apps);
268     }
269 
270     @Test
testOptionsMenu_clickToggleAppsMenu_dataChanged()271     public void testOptionsMenu_clickToggleAppsMenu_dataChanged() {
272         testToggleAllApps(true);
273         testToggleAllApps(false);
274     }
275 
276     @Test
testExtractKeyFromSipper_typeAPPUidObjectNull_returnPackageNames()277     public void testExtractKeyFromSipper_typeAPPUidObjectNull_returnPackageNames() {
278         mNormalBatterySipper.uidObj = null;
279         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
280 
281         final String key = mFragment.extractKeyFromSipper(mNormalBatterySipper);
282         assertThat(key).isEqualTo(TextUtils.concat(mNormalBatterySipper.getPackages()).toString());
283     }
284 
285     @Test
testExtractKeyFromSipper_typeOther_returnDrainType()286     public void testExtractKeyFromSipper_typeOther_returnDrainType() {
287         mNormalBatterySipper.uidObj = null;
288         mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
289 
290         final String key = mFragment.extractKeyFromSipper(mNormalBatterySipper);
291         assertThat(key).isEqualTo(mNormalBatterySipper.drainType.toString());
292     }
293 
294     @Test
testExtractKeyFromSipper_typeAPPUidObjectNotNull_returnUid()295     public void testExtractKeyFromSipper_typeAPPUidObjectNotNull_returnUid() {
296         mNormalBatterySipper.uidObj = new BatteryStatsImpl.Uid(new BatteryStatsImpl(), UID);
297         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
298 
299         final String key = mFragment.extractKeyFromSipper(mNormalBatterySipper);
300         assertThat(key).isEqualTo(Integer.toString(mNormalBatterySipper.getUid()));
301     }
302 
303     @Test
testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary()304     public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
305         mNormalBatterySipper.usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
306 
307         mFragment.setUsageSummary(mPreference, mNormalBatterySipper);
308         assertThat(mPreference.getSummary()).isNull();
309     }
310 
311     @Test
testSetUsageSummary_timeMoreThanOneMinute_normalApp_setScreenSummary()312     public void testSetUsageSummary_timeMoreThanOneMinute_normalApp_setScreenSummary() {
313         mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
314         doReturn(mRealContext.getText(R.string.battery_screen_usage)).when(mFragment).getText(
315                 R.string.battery_screen_usage);
316         doReturn(mRealContext).when(mFragment).getContext();
317         final String expectedSummary = "Screen usage 2m";
318 
319         mFragment.setUsageSummary(mPreference, mNormalBatterySipper);
320 
321         assertThat(mPreference.getSummary().toString()).isEqualTo(expectedSummary);
322     }
323 
324     @Test
testSetUsageSummary_timeMoreThanOneMinute_hiddenApp_setUsedSummary()325     public void testSetUsageSummary_timeMoreThanOneMinute_hiddenApp_setUsedSummary() {
326         mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
327         doReturn(true).when(mFragment.mBatteryUtils).shouldHideSipper(mNormalBatterySipper);
328         doReturn(mRealContext).when(mFragment).getContext();
329         final String expectedSummary = "2m";
330 
331         mFragment.setUsageSummary(mPreference, mNormalBatterySipper);
332 
333         assertThat(mPreference.getSummary().toString()).isEqualTo(expectedSummary);
334     }
335 
testToggleAllApps(final boolean isShowApps)336     private void testToggleAllApps(final boolean isShowApps) {
337         mFragment.mShowAllApps = isShowApps;
338 
339         mFragment.onOptionsItemSelected(mToggleAppsMenu);
340         assertThat(mFragment.mShowAllApps).isEqualTo(!isShowApps);
341     }
342 
343     @Test
testFindBatterySipperByType_findTypeScreen()344     public void testFindBatterySipperByType_findTypeScreen() {
345         BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList,
346                 BatterySipper.DrainType.SCREEN);
347 
348         assertThat(sipper).isSameAs(mScreenBatterySipper);
349     }
350 
351     @Test
testFindBatterySipperByType_findTypeApp()352     public void testFindBatterySipperByType_findTypeApp() {
353         BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList,
354                 BatterySipper.DrainType.APP);
355 
356         assertThat(sipper).isSameAs(mNormalBatterySipper);
357     }
358 
359     @Test
testUpdateScreenPreference_showCorrectSummary()360     public void testUpdateScreenPreference_showCorrectSummary() {
361         doReturn(mScreenBatterySipper).when(mFragment).findBatterySipperByType(any(), any());
362         doReturn(mRealContext).when(mFragment).getContext();
363         final CharSequence expectedSummary = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS,
364                 false);
365 
366         mFragment.updateScreenPreference();
367 
368         assertThat(mScreenUsagePref.getSubtitle()).isEqualTo(expectedSummary);
369     }
370 
371     @Test
testUpdateLastFullChargePreference_showCorrectSummary()372     public void testUpdateLastFullChargePreference_showCorrectSummary() {
373         final CharSequence formattedString = mRealContext.getText(
374                 R.string.power_last_full_charge_summary);
375         final CharSequence timeSequence = Utils.formatElapsedTime(mRealContext,
376                 TIME_SINCE_LAST_FULL_CHARGE_MS, false);
377         final CharSequence expectedSummary = TextUtils.expandTemplate(
378                 formattedString, timeSequence);
379         doReturn(formattedString).when(mFragment).getText(R.string.power_last_full_charge_summary);
380         doReturn(mRealContext).when(mFragment).getContext();
381 
382         mFragment.updateLastFullChargePreference(TIME_SINCE_LAST_FULL_CHARGE_MS);
383 
384         assertThat(mLastFullChargePref.getSubtitle()).isEqualTo(expectedSummary);
385     }
386 
387     @Test
testUpdatePreference_usageListEmpty_shouldNotCrash()388     public void testUpdatePreference_usageListEmpty_shouldNotCrash() {
389         when(mBatteryHelper.getUsageList()).thenReturn(new ArrayList<BatterySipper>());
390         doReturn(STUB_STRING).when(mFragment).getString(anyInt(), any());
391         doReturn(mRealContext).when(mFragment).getContext();
392 
393         // Should not crash when update
394         mFragment.updateScreenPreference();
395     }
396 
397     @Test
testCalculatePercentage()398     public void testCalculatePercentage() {
399         final double percent = mFragment.calculatePercentage(POWER_MAH, DISCHARGE_AMOUNT);
400         assertThat(percent).isWithin(PRECISION).of(POWER_USAGE_PERCENTAGE);
401     }
402 
403     @Test
testCalculateRunningTimeBasedOnStatsType()404     public void testCalculateRunningTimeBasedOnStatsType() {
405         assertThat(mFragment.calculateRunningTimeBasedOnStatsType()).isEqualTo(
406                 TIME_SINCE_LAST_FULL_CHARGE_MS);
407     }
408 
409     @Test
testNonIndexableKeys_MatchPreferenceKeys()410     public void testNonIndexableKeys_MatchPreferenceKeys() {
411         final Context context = RuntimeEnvironment.application;
412         final List<String> niks = PowerUsageSummary.SEARCH_INDEX_DATA_PROVIDER
413                 .getNonIndexableKeys(context);
414 
415         final List<String> keys = XmlTestUtils.getKeysFromPreferenceXml(context,
416                 R.xml.power_usage_summary);
417 
418         assertThat(keys).containsAllIn(niks);
419     }
420 
421     @Test
testPreferenceControllers_getPreferenceKeys_existInPreferenceScreen()422     public void testPreferenceControllers_getPreferenceKeys_existInPreferenceScreen() {
423         final Context context = RuntimeEnvironment.application;
424         final PowerUsageSummary fragment = new PowerUsageSummary();
425         final List<String> preferenceScreenKeys = XmlTestUtils.getKeysFromPreferenceXml(context,
426                 fragment.getPreferenceScreenResId());
427         final List<String> preferenceKeys = new ArrayList<>();
428 
429         for (PreferenceController controller : fragment.getPreferenceControllers(context)) {
430             preferenceKeys.add(controller.getPreferenceKey());
431         }
432 
433         assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys);
434     }
435 
436     @Test
testSaveInstanceState_showAllAppsRestored()437     public void testSaveInstanceState_showAllAppsRestored() {
438         Bundle bundle = new Bundle();
439         mFragment.mShowAllApps = true;
440         doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
441 
442         mFragment.onSaveInstanceState(bundle);
443         mFragment.restoreSavedInstance(bundle);
444 
445         assertThat(mFragment.mShowAllApps).isTrue();
446     }
447 
448     public static class TestFragment extends PowerUsageSummary {
449 
450         private Context mContext;
451         private boolean mStartActivityCalled;
452         private Intent mStartActivityIntent;
453 
TestFragment(Context context)454         public TestFragment(Context context) {
455             mContext = context;
456         }
457 
458         @Override
getContext()459         public Context getContext() {
460             return mContext;
461         }
462 
463         @Override
startActivity(Intent intent)464         public void startActivity(Intent intent) {
465             mStartActivityCalled = true;
466             mStartActivityIntent = intent;
467         }
468 
469         @Override
refreshUi()470         protected void refreshUi() {
471             // Leave it empty for toggle apps menu test
472         }
473     }
474 }
475