1 /*
2  * Copyright (C) 2018 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.homepage.contextualcards.slices;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.when;
23 
24 import android.content.Context;
25 import android.net.Uri;
26 
27 import androidx.slice.Slice;
28 import androidx.slice.SliceMetadata;
29 import androidx.slice.SliceProvider;
30 import androidx.slice.widget.SliceLiveData;
31 
32 import com.android.internal.os.BatteryStatsHelper;
33 import com.android.settings.R;
34 import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
35 import com.android.settings.fuelgauge.batterytip.AppInfo;
36 import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
37 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
38 import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
39 import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
40 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
41 import com.android.settings.slices.SliceBackgroundWorker;
42 
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.MockitoAnnotations;
48 import org.robolectric.RobolectricTestRunner;
49 import org.robolectric.RuntimeEnvironment;
50 import org.robolectric.annotation.Config;
51 import org.robolectric.annotation.Implementation;
52 import org.robolectric.annotation.Implements;
53 import org.robolectric.annotation.Resetter;
54 
55 import java.util.ArrayList;
56 import java.util.List;
57 
58 @RunWith(RobolectricTestRunner.class)
59 @Config(shadows = {
60         BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
61         BatteryFixSliceTest.ShadowBatteryTipLoader.class
62 })
63 public class BatteryFixSliceTest {
64 
65     private Context mContext;
66     private BatteryFixSlice mSlice;
67 
68     @Before
setUp()69     public void setUp() {
70         MockitoAnnotations.initMocks(this);
71         mContext = RuntimeEnvironment.application;
72         mSlice = new BatteryFixSlice(mContext);
73 
74         // Set-up specs for SliceMetadata.
75         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
76     }
77 
78     @After
tearDown()79     public void tearDown() {
80         ShadowBatteryTipLoader.reset();
81         ShadowSliceBackgroundWorker.reset();
82         ShadowEarlyWarningTip.reset();
83     }
84 
85     @Test
refreshBatteryTips_hasImportantTip_shouldReturnTrue()86     public void refreshBatteryTips_hasImportantTip_shouldReturnTrue() {
87         final List<BatteryTip> tips = new ArrayList<>();
88         tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, ""));
89         tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false));
90         ShadowBatteryTipLoader.setBatteryTips(tips);
91 
92         BatteryFixSlice.refreshBatteryTips(mContext);
93 
94         assertThat(BatteryFixSlice.isBatteryTipAvailableFromCache(mContext)).isTrue();
95     }
96 
97     @Test
getSlice_unimportantSlice_shouldSkip()98     public void getSlice_unimportantSlice_shouldSkip() {
99         final List<BatteryTip> tips = new ArrayList<>();
100         final List<AppInfo> appList = new ArrayList<>();
101         appList.add(new AppInfo.Builder()
102                 .setPackageName("com.android.settings")
103                 .setScreenOnTimeMs(10000L)
104                 .build());
105         tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, ""));
106         tips.add(new EarlyWarningTip(BatteryTip.StateType.HANDLED, false));
107         tips.add(new HighUsageTip(1000L, appList));
108         ShadowBatteryTipLoader.setBatteryTips(tips);
109 
110         BatteryFixSlice.refreshBatteryTips(mContext);
111         final Slice slice = mSlice.getSlice();
112 
113         assertThat(SliceMetadata.from(mContext, slice).isErrorSlice()).isTrue();
114     }
115 
116     @Test
117     @Config(shadows = {
118             BatteryFixSliceTest.ShadowEarlyWarningTip.class,
119             BatteryFixSliceTest.ShadowSliceBackgroundWorker.class
120     })
getSlice_hasImportantTip_shouldTintIcon()121     public void getSlice_hasImportantTip_shouldTintIcon() {
122         final List<BatteryTip> tips = new ArrayList<>();
123         tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false));
124         // Create fake cache data
125         ShadowBatteryTipLoader.setBatteryTips(tips);
126         BatteryFixSlice.refreshBatteryTips(mContext);
127         // Create fake background worker data
128         BatteryFixSlice.BatteryTipWorker batteryTipWorker = mock(
129                 BatteryFixSlice.BatteryTipWorker.class);
130         when(batteryTipWorker.getResults()).thenReturn(tips);
131         ShadowSliceBackgroundWorker.setBatteryTipWorkerWorker(batteryTipWorker);
132 
133         final Slice slice = mSlice.getSlice();
134 
135         assertThat(ShadowEarlyWarningTip.isIconTintColorIdCalled()).isTrue();
136     }
137 
138     @Implements(BatteryStatsHelperLoader.class)
139     public static class ShadowBatteryStatsHelperLoader {
140 
141         @Implementation
loadInBackground()142         protected BatteryStatsHelper loadInBackground() {
143             return null;
144         }
145     }
146 
147     @Implements(BatteryTipLoader.class)
148     public static class ShadowBatteryTipLoader {
149 
150         private static List<BatteryTip> sBatteryTips = new ArrayList<>();
151 
152         @Resetter
reset()153         public static void reset() {
154             sBatteryTips = new ArrayList<>();
155         }
156 
157         @Implementation
loadInBackground()158         protected List<BatteryTip> loadInBackground() {
159             return sBatteryTips;
160         }
161 
setBatteryTips(List<BatteryTip> tips)162         private static void setBatteryTips(List<BatteryTip> tips) {
163             sBatteryTips = tips;
164         }
165     }
166 
167     @Implements(SliceBackgroundWorker.class)
168     public static class ShadowSliceBackgroundWorker {
169 
170         private static BatteryFixSlice.BatteryTipWorker sBatteryTipWorkerWorker;
171 
172         @Resetter
reset()173         public static void reset() {
174             sBatteryTipWorkerWorker = null;
175         }
176 
177         @Implementation
getInstance(Uri uri)178         protected static <T extends SliceBackgroundWorker> T getInstance(Uri uri) {
179             return (T) sBatteryTipWorkerWorker;
180         }
181 
setBatteryTipWorkerWorker(BatteryFixSlice.BatteryTipWorker worker)182         public static void setBatteryTipWorkerWorker(BatteryFixSlice.BatteryTipWorker worker) {
183             sBatteryTipWorkerWorker = worker;
184         }
185     }
186 
187     @Implements(EarlyWarningTip.class)
188     public static class ShadowEarlyWarningTip {
189 
190         private static boolean mIsGetIconTintColorIdCalled;
191 
192         @Resetter
reset()193         public static void reset() {
194             mIsGetIconTintColorIdCalled = false;
195         }
196 
197         @Implementation
getIconTintColorId()198         protected int getIconTintColorId() {
199             mIsGetIconTintColorIdCalled = true;
200             return R.color.battery_bad_color_light;
201         }
202 
isIconTintColorIdCalled()203         public static boolean isIconTintColorIdCalled() {
204             return mIsGetIconTintColorIdCalled;
205         }
206     }
207 }
208