• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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 package com.android.systemui.dreams.homecontrols
17 
18 import android.app.Activity
19 import android.content.ComponentName
20 import android.content.Intent
21 import android.os.powerManager
22 import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_ACTIVITY_PANEL
23 import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_DREAM
24 import android.service.controls.ControlsProviderService.EXTRA_CONTROLS_SURFACE
25 import android.service.dreams.DreamService
26 import android.window.TaskFragmentInfo
27 import androidx.lifecycle.testing.TestLifecycleOwner
28 import androidx.test.ext.junit.runners.AndroidJUnit4
29 import androidx.test.filters.SmallTest
30 import com.android.systemui.SysuiTestCase
31 import com.android.systemui.dreams.homecontrols.service.TaskFragmentComponent
32 import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
33 import com.android.systemui.dreams.homecontrols.shared.model.fakeHomeControlsDataSource
34 import com.android.systemui.dreams.homecontrols.shared.model.homeControlsDataSource
35 import com.android.systemui.kosmos.testDispatcher
36 import com.android.systemui.kosmos.testScope
37 import com.android.systemui.log.logcatLogBuffer
38 import com.android.systemui.testKosmos
39 import com.android.systemui.util.time.fakeSystemClock
40 import com.android.systemui.util.wakelock.WakeLockFake
41 import com.google.common.truth.Truth.assertThat
42 import kotlinx.coroutines.Dispatchers
43 import kotlinx.coroutines.test.advanceUntilIdle
44 import kotlinx.coroutines.test.resetMain
45 import kotlinx.coroutines.test.runCurrent
46 import kotlinx.coroutines.test.runTest
47 import kotlinx.coroutines.test.setMain
48 import org.junit.After
49 import org.junit.Before
50 import org.junit.Test
51 import org.junit.runner.RunWith
52 import org.mockito.kotlin.any
53 import org.mockito.kotlin.argThat
54 import org.mockito.kotlin.argumentCaptor
55 import org.mockito.kotlin.doReturn
56 import org.mockito.kotlin.eq
57 import org.mockito.kotlin.mock
58 import org.mockito.kotlin.never
59 import org.mockito.kotlin.times
60 import org.mockito.kotlin.verify
61 
62 @SmallTest
63 @RunWith(AndroidJUnit4::class)
64 class HomeControlsDreamServiceTest : SysuiTestCase() {
65 
66     private val kosmos = testKosmos()
67     private val testScope = kosmos.testScope
68 
69     private val fakeWakeLock = WakeLockFake()
<lambda>null70     private val fakeWakeLockBuilder by lazy {
71         WakeLockFake.Builder(context).apply { setWakeLock(fakeWakeLock) }
72     }
73 
74     private val lifecycleOwner = TestLifecycleOwner(coroutineDispatcher = kosmos.testDispatcher)
75 
76     private val taskFragmentComponent = mock<TaskFragmentComponent>()
77     private val activity = mock<Activity>()
78     private val onCreateCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>()
79     private val onInfoChangedCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>()
80     private val hideCallback = argumentCaptor<() -> Unit>()
81     private var dreamService =
<lambda>null82         mock<DreamService> {
83             on { activity } doReturn activity
84             on { redirectWake } doReturn false
85         }
86 
87     private val taskFragmentComponentFactory =
<lambda>null88         mock<TaskFragmentComponent.Factory> {
89             on {
90                 create(
91                     activity = eq(activity),
92                     onCreateCallback = onCreateCallback.capture(),
93                     onInfoChangedCallback = onInfoChangedCallback.capture(),
94                     hide = hideCallback.capture(),
95                 )
96             } doReturn taskFragmentComponent
97         }
98 
<lambda>null99     private val underTest: HomeControlsDreamServiceImpl by lazy {
100         with(kosmos) {
101             HomeControlsDreamServiceImpl(
102                 taskFragmentFactory = taskFragmentComponentFactory,
103                 wakeLockBuilder = fakeWakeLockBuilder,
104                 powerManager = powerManager,
105                 systemClock = fakeSystemClock,
106                 dataSource = homeControlsDataSource,
107                 logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest"),
108                 service = dreamService,
109                 lifecycleOwner = lifecycleOwner,
110             )
111         }
112     }
113 
114     @Before
setupnull115     fun setup() {
116         Dispatchers.setMain(kosmos.testDispatcher)
117         kosmos.fakeHomeControlsDataSource.setComponentInfo(
118             HomeControlsComponentInfo(PANEL_COMPONENT, true)
119         )
120     }
121 
122     @After
tearDownnull123     fun tearDown() {
124         Dispatchers.resetMain()
125     }
126 
127     @Test
testOnAttachedToWindowCreatesTaskFragmentComponentnull128     fun testOnAttachedToWindowCreatesTaskFragmentComponent() =
129         testScope.runTest {
130             underTest.onAttachedToWindow()
131             verify(taskFragmentComponentFactory).create(any(), any(), any(), any())
132         }
133 
134     @Test
testOnDetachedFromWindowDestroyTaskFragmentComponentnull135     fun testOnDetachedFromWindowDestroyTaskFragmentComponent() =
136         testScope.runTest {
137             underTest.onAttachedToWindow()
138             underTest.onDetachedFromWindow()
139             verify(taskFragmentComponent).destroy()
140         }
141 
142     @Test
testNotCreatingTaskFragmentComponentWhenActivityIsNullnull143     fun testNotCreatingTaskFragmentComponentWhenActivityIsNull() =
144         testScope.runTest {
145             dreamService = mock<DreamService> { on { activity } doReturn null }
146             underTest.onAttachedToWindow()
147             verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any())
148             verify(dreamService).finish()
149         }
150 
151     @Test
testAttachWindow_wakeLockAcquirednull152     fun testAttachWindow_wakeLockAcquired() =
153         testScope.runTest {
154             underTest.onAttachedToWindow()
155             assertThat(fakeWakeLock.isHeld).isTrue()
156         }
157 
158     @Test
testDetachWindow_wakeLockCanBeReleasednull159     fun testDetachWindow_wakeLockCanBeReleased() =
160         testScope.runTest {
161             underTest.onAttachedToWindow()
162             assertThat(fakeWakeLock.isHeld).isTrue()
163 
164             underTest.onDetachedFromWindow()
165             assertThat(fakeWakeLock.isHeld).isFalse()
166         }
167 
168     @Test
testFinishesDreamWithoutRestartingActivityWhenNotRedirectingWakesnull169     fun testFinishesDreamWithoutRestartingActivityWhenNotRedirectingWakes() =
170         testScope.runTest {
171             underTest.onAttachedToWindow()
172             onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>())
173             runCurrent()
174             verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
175 
176             // Task fragment becomes empty
177             onInfoChangedCallback.firstValue.invoke(
178                 mock<TaskFragmentInfo> { on { isEmpty } doReturn true }
179             )
180             advanceUntilIdle()
181             // Dream is finished and activity is not restarted
182             verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
183             verify(dreamService, never()).wakeUp()
184             verify(dreamService).finish()
185         }
186 
187     @Test
testRestartsActivityWhenRedirectingWakesnull188     fun testRestartsActivityWhenRedirectingWakes() =
189         testScope.runTest {
190             dreamService =
191                 mock<DreamService> {
192                     on { activity } doReturn activity
193                     on { redirectWake } doReturn true
194                 }
195             underTest.onAttachedToWindow()
196             onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>())
197             runCurrent()
198             verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
199 
200             // Task fragment becomes empty
201             onInfoChangedCallback.firstValue.invoke(
202                 mock<TaskFragmentInfo> { on { isEmpty } doReturn true }
203             )
204             advanceUntilIdle()
205 
206             // Activity is restarted instead of finishing the dream.
207             verify(taskFragmentComponent, times(2)).startActivityInTaskFragment(intentMatcher())
208             verify(dreamService).wakeUp()
209             verify(dreamService, never()).finish()
210         }
211 
intentMatchernull212     private fun intentMatcher() =
213         argThat<Intent> {
214             getIntExtra(EXTRA_CONTROLS_SURFACE, CONTROLS_SURFACE_ACTIVITY_PANEL) ==
215                 CONTROLS_SURFACE_DREAM && component == PANEL_COMPONENT
216         }
217 
218     private companion object {
219         val PANEL_COMPONENT = ComponentName("test.pkg", "test.panel")
220     }
221 }
222