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 
17 package com.android.systemui.bluetooth.qsdialog
18 
19 import android.testing.TestableLooper
20 import androidx.test.ext.junit.runners.AndroidJUnit4
21 import androidx.test.filters.SmallTest
22 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
23 import com.android.dx.mockito.inline.extended.StaticMockitoSession
24 import com.android.settingslib.bluetooth.BluetoothUtils
25 import com.android.settingslib.bluetooth.CachedBluetoothDevice
26 import com.android.settingslib.bluetooth.LocalBluetoothManager
27 import com.android.systemui.SysuiTestCase
28 import com.android.systemui.coroutines.collectLastValue
29 import com.android.systemui.kosmos.testScope
30 import com.android.systemui.lifecycle.activateIn
31 import com.android.systemui.res.R
32 import com.android.systemui.testKosmos
33 import com.android.systemui.util.mockito.whenever
34 import com.google.common.truth.Truth.assertThat
35 import kotlinx.coroutines.ExperimentalCoroutinesApi
36 import kotlinx.coroutines.flow.MutableSharedFlow
37 import kotlinx.coroutines.flow.MutableStateFlow
38 import kotlinx.coroutines.test.runCurrent
39 import kotlinx.coroutines.test.runTest
40 import org.junit.After
41 import org.junit.Before
42 import org.junit.Test
43 import org.junit.runner.RunWith
44 import org.mockito.Mock
45 
46 @ExperimentalCoroutinesApi
47 @SmallTest
48 @RunWith(AndroidJUnit4::class)
49 @TestableLooper.RunWithLooper(setAsMainLooper = true)
50 class AudioSharingButtonViewModelTest : SysuiTestCase() {
51     private val kosmos = testKosmos()
52     private val testScope = kosmos.testScope
53     private val bluetoothState = MutableStateFlow(false)
54     private val deviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = MutableSharedFlow()
55     @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
56     @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
57     @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
58     @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
59     @Mock private lateinit var deviceItem: DeviceItem
60     private lateinit var mockitoSession: StaticMockitoSession
61     private lateinit var audioSharingButtonViewModel: AudioSharingButtonViewModel
62 
63     @Before
setUpnull64     fun setUp() {
65         mockitoSession =
66             mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
67         whenever(bluetoothStateInteractor.bluetoothStateUpdate).thenReturn(bluetoothState)
68         whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(deviceItemUpdate)
69         audioSharingButtonViewModel =
70             AudioSharingButtonViewModel(
71                 localBluetoothManager,
72                 kosmos.audioSharingInteractor,
73                 bluetoothStateInteractor,
74                 deviceItemInteractor,
75             )
76         audioSharingButtonViewModel.activateIn(testScope)
77     }
78 
79     @After
tearDownnull80     fun tearDown() {
81         mockitoSession.finishMocking()
82     }
83 
84     @Test
testButtonStateUpdate_bluetoothOff_returnGonenull85     fun testButtonStateUpdate_bluetoothOff_returnGone() {
86         testScope.runTest {
87             val actual by
88                 collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
89             kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
90 
91             runCurrent()
92 
93             assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
94         }
95     }
96 
97     @Test
testButtonStateUpdate_noDevice_returnGonenull98     fun testButtonStateUpdate_noDevice_returnGone() {
99         testScope.runTest {
100             val actual by
101                 collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
102             kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
103             bluetoothState.value = true
104             runCurrent()
105 
106             assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
107         }
108     }
109 
110     @Test
testButtonStateUpdate_isBroadcasting_returnSharingAudionull111     fun testButtonStateUpdate_isBroadcasting_returnSharingAudio() {
112         testScope.runTest {
113             val actual by
114                 collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
115             kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
116             bluetoothState.value = true
117             runCurrent()
118             deviceItemUpdate.emit(listOf())
119             runCurrent()
120             kosmos.bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
121             runCurrent()
122 
123             assertThat(actual)
124                 .isEqualTo(
125                     AudioSharingButtonState.Visible(
126                         R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
127                         isActive = true,
128                     )
129                 )
130         }
131     }
132 
133     @Test
testButtonStateUpdate_hasSource_returnGonenull134     fun testButtonStateUpdate_hasSource_returnGone() {
135         testScope.runTest {
136             val actual by
137                 collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
138             kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
139             whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
140             whenever(
141                     BluetoothUtils.hasConnectedBroadcastSource(
142                         cachedBluetoothDevice,
143                         localBluetoothManager,
144                     )
145                 )
146                 .thenReturn(true)
147             bluetoothState.value = true
148             runCurrent()
149             deviceItemUpdate.emit(listOf(deviceItem))
150             runCurrent()
151 
152             assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
153         }
154     }
155 
156     @Test
testButtonStateUpdate_hasActiveDevice_returnAudioSharingnull157     fun testButtonStateUpdate_hasActiveDevice_returnAudioSharing() {
158         testScope.runTest {
159             val actual by
160                 collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
161             kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
162             whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
163             whenever(
164                     BluetoothUtils.hasConnectedBroadcastSource(
165                         cachedBluetoothDevice,
166                         localBluetoothManager,
167                     )
168                 )
169                 .thenReturn(false)
170             whenever(BluetoothUtils.isActiveLeAudioDevice(cachedBluetoothDevice)).thenReturn(true)
171             bluetoothState.value = true
172             runCurrent()
173             deviceItemUpdate.emit(listOf(deviceItem))
174             runCurrent()
175 
176             assertThat(actual)
177                 .isEqualTo(
178                     AudioSharingButtonState.Visible(
179                         R.string.quick_settings_bluetooth_audio_sharing_button,
180                         isActive = false,
181                     )
182                 )
183         }
184     }
185 }
186