1 /*
<lambda>null2  * Copyright (C) 2024 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.fuelgauge.batteryusage
18 
19 import android.content.Context
20 import androidx.test.core.app.ApplicationProvider
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action
23 import com.android.settings.fuelgauge.BatteryOptimizeUtils
24 import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_OPTIMIZED
25 import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_RESTRICTED
26 import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNKNOWN
27 import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNRESTRICTED
28 import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils.UNLIMITED_EXPIRE_TIME
29 import com.android.settings.testutils.FakeFeatureFactory
30 import com.google.common.truth.Truth.assertThat
31 import org.junit.After
32 import org.junit.Before
33 import org.junit.Rule
34 import org.junit.Test
35 import org.junit.runner.RunWith
36 import org.mockito.ArgumentMatchers.any
37 import org.mockito.Mockito.anyInt
38 import org.mockito.Mockito.never
39 import org.mockito.Mockito.spy
40 import org.mockito.Mockito.verify
41 import org.mockito.Mockito.`when` as whenever
42 import org.mockito.Spy
43 import org.mockito.junit.MockitoJUnit
44 import org.mockito.junit.MockitoRule
45 
46 @RunWith(AndroidJUnit4::class)
47 class AppOptModeSharedPreferencesUtilsTest {
48     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
49 
50     @Spy private var context: Context = ApplicationProvider.getApplicationContext()
51 
52     @Spy
53     private var testBatteryOptimizeUtils = spy(BatteryOptimizeUtils(context, UID, PACKAGE_NAME))
54 
55     private lateinit var featureFactory: FakeFeatureFactory
56 
57     @Before
58     fun setup() {
59         AppOptModeSharedPreferencesUtils.clearAll(context)
60         featureFactory = FakeFeatureFactory.setupForTest()
61         whenever(featureFactory.powerUsageFeatureProvider.isForceExpireAppOptimizationModeEnabled)
62             .thenReturn(false)
63     }
64 
65     @After
66     fun tearDown() {
67         AppOptModeSharedPreferencesUtils.clearAll(context)
68     }
69 
70     @Test
71     fun getAllEvents_emptyData_verifyEmptyList() {
72         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
73     }
74 
75     @Test
76     fun clearAll_withData_verifyCleared() {
77         insertAppOptModeEventForTest(expirationTime = 1000L)
78         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).hasSize(1)
79 
80         AppOptModeSharedPreferencesUtils.clearAll(context)
81 
82         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
83     }
84 
85     @Test
86     fun updateAppOptModeExpirationInternal_withoutExpirationTime_verifyEmptyList() {
87         insertAppOptModeEventForTest(expirationTime = UNLIMITED_EXPIRE_TIME)
88 
89         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
90     }
91 
92     @Test
93     fun updateAppOptModeExpirationInternal_setOptimizedModeWithFlagEnabled_verifyData() {
94         whenever(featureFactory.powerUsageFeatureProvider.isRestrictedModeOverwriteEnabled)
95             .thenReturn(true)
96         insertAppOptModeEventForTest(expirationTime = 1000L, mode = MODE_OPTIMIZED)
97 
98         val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
99 
100         assertThat(events).hasSize(1)
101         assertAppOptimizationModeEventInfo(
102             events[0],
103             UID,
104             PACKAGE_NAME,
105             MODE_OPTIMIZED,
106             expirationTime = 1000L
107         )
108     }
109 
110     @Test
111     fun updateAppOptModeExpirationInternal_setOptimizedModeWithFlagDisabled_verifyData() {
112         whenever(featureFactory.powerUsageFeatureProvider.isRestrictedModeOverwriteEnabled)
113             .thenReturn(false)
114         insertAppOptModeEventForTest(expirationTime = 1000L, mode = MODE_OPTIMIZED)
115 
116         val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
117 
118         assertThat(events).hasSize(1)
119         assertAppOptimizationModeEventInfo(
120             events[0],
121             UID,
122             PACKAGE_NAME,
123             MODE_OPTIMIZED,
124             expirationTime = 1000L
125         )
126     }
127 
128     @Test
129     fun updateAppOptModeExpirationInternal_setRestrictedModeWithFlagEnabled_verifyData() {
130         whenever(featureFactory.powerUsageFeatureProvider.isRestrictedModeOverwriteEnabled)
131             .thenReturn(true)
132         insertAppOptModeEventForTest(expirationTime = 1000L, mode = MODE_RESTRICTED)
133 
134         val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
135 
136         assertThat(events).hasSize(1)
137         assertAppOptimizationModeEventInfo(
138             events[0],
139             UID,
140             PACKAGE_NAME,
141             MODE_RESTRICTED,
142             expirationTime = 1000L
143         )
144     }
145 
146     @Test
147     fun updateAppOptModeExpirationInternal_setRestrictedModeWithFlagDisabled_verifyEmptyList() {
148         whenever(featureFactory.powerUsageFeatureProvider.isRestrictedModeOverwriteEnabled)
149             .thenReturn(false)
150         insertAppOptModeEventForTest(expirationTime = 1000L, mode = MODE_RESTRICTED)
151 
152         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
153     }
154 
155     @Test
156     fun deleteAppOptimizationModeEventByUid_uidNotContained_verifyData() {
157         insertAppOptModeEventForTest(expirationTime = 1000L)
158         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).hasSize(1)
159 
160         AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(context, UNSET_UID)
161         val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
162 
163         assertThat(events).hasSize(1)
164         assertAppOptimizationModeEventInfo(
165             events[0],
166             UID,
167             PACKAGE_NAME,
168             MODE_OPTIMIZED,
169             expirationTime = 1000L
170         )
171     }
172 
173     @Test
174     fun deleteAppOptimizationModeEventByUid_uidExisting_verifyData() {
175         insertAppOptModeEventForTest(expirationTime = 1000L)
176 
177         AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(context, UID)
178 
179         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
180     }
181 
182     @Test
183     fun resetExpiredAppOptModeBeforeTimestamp_forceExpiredData_verifyEmptyList() {
184         whenever(featureFactory.powerUsageFeatureProvider.isForceExpireAppOptimizationModeEnabled)
185             .thenReturn(true)
186         insertAppOptModeEventForTest(expirationTime = 1000L)
187 
188         AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(
189             context,
190             queryTimestampMs = 999L
191         )
192 
193         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
194     }
195 
196     @Test
197     fun resetExpiredAppOptModeBeforeTimestamp_noExpiredData_verifyData() {
198         insertAppOptModeEventForTest(expirationTime = 1000L)
199 
200         AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(
201             context,
202             queryTimestampMs = 999L
203         )
204         val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
205 
206         assertThat(events).hasSize(1)
207         assertAppOptimizationModeEventInfo(
208             events[0],
209             UID,
210             PACKAGE_NAME,
211             MODE_OPTIMIZED,
212             expirationTime = 1000L
213         )
214     }
215 
216     @Test
217     fun resetExpiredAppOptModeBeforeTimestamp_hasExpiredData_verifyEmptyList() {
218         insertAppOptModeEventForTest(expirationTime = 1000L)
219 
220         AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(
221             context,
222             queryTimestampMs = 1001L
223         )
224 
225         assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
226     }
227 
228     @Test
229     fun updateBatteryOptimizationMode_updateToOptimizedMode_verifyAction() {
230         whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(true)
231         whenever(testBatteryOptimizeUtils?.getAppOptimizationMode())
232             .thenReturn(MODE_UNRESTRICTED)
233 
234         val currentOptMode =
235             AppOptModeSharedPreferencesUtils.updateBatteryOptimizationMode(
236                 context,
237                 UID,
238                 PACKAGE_NAME,
239                 MODE_OPTIMIZED,
240                 Action.EXTERNAL_UPDATE,
241                 testBatteryOptimizeUtils
242             )
243 
244         verify(testBatteryOptimizeUtils)?.setAppUsageState(MODE_OPTIMIZED, Action.EXTERNAL_UPDATE)
245         assertThat(currentOptMode).isEqualTo(MODE_UNRESTRICTED)
246     }
247 
248     @Test
249     fun updateBatteryOptimizationMode_optimizationModeImmutable_verifyAction() {
250         whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(false)
251         whenever(testBatteryOptimizeUtils?.appOptimizationMode)
252             .thenReturn(MODE_UNRESTRICTED)
253 
254         val currentOptMode =
255             AppOptModeSharedPreferencesUtils.updateBatteryOptimizationMode(
256                 context,
257                 UID,
258                 PACKAGE_NAME,
259                 MODE_OPTIMIZED,
260                 Action.EXTERNAL_UPDATE,
261                 testBatteryOptimizeUtils
262             )
263 
264         verify(testBatteryOptimizeUtils, never())?.setAppUsageState(anyInt(), any())
265         assertThat(currentOptMode).isEqualTo(MODE_UNKNOWN)
266     }
267 
268     @Test
269     fun updateBatteryOptimizationMode_updateToSameOptimizationMode_verifyAction() {
270         whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(true)
271         whenever(testBatteryOptimizeUtils?.appOptimizationMode).thenReturn(MODE_RESTRICTED)
272 
273         val currentOptMode =
274             AppOptModeSharedPreferencesUtils.updateBatteryOptimizationMode(
275                 context,
276                 UID,
277                 PACKAGE_NAME,
278                 MODE_RESTRICTED,
279                 Action.EXTERNAL_UPDATE,
280                 testBatteryOptimizeUtils
281             )
282 
283         verify(testBatteryOptimizeUtils)?.setAppUsageState(MODE_RESTRICTED, Action.EXTERNAL_UPDATE)
284         assertThat(currentOptMode).isEqualTo(MODE_RESTRICTED)
285     }
286 
287     private fun insertAppOptModeEventForTest(expirationTime: Long, mode: Int = MODE_OPTIMIZED) {
288         whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(true)
289         whenever(testBatteryOptimizeUtils?.appOptimizationMode).thenReturn(mode)
290         AppOptModeSharedPreferencesUtils.updateAppOptModeExpirationInternal(
291             context,
292             mutableListOf(UID),
293             mutableListOf(PACKAGE_NAME),
294             mutableListOf(mode),
295             longArrayOf(expirationTime),
296         ) { _: Int, _: String ->
297             testBatteryOptimizeUtils
298         }
299     }
300 
301     companion object {
302         const val UID: Int = 12345
303         const val UNSET_UID: Int = 15432
304         const val PACKAGE_NAME: String = "com.android.app"
305 
306         private fun assertAppOptimizationModeEventInfo(
307             event: AppOptimizationModeEvent,
308             uid: Int,
309             packageName: String,
310             resetOptimizationMode: Int,
311             expirationTime: Long
312         ) {
313             assertThat(event.uid).isEqualTo(uid)
314             assertThat(event.packageName).isEqualTo(packageName)
315             assertThat(event.resetOptimizationMode).isEqualTo(resetOptimizationMode)
316             assertThat(event.expirationTime).isEqualTo(expirationTime)
317         }
318     }
319 }
320