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