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 static com.android.settings.fuelgauge.PowerUsageSummary.BATTERY_INFO_LOADER;
19 import static com.android.settings.fuelgauge.PowerUsageSummary.KEY_BATTERY_ERROR;
20 import static com.android.settings.fuelgauge.PowerUsageSummary.KEY_BATTERY_USAGE;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.doNothing;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
34 
35 import android.content.ContentResolver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.os.Bundle;
39 import android.provider.Settings;
40 
41 import androidx.loader.app.LoaderManager;
42 import androidx.preference.Preference;
43 import androidx.preference.PreferenceScreen;
44 
45 import com.android.settings.R;
46 import com.android.settings.SettingsActivity;
47 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
48 import com.android.settings.testutils.FakeFeatureFactory;
49 import com.android.settings.testutils.XmlTestUtils;
50 import com.android.settings.testutils.shadow.ShadowUtils;
51 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
52 
53 import org.junit.Before;
54 import org.junit.BeforeClass;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.Mock;
58 import org.mockito.MockitoAnnotations;
59 import org.robolectric.RobolectricTestRunner;
60 import org.robolectric.RuntimeEnvironment;
61 import org.robolectric.annotation.Config;
62 import org.robolectric.util.ReflectionHelpers;
63 
64 import java.util.List;
65 
66 // TODO: Improve this test class so that it starts up the real activity and fragment.
67 @RunWith(RobolectricTestRunner.class)
68 public class PowerUsageSummaryTest {
69 
70     private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
71     private static Intent sAdditionalBatteryInfoIntent;
72 
73     @BeforeClass
beforeClass()74     public static void beforeClass() {
75         sAdditionalBatteryInfoIntent = new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
76     }
77     @Mock
78     private SettingsActivity mSettingsActivity;
79     @Mock
80     private LoaderManager mLoaderManager;
81     @Mock
82     private ContentResolver mContentResolver;
83     @Mock
84     private BatteryBroadcastReceiver mBatteryBroadcastReceiver;
85     @Mock
86     private VisibilityLoggerMixin mVisibilityLoggerMixin;
87     @Mock
88     private PreferenceScreen mPreferenceScreen;
89     @Mock
90     private Preference mBatteryUsagePreference;
91     @Mock
92     private Preference mHelpPreference;
93 
94     private Context mRealContext;
95     private TestFragment mFragment;
96     private FakeFeatureFactory mFeatureFactory;
97 
98     @Before
setUp()99     public void setUp() {
100         MockitoAnnotations.initMocks(this);
101 
102         mRealContext = spy(RuntimeEnvironment.application);
103         mFeatureFactory = FakeFeatureFactory.setupForTest();
104         mFragment = spy(new TestFragment(mRealContext));
105         mFragment.initFeatureProvider();
106         doNothing().when(mFragment).restartBatteryStatsLoader(anyInt());
107         doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager();
108 
109         when(mFragment.getActivity()).thenReturn(mSettingsActivity);
110         when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
111                 .thenReturn(sAdditionalBatteryInfoIntent);
112         when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mRealContext))
113                 .thenReturn(true);
114         mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext));
115         ReflectionHelpers.setField(mFragment, "mVisibilityLoggerMixin", mVisibilityLoggerMixin);
116         ReflectionHelpers.setField(mFragment, "mBatteryBroadcastReceiver",
117                 mBatteryBroadcastReceiver);
118         doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
119         when(mFragment.getContentResolver()).thenReturn(mContentResolver);
120         when(mFragment.findPreference(KEY_BATTERY_USAGE)).thenReturn(mBatteryUsagePreference);
121         when(mFragment.findPreference(KEY_BATTERY_ERROR)).thenReturn(mHelpPreference);
122     }
123 
124     @Test
125     @Config(shadows = ShadowUtils.class)
nonIndexableKeys_MatchPreferenceKeys()126     public void nonIndexableKeys_MatchPreferenceKeys() {
127         final Context context = RuntimeEnvironment.application;
128         final List<String> niks =
129                 PowerUsageSummary.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(context);
130 
131         final List<String> keys =
132                 XmlTestUtils.getKeysFromPreferenceXml(context, R.xml.power_usage_summary);
133 
134         assertThat(keys).containsAtLeastElementsIn(niks);
135     }
136 
137     @Test
initPreference_chartGraphEnabled_hasCorrectSummary()138     public void initPreference_chartGraphEnabled_hasCorrectSummary() {
139         mFragment.initPreference();
140 
141         verify(mBatteryUsagePreference).setSummary("View usage for past 24 hours");
142     }
143 
144     @Test
initPreference_chartGraphDisabled_hasCorrectSummary()145     public void initPreference_chartGraphDisabled_hasCorrectSummary() {
146         when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mRealContext))
147                 .thenReturn(false);
148 
149         mFragment.initPreference();
150 
151         verify(mBatteryUsagePreference).setSummary("View usage from last full charge");
152     }
153 
154     @Test
initPreference_helpPreferenceInvisible()155     public void initPreference_helpPreferenceInvisible() {
156         mFragment.initPreference();
157 
158         verify(mHelpPreference).setVisible(false);
159     }
160 
161     @Test
restartBatteryTipLoader()162     public void restartBatteryTipLoader() {
163         //TODO: add policy logic here when BatteryTipPolicy is implemented
164         doReturn(mLoaderManager).when(mFragment).getLoaderManager();
165 
166         mFragment.restartBatteryTipLoader();
167 
168         verify(mLoaderManager)
169                 .restartLoader(eq(PowerUsageSummary.BATTERY_TIP_LOADER), eq(Bundle.EMPTY), any());
170     }
171 
172     @Test
refreshUi_deviceRotate_doNotUpdateBatteryTip()173     public void refreshUi_deviceRotate_doNotUpdateBatteryTip() {
174         mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class);
175         when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(false);
176         mFragment.updateBatteryTipFlag(new Bundle());
177 
178         mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
179 
180         verify(mFragment, never()).restartBatteryTipLoader();
181     }
182 
183     @Test
refreshUi_batteryLevelChanged_doNotUpdateBatteryTip()184     public void refreshUi_batteryLevelChanged_doNotUpdateBatteryTip() {
185         mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class);
186         when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(true);
187         mFragment.updateBatteryTipFlag(new Bundle());
188 
189         mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_LEVEL);
190 
191         verify(mFragment, never()).restartBatteryTipLoader();
192     }
193 
194     @Test
refreshUi_tipNeedUpdate_updateBatteryTip()195     public void refreshUi_tipNeedUpdate_updateBatteryTip() {
196         mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class);
197         when(mFragment.mBatteryTipPreferenceController.needUpdate()).thenReturn(true);
198         mFragment.updateBatteryTipFlag(new Bundle());
199 
200         mFragment.refreshUi(BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
201 
202         verify(mFragment).restartBatteryTipLoader();
203     }
204 
205     @Test
onResume_registerContentObserver()206     public void onResume_registerContentObserver() {
207         mFragment.onResume();
208 
209         verify(mContentResolver).registerContentObserver(
210                 Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
211                 false,
212                 mFragment.mSettingsObserver);
213     }
214 
215     @Test
onPause_unregisterContentObserver()216     public void onPause_unregisterContentObserver() {
217         mFragment.onPause();
218 
219         verify(mContentResolver).unregisterContentObserver(
220                 mFragment.mSettingsObserver);
221     }
222 
223     @Test
restartBatteryInfoLoader_contextNull_doNothing()224     public void restartBatteryInfoLoader_contextNull_doNothing() {
225         when(mFragment.getContext()).thenReturn(null);
226         when(mFragment.getLoaderManager()).thenReturn(mLoaderManager);
227 
228         mFragment.restartBatteryInfoLoader();
229 
230         verify(mLoaderManager, never()).restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
231                 mFragment.mBatteryInfoLoaderCallbacks);
232     }
233 
234     public static class TestFragment extends PowerUsageSummary {
235         private Context mContext;
236 
TestFragment(Context context)237         public TestFragment(Context context) {
238             mContext = context;
239         }
240 
241         @Override
getContext()242         public Context getContext() {
243             return mContext;
244         }
245 
246         @Override
getContentResolver()247         protected ContentResolver getContentResolver() {
248             // Override it so we can access this method in test
249             return super.getContentResolver();
250         }
251     }
252 }
253