1 /*
2  * Copyright (C) 2023 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.systemui.communal.ui.widgets
18 
19 import android.testing.TestableLooper
20 import android.testing.TestableLooper.RunWithLooper
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.SysuiTestCase
24 import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
25 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
26 import com.android.systemui.coroutines.collectLastValue
27 import com.android.systemui.kosmos.applicationCoroutineScope
28 import com.android.systemui.kosmos.testScope
29 import com.android.systemui.log.logcatLogBuffer
30 import com.android.systemui.testKosmos
31 import com.android.systemui.util.mockito.any
32 import com.android.systemui.util.mockito.eq
33 import com.android.systemui.util.mockito.mock
34 import com.google.common.truth.Truth.assertThat
35 import kotlinx.coroutines.ExperimentalCoroutinesApi
36 import kotlinx.coroutines.test.runCurrent
37 import kotlinx.coroutines.test.runTest
38 import org.junit.Before
39 import org.junit.Test
40 import org.junit.runner.RunWith
41 import org.mockito.Mockito.clearInvocations
42 import org.mockito.Mockito.never
43 import org.mockito.Mockito.verify
44 
45 @OptIn(ExperimentalCoroutinesApi::class)
46 @SmallTest
47 @RunWithLooper(setAsMainLooper = true)
48 @RunWith(AndroidJUnit4::class)
49 class CommunalAppWidgetHostTest : SysuiTestCase() {
50     private val kosmos = testKosmos()
51     private val testScope = kosmos.testScope
52 
53     private lateinit var testableLooper: TestableLooper
54     private lateinit var underTest: CommunalAppWidgetHost
55 
56     @Before
setUpnull57     fun setUp() {
58         testableLooper = TestableLooper.get(this)
59         underTest =
60             CommunalAppWidgetHost(
61                 context = context,
62                 backgroundScope = kosmos.applicationCoroutineScope,
63                 hostId = 116,
64                 logBuffer = logcatLogBuffer("CommunalAppWidgetHostTest"),
65                 glanceableHubMultiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper,
66             )
67     }
68 
69     @Test
appWidgetIdToRemove_emitnull70     fun appWidgetIdToRemove_emit() =
71         testScope.runTest {
72             val appWidgetIdToRemove by collectLastValue(underTest.appWidgetIdToRemove)
73 
74             // Nothing should be emitted yet
75             assertThat(appWidgetIdToRemove).isNull()
76 
77             underTest.onAppWidgetRemoved(appWidgetId = 1)
78             runCurrent()
79 
80             assertThat(appWidgetIdToRemove).isEqualTo(1)
81 
82             underTest.onAppWidgetRemoved(appWidgetId = 2)
83             runCurrent()
84 
85             assertThat(appWidgetIdToRemove).isEqualTo(2)
86         }
87 
88     @Test
observer_onHostStartListeningTriggeredWhileObserverActivenull89     fun observer_onHostStartListeningTriggeredWhileObserverActive() =
90         testScope.runTest {
91             // Observer added
92             val observer = mock<CommunalAppWidgetHost.Observer>()
93             underTest.addObserver(observer)
94             runCurrent()
95 
96             // Verify callback triggered
97             verify(observer, never()).onHostStartListening()
98             underTest.startListening()
99             runCurrent()
100             verify(observer).onHostStartListening()
101 
102             clearInvocations(observer)
103 
104             // Observer removed
105             underTest.removeObserver(observer)
106             runCurrent()
107 
108             // Verify callback not triggered
109             underTest.startListening()
110             runCurrent()
111             verify(observer, never()).onHostStartListening()
112         }
113 
114     @Test
observer_onHostStopListeningTriggeredWhileObserverActivenull115     fun observer_onHostStopListeningTriggeredWhileObserverActive() =
116         testScope.runTest {
117             // Observer added
118             val observer = mock<CommunalAppWidgetHost.Observer>()
119             underTest.addObserver(observer)
120             runCurrent()
121 
122             // Verify callback triggered
123             verify(observer, never()).onHostStopListening()
124             underTest.stopListening()
125             runCurrent()
126             verify(observer).onHostStopListening()
127 
128             clearInvocations(observer)
129 
130             // Observer removed
131             underTest.removeObserver(observer)
132             runCurrent()
133 
134             // Verify callback not triggered
135             underTest.stopListening()
136             runCurrent()
137             verify(observer, never()).onHostStopListening()
138         }
139 
140     @Test
observer_onAllocateAppWidgetIdTriggeredWhileObserverActivenull141     fun observer_onAllocateAppWidgetIdTriggeredWhileObserverActive() =
142         testScope.runTest {
143             // Observer added
144             val observer = mock<CommunalAppWidgetHost.Observer>()
145             underTest.addObserver(observer)
146             runCurrent()
147 
148             // Verify callback triggered
149             verify(observer, never()).onAllocateAppWidgetId(any())
150             val id = underTest.allocateAppWidgetId()
151             runCurrent()
152             verify(observer).onAllocateAppWidgetId(eq(id))
153 
154             clearInvocations(observer)
155 
156             // Observer removed
157             underTest.removeObserver(observer)
158             runCurrent()
159 
160             // Verify callback not triggered
161             underTest.allocateAppWidgetId()
162             runCurrent()
163             verify(observer, never()).onAllocateAppWidgetId(any())
164         }
165 
166     @Test
observer_onDeleteAppWidgetIdTriggeredWhileObserverActivenull167     fun observer_onDeleteAppWidgetIdTriggeredWhileObserverActive() =
168         testScope.runTest {
169             // Observer added
170             val observer = mock<CommunalAppWidgetHost.Observer>()
171             underTest.addObserver(observer)
172             runCurrent()
173 
174             // Verify callback triggered
175             verify(observer, never()).onDeleteAppWidgetId(any())
176             underTest.deleteAppWidgetId(1)
177             runCurrent()
178             verify(observer).onDeleteAppWidgetId(eq(1))
179 
180             clearInvocations(observer)
181 
182             // Observer removed
183             underTest.removeObserver(observer)
184             runCurrent()
185 
186             // Verify callback not triggered
187             underTest.deleteAppWidgetId(2)
188             runCurrent()
189             verify(observer, never()).onDeleteAppWidgetId(any())
190         }
191 
192     @Test
observer_multipleObserversnull193     fun observer_multipleObservers() =
194         testScope.runTest {
195             // Set up two observers
196             val observer1 = mock<CommunalAppWidgetHost.Observer>()
197             val observer2 = mock<CommunalAppWidgetHost.Observer>()
198             underTest.addObserver(observer1)
199             underTest.addObserver(observer2)
200             runCurrent()
201 
202             // Verify both observers triggered
203             verify(observer1, never()).onHostStartListening()
204             verify(observer2, never()).onHostStartListening()
205             underTest.startListening()
206             runCurrent()
207             verify(observer1).onHostStartListening()
208             verify(observer2).onHostStartListening()
209 
210             // Observer 1 removed
211             underTest.removeObserver(observer1)
212             runCurrent()
213 
214             // Verify only observer 2 is triggered
215             underTest.stopListening()
216             runCurrent()
217             verify(observer2).onHostStopListening()
218             verify(observer1, never()).onHostStopListening()
219         }
220 }
221