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 package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
18 
19 import androidx.test.ext.junit.runners.AndroidJUnit4
20 import androidx.test.filters.SmallTest
21 import com.android.systemui.RoboPilotTest
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.coroutines.collectLastValue
24 import com.android.systemui.log.table.TableLogBuffer
25 import com.android.systemui.statusbar.phone.StatusBarLocation
26 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
27 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
28 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
29 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
30 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
31 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
32 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
33 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
34 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
35 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
36 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
37 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
38 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
39 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
40 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
41 import com.google.common.truth.Truth.assertThat
42 import kotlinx.coroutines.flow.MutableStateFlow
43 import kotlinx.coroutines.test.TestScope
44 import kotlinx.coroutines.test.runTest
45 import org.junit.Before
46 import org.junit.Test
47 import org.junit.runner.RunWith
48 import org.mockito.Mock
49 import org.mockito.Mockito.`when` as whenever
50 import org.mockito.MockitoAnnotations
51 
52 @SmallTest
53 @RoboPilotTest
54 @RunWith(AndroidJUnit4::class)
55 class WifiViewModelTest : SysuiTestCase() {
56 
57     private lateinit var underTest: WifiViewModel
58 
59     @Mock private lateinit var tableLogBuffer: TableLogBuffer
60     @Mock private lateinit var connectivityConstants: ConnectivityConstants
61     @Mock private lateinit var wifiConstants: WifiConstants
62     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
63     private lateinit var connectivityRepository: FakeConnectivityRepository
64     private lateinit var wifiRepository: FakeWifiRepository
65     private lateinit var interactor: WifiInteractor
66     private lateinit var airplaneModeViewModel: AirplaneModeViewModel
67     private val shouldShowSignalSpacerProviderFlow = MutableStateFlow(false)
68     private val testScope = TestScope()
69 
70     @Before
71     fun setUp() {
72         MockitoAnnotations.initMocks(this)
73         airplaneModeRepository = FakeAirplaneModeRepository()
74         connectivityRepository = FakeConnectivityRepository()
75         wifiRepository = FakeWifiRepository()
76         wifiRepository.setIsWifiEnabled(true)
77         interactor =
78             WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
79         airplaneModeViewModel =
80             AirplaneModeViewModelImpl(
81                 AirplaneModeInteractor(
82                     airplaneModeRepository,
83                     connectivityRepository,
84                 ),
85                 tableLogBuffer,
86                 testScope.backgroundScope,
87             )
88 
89         createAndSetViewModel()
90     }
91 
92     // See [WifiViewModelIconParameterizedTest] for additional view model tests.
93 
94     // Note on testing: [WifiViewModel] exposes 3 different instances of
95     // [LocationBasedWifiViewModel]. In practice, these 3 different instances will get the exact
96     // same data for icon, activity, etc. flows. So, most of these tests will test just one of the
97     // instances. There are also some tests that verify all 3 instances received the same data.
98 
99     @Test
100     fun wifiIcon_allLocationViewModelsReceiveSameData() =
101         testScope.runTest {
102             val home = viewModelForLocation(underTest, StatusBarLocation.HOME)
103             val keyguard = viewModelForLocation(underTest, StatusBarLocation.KEYGUARD)
104             val qs = viewModelForLocation(underTest, StatusBarLocation.QS)
105 
106             val latestHome by collectLastValue(home.wifiIcon)
107             val latestKeyguard by collectLastValue(keyguard.wifiIcon)
108             val latestQs by collectLastValue(qs.wifiIcon)
109 
110             wifiRepository.setWifiNetwork(
111                 WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1)
112             )
113 
114             assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
115             assertThat(latestHome).isEqualTo(latestKeyguard)
116             assertThat(latestKeyguard).isEqualTo(latestQs)
117         }
118 
119     @Test
120     fun activity_showActivityConfigFalse_outputsFalse() =
121         testScope.runTest {
122             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
123             createAndSetViewModel()
124             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
125 
126             val activityIn by collectLastValue(underTest.isActivityInViewVisible)
127             val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
128             val activityContainer by collectLastValue(underTest.isActivityContainerVisible)
129 
130             // Verify that on launch, we receive false.
131             assertThat(activityIn).isFalse()
132             assertThat(activityOut).isFalse()
133             assertThat(activityContainer).isFalse()
134         }
135 
136     @Test
137     fun activity_showActivityConfigFalse_noUpdatesReceived() =
138         testScope.runTest {
139             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
140             createAndSetViewModel()
141             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
142 
143             val activityIn by collectLastValue(underTest.isActivityInViewVisible)
144             val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
145             val activityContainer by collectLastValue(underTest.isActivityContainerVisible)
146 
147             // WHEN we update the repo to have activity
148             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
149             wifiRepository.setWifiActivity(activity)
150 
151             // THEN we didn't update to the new activity (because our config is false)
152             assertThat(activityIn).isFalse()
153             assertThat(activityOut).isFalse()
154             assertThat(activityContainer).isFalse()
155         }
156 
157     @Test
158     fun activity_nullSsid_outputsFalse() =
159         testScope.runTest {
160             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
161             createAndSetViewModel()
162 
163             wifiRepository.setWifiNetwork(
164                 WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
165             )
166 
167             val activityIn by collectLastValue(underTest.isActivityInViewVisible)
168             val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
169             val activityContainer by collectLastValue(underTest.isActivityContainerVisible)
170 
171             // WHEN we update the repo to have activity
172             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
173             wifiRepository.setWifiActivity(activity)
174 
175             // THEN we still output false because our network's SSID is null
176             assertThat(activityIn).isFalse()
177             assertThat(activityOut).isFalse()
178             assertThat(activityContainer).isFalse()
179         }
180 
181     @Test
182     fun activity_allLocationViewModelsReceiveSameData() =
183         testScope.runTest {
184             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
185             createAndSetViewModel()
186             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
187 
188             val home = viewModelForLocation(underTest, StatusBarLocation.HOME)
189             val keyguard = viewModelForLocation(underTest, StatusBarLocation.KEYGUARD)
190             val qs = viewModelForLocation(underTest, StatusBarLocation.QS)
191 
192             val latestHome by collectLastValue(home.isActivityInViewVisible)
193             val latestKeyguard by collectLastValue(keyguard.isActivityInViewVisible)
194             val latestQs by collectLastValue(qs.isActivityInViewVisible)
195 
196             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
197             wifiRepository.setWifiActivity(activity)
198 
199             assertThat(latestHome).isTrue()
200             assertThat(latestKeyguard).isTrue()
201             assertThat(latestQs).isTrue()
202         }
203 
204     @Test
205     fun activityIn_hasActivityInTrue_outputsTrue() =
206         testScope.runTest {
207             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
208             createAndSetViewModel()
209             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
210 
211             val latest by collectLastValue(underTest.isActivityInViewVisible)
212 
213             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
214             wifiRepository.setWifiActivity(activity)
215 
216             assertThat(latest).isTrue()
217         }
218 
219     @Test
220     fun activityIn_hasActivityInFalse_outputsFalse() =
221         testScope.runTest {
222             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
223             createAndSetViewModel()
224             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
225 
226             val latest by collectLastValue(underTest.isActivityInViewVisible)
227 
228             val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
229             wifiRepository.setWifiActivity(activity)
230 
231             assertThat(latest).isFalse()
232         }
233 
234     @Test
235     fun activityOut_hasActivityOutTrue_outputsTrue() =
236         testScope.runTest {
237             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
238             createAndSetViewModel()
239             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
240 
241             val latest by collectLastValue(underTest.isActivityOutViewVisible)
242 
243             val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
244             wifiRepository.setWifiActivity(activity)
245 
246             assertThat(latest).isTrue()
247         }
248 
249     @Test
250     fun activityOut_hasActivityOutFalse_outputsFalse() =
251         testScope.runTest {
252             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
253             createAndSetViewModel()
254             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
255 
256             val latest by collectLastValue(underTest.isActivityOutViewVisible)
257 
258             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
259             wifiRepository.setWifiActivity(activity)
260 
261             assertThat(latest).isFalse()
262         }
263 
264     @Test
265     fun activityContainer_hasActivityInTrue_outputsTrue() =
266         testScope.runTest {
267             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
268             createAndSetViewModel()
269             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
270 
271             val latest by collectLastValue(underTest.isActivityContainerVisible)
272 
273             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
274             wifiRepository.setWifiActivity(activity)
275 
276             assertThat(latest).isTrue()
277         }
278 
279     @Test
280     fun activityContainer_hasActivityOutTrue_outputsTrue() =
281         testScope.runTest {
282             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
283             createAndSetViewModel()
284             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
285 
286             val latest by collectLastValue(underTest.isActivityContainerVisible)
287 
288             val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
289             wifiRepository.setWifiActivity(activity)
290 
291             assertThat(latest).isTrue()
292         }
293 
294     @Test
295     fun activityContainer_inAndOutTrue_outputsTrue() =
296         testScope.runTest {
297             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
298             createAndSetViewModel()
299             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
300 
301             val latest by collectLastValue(underTest.isActivityContainerVisible)
302 
303             val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
304             wifiRepository.setWifiActivity(activity)
305 
306             assertThat(latest).isTrue()
307         }
308 
309     @Test
310     fun activityContainer_inAndOutFalse_outputsFalse() =
311         testScope.runTest {
312             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
313             createAndSetViewModel()
314             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
315 
316             val latest by collectLastValue(underTest.isActivityContainerVisible)
317 
318             val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
319             wifiRepository.setWifiActivity(activity)
320 
321             assertThat(latest).isFalse()
322         }
323 
324     @Test
325     fun airplaneSpacer_notAirplaneMode_outputsFalse() =
326         testScope.runTest {
327             val latest by collectLastValue(underTest.isAirplaneSpacerVisible)
328 
329             airplaneModeRepository.setIsAirplaneMode(false)
330 
331             assertThat(latest).isFalse()
332         }
333 
334     @Test
335     fun airplaneSpacer_airplaneForceHidden_outputsFalse() =
336         testScope.runTest {
337             val latest by collectLastValue(underTest.isAirplaneSpacerVisible)
338 
339             airplaneModeRepository.setIsAirplaneMode(true)
340             connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.AIRPLANE))
341 
342             assertThat(latest).isFalse()
343         }
344 
345     @Test
346     fun airplaneSpacer_airplaneIconVisible_outputsTrue() =
347         testScope.runTest {
348             val latest by collectLastValue(underTest.isAirplaneSpacerVisible)
349 
350             airplaneModeRepository.setIsAirplaneMode(true)
351 
352             assertThat(latest).isTrue()
353         }
354 
355     @Test
356     fun signalSpacer_firstSubNotShowingNetworkTypeIcon_outputsFalse() =
357         testScope.runTest {
358             val latest by collectLastValue(underTest.isSignalSpacerVisible)
359 
360             shouldShowSignalSpacerProviderFlow.value = false
361 
362             assertThat(latest).isFalse()
363         }
364 
365     @Test
366     fun signalSpacer_firstSubIsShowingNetworkTypeIcon_outputsTrue() =
367         testScope.runTest {
368             val latest by collectLastValue(underTest.isSignalSpacerVisible)
369 
370             shouldShowSignalSpacerProviderFlow.value = true
371 
372             assertThat(latest).isTrue()
373         }
374 
375     private fun createAndSetViewModel() {
376         // [WifiViewModel] creates its flows as soon as it's instantiated, and some of those flow
377         // creations rely on certain config values that we mock out in individual tests. This method
378         // allows tests to create the view model only after those configs are correctly set up.
379         underTest =
380             WifiViewModel(
381                 airplaneModeViewModel,
382                 { shouldShowSignalSpacerProviderFlow },
383                 connectivityConstants,
384                 context,
385                 tableLogBuffer,
386                 interactor,
387                 testScope.backgroundScope,
388                 wifiConstants,
389             )
390     }
391 
392     companion object {
393         private const val NETWORK_ID = 2
394         private val ACTIVE_VALID_WIFI_NETWORK =
395             WifiNetworkModel.Active(NETWORK_ID, ssid = "AB", level = 1)
396     }
397 }
398