1 /*
2  * Copyright (C) 2022 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 
18 package com.android.systemui.power.data.repository
19 
20 import android.content.BroadcastReceiver
21 import android.content.Intent
22 import android.content.IntentFilter
23 import android.os.PowerManager
24 import androidx.test.ext.junit.runners.AndroidJUnit4
25 import androidx.test.filters.SmallTest
26 import com.android.systemui.SysuiTestCase
27 import com.android.systemui.broadcast.BroadcastDispatcher
28 import com.android.systemui.util.mockito.any
29 import com.android.systemui.util.mockito.argumentCaptor
30 import com.android.systemui.util.mockito.capture
31 import com.android.systemui.util.mockito.eq
32 import com.android.systemui.util.time.FakeSystemClock
33 import com.google.common.truth.Truth.assertThat
34 import kotlinx.coroutines.Dispatchers
35 import kotlinx.coroutines.flow.launchIn
36 import kotlinx.coroutines.flow.onEach
37 import kotlinx.coroutines.runBlocking
38 import org.junit.Before
39 import org.junit.Test
40 import org.junit.runner.RunWith
41 import org.mockito.ArgumentCaptor
42 import org.mockito.Captor
43 import org.mockito.Mock
44 import org.mockito.Mockito.anyInt
45 import org.mockito.Mockito.isNull
46 import org.mockito.Mockito.verify
47 import org.mockito.Mockito.`when` as whenever
48 import org.mockito.MockitoAnnotations
49 
50 @SmallTest
51 @RunWith(AndroidJUnit4::class)
52 class PowerRepositoryImplTest : SysuiTestCase() {
53 
54     private val systemClock = FakeSystemClock()
55 
56     @Mock private lateinit var manager: PowerManager
57     @Mock private lateinit var dispatcher: BroadcastDispatcher
58     @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
59     @Captor private lateinit var filterCaptor: ArgumentCaptor<IntentFilter>
60 
61     private lateinit var underTest: PowerRepositoryImpl
62 
63     private var isInteractive = true
64 
65     @Before
setUpnull66     fun setUp() {
67         MockitoAnnotations.initMocks(this)
68         isInteractive = true
69         whenever(manager.isInteractive).then { isInteractive }
70 
71         underTest =
72             PowerRepositoryImpl(
73                 manager,
74                 context.applicationContext,
75                 systemClock,
76                 dispatcher,
77             )
78     }
79 
80     @Test
isInteractive_registersForBroadcastsnull81     fun isInteractive_registersForBroadcasts() =
82         runBlocking(IMMEDIATE) {
83             val job = underTest.isInteractive.onEach {}.launchIn(this)
84 
85             verifyRegistered()
86             assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_ON)).isTrue()
87             assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_OFF)).isTrue()
88 
89             job.cancel()
90         }
91 
92     @Test
isInteractive_unregistersFromBroadcastsnull93     fun isInteractive_unregistersFromBroadcasts() =
94         runBlocking(IMMEDIATE) {
95             val job = underTest.isInteractive.onEach {}.launchIn(this)
96             verifyRegistered()
97 
98             job.cancel()
99 
100             verify(dispatcher).unregisterReceiver(receiverCaptor.value)
101         }
102 
103     @Test
isInteractive_emitsInitialTrueValueIfScreenWasOnnull104     fun isInteractive_emitsInitialTrueValueIfScreenWasOn() =
105         runBlocking(IMMEDIATE) {
106             isInteractive = true
107             var value: Boolean? = null
108             val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
109 
110             verifyRegistered()
111 
112             assertThat(value).isTrue()
113             job.cancel()
114         }
115 
116     @Test
isInteractive_emitsInitialFalseValueIfScreenWasOffnull117     fun isInteractive_emitsInitialFalseValueIfScreenWasOff() =
118         runBlocking(IMMEDIATE) {
119             isInteractive = false
120             var value: Boolean? = null
121             val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
122 
123             verifyRegistered()
124 
125             assertThat(value).isFalse()
126             job.cancel()
127         }
128 
129     @Test
isInteractive_emitsTrueWhenTheScreenTurnsOnnull130     fun isInteractive_emitsTrueWhenTheScreenTurnsOn() =
131         runBlocking(IMMEDIATE) {
132             var value: Boolean? = null
133             val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
134             verifyRegistered()
135 
136             isInteractive = true
137             receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))
138 
139             assertThat(value).isTrue()
140             job.cancel()
141         }
142 
143     @Test
isInteractive_emitsFalseWhenTheScreenTurnsOffnull144     fun isInteractive_emitsFalseWhenTheScreenTurnsOff() =
145         runBlocking(IMMEDIATE) {
146             var value: Boolean? = null
147             val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
148             verifyRegistered()
149 
150             isInteractive = false
151             receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
152 
153             assertThat(value).isFalse()
154             job.cancel()
155         }
156 
157     @Test
isInteractive_emitsCorrectlyOverTimenull158     fun isInteractive_emitsCorrectlyOverTime() =
159         runBlocking(IMMEDIATE) {
160             val values = mutableListOf<Boolean>()
161             val job = underTest.isInteractive.onEach(values::add).launchIn(this)
162             verifyRegistered()
163 
164             isInteractive = false
165             receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
166             isInteractive = true
167             receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))
168             isInteractive = false
169             receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
170 
171             assertThat(values).isEqualTo(listOf(true, false, true, false))
172             job.cancel()
173         }
174 
175     @Test
wakeUp_notifiesPowerManagernull176     fun wakeUp_notifiesPowerManager() {
177         systemClock.setUptimeMillis(345000)
178 
179         underTest.wakeUp("fakeWhy", PowerManager.WAKE_REASON_GESTURE)
180 
181         val reasonCaptor = argumentCaptor<String>()
182         verify(manager)
183             .wakeUp(eq(345000L), eq(PowerManager.WAKE_REASON_GESTURE), capture(reasonCaptor))
184         assertThat(reasonCaptor.value).contains("fakeWhy")
185     }
186 
187     @Test
wakeUp_usesApplicationPackageNamenull188     fun wakeUp_usesApplicationPackageName() {
189         underTest.wakeUp("fakeWhy", PowerManager.WAKE_REASON_GESTURE)
190 
191         val reasonCaptor = argumentCaptor<String>()
192         verify(manager).wakeUp(any(), any(), capture(reasonCaptor))
193         assertThat(reasonCaptor.value).contains(context.applicationContext.packageName)
194     }
195 
196     @Test
userActivity_notifiesPowerManagernull197     fun userActivity_notifiesPowerManager() {
198         systemClock.setUptimeMillis(345000)
199 
200         underTest.userTouch()
201 
202         val flagsCaptor = argumentCaptor<Int>()
203         verify(manager)
204             .userActivity(
205                 eq(345000L),
206                 eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
207                 capture(flagsCaptor)
208             )
209         assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS)
210         assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_INDIRECT)
211     }
212 
213     @Test
userActivity_notifiesPowerManager_noChangeLightsTruenull214     fun userActivity_notifiesPowerManager_noChangeLightsTrue() {
215         systemClock.setUptimeMillis(345000)
216 
217         underTest.userTouch(noChangeLights = true)
218 
219         val flagsCaptor = argumentCaptor<Int>()
220         verify(manager)
221             .userActivity(
222                 eq(345000L),
223                 eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
224                 capture(flagsCaptor)
225             )
226         assertThat(flagsCaptor.value).isEqualTo(PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS)
227     }
228 
verifyRegisterednull229     private fun verifyRegistered() {
230         // We must verify with all arguments, even those that are optional because they have default
231         // values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be
232         // able to remove this.
233         verify(dispatcher)
234             .registerReceiver(
235                 capture(receiverCaptor),
236                 capture(filterCaptor),
237                 isNull(),
238                 isNull(),
239                 anyInt(),
240                 isNull(),
241             )
242     }
243 
244     companion object {
245         private val IMMEDIATE = Dispatchers.Main.immediate
246     }
247 }
248