1 /* <lambda>null2 * 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.bluetooth.qsdialog 18 19 import android.bluetooth.BluetoothAdapter 20 import android.bluetooth.BluetoothDevice 21 import android.content.Context 22 import android.media.AudioManager 23 import android.testing.TestableLooper 24 import androidx.test.ext.junit.runners.AndroidJUnit4 25 import androidx.test.filters.SmallTest 26 import com.android.settingslib.bluetooth.CachedBluetoothDevice 27 import com.android.settingslib.bluetooth.LocalBluetoothManager 28 import com.android.systemui.SysuiTestCase 29 import com.android.systemui.coroutines.collectLastValue 30 import com.android.systemui.util.time.FakeSystemClock 31 import com.google.common.truth.Truth.assertThat 32 import kotlinx.coroutines.CoroutineDispatcher 33 import kotlinx.coroutines.test.TestScope 34 import kotlinx.coroutines.test.UnconfinedTestDispatcher 35 import kotlinx.coroutines.test.runTest 36 import org.junit.Before 37 import org.junit.Rule 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 import org.mockito.Mock 41 import org.mockito.Mockito.`when` 42 import org.mockito.junit.MockitoJUnit 43 import org.mockito.junit.MockitoRule 44 45 @SmallTest 46 @RunWith(AndroidJUnit4::class) 47 @TestableLooper.RunWithLooper(setAsMainLooper = true) 48 class DeviceItemInteractorTest : SysuiTestCase() { 49 50 @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() 51 52 @Mock private lateinit var bluetoothTileDialogRepository: BluetoothTileDialogRepository 53 54 @Mock private lateinit var cachedDevice1: CachedBluetoothDevice 55 56 @Mock private lateinit var cachedDevice2: CachedBluetoothDevice 57 58 @Mock private lateinit var device1: BluetoothDevice 59 60 @Mock private lateinit var device2: BluetoothDevice 61 62 @Mock private lateinit var deviceItem1: DeviceItem 63 64 @Mock private lateinit var deviceItem2: DeviceItem 65 66 @Mock private lateinit var audioManager: AudioManager 67 68 @Mock private lateinit var adapter: BluetoothAdapter 69 70 @Mock private lateinit var localBluetoothManager: LocalBluetoothManager 71 72 @Mock private lateinit var logger: BluetoothTileDialogLogger 73 74 private val fakeSystemClock = FakeSystemClock() 75 76 private lateinit var interactor: DeviceItemInteractor 77 78 private lateinit var dispatcher: CoroutineDispatcher 79 80 private lateinit var testScope: TestScope 81 82 @Before 83 fun setUp() { 84 dispatcher = UnconfinedTestDispatcher() 85 testScope = TestScope(dispatcher) 86 interactor = 87 DeviceItemInteractor( 88 bluetoothTileDialogRepository, 89 audioManager, 90 adapter, 91 localBluetoothManager, 92 fakeSystemClock, 93 logger, 94 testScope.backgroundScope, 95 dispatcher 96 ) 97 98 `when`(deviceItem1.cachedBluetoothDevice).thenReturn(cachedDevice1) 99 `when`(deviceItem2.cachedBluetoothDevice).thenReturn(cachedDevice2) 100 `when`(cachedDevice1.address).thenReturn("ADDRESS") 101 `when`(cachedDevice1.device).thenReturn(device1) 102 `when`(cachedDevice2.device).thenReturn(device2) 103 `when`(bluetoothTileDialogRepository.cachedDevices) 104 .thenReturn(listOf(cachedDevice1, cachedDevice2)) 105 } 106 107 @Test 108 fun testUpdateDeviceItems_noCachedDevice_returnEmpty() { 109 testScope.runTest { 110 `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(emptyList()) 111 interactor.setDeviceItemFactoryListForTesting( 112 listOf(createFactory({ true }, deviceItem1)) 113 ) 114 115 val latest by collectLastValue(interactor.deviceItemUpdate) 116 interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD) 117 118 assertThat(latest).isEqualTo(emptyList<DeviceItem>()) 119 } 120 } 121 122 @Test 123 fun testUpdateDeviceItems_hasCachedDevice_filterNotMatch_returnEmpty() { 124 testScope.runTest { 125 `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1)) 126 interactor.setDeviceItemFactoryListForTesting( 127 listOf(createFactory({ false }, deviceItem1)) 128 ) 129 130 val latest by collectLastValue(interactor.deviceItemUpdate) 131 interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD) 132 133 assertThat(latest).isEqualTo(emptyList<DeviceItem>()) 134 } 135 } 136 137 @Test 138 fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnDeviceItem() { 139 testScope.runTest { 140 `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1)) 141 interactor.setDeviceItemFactoryListForTesting( 142 listOf(createFactory({ true }, deviceItem1)) 143 ) 144 145 val latest by collectLastValue(interactor.deviceItemUpdate) 146 interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD) 147 148 assertThat(latest).isEqualTo(listOf(deviceItem1)) 149 } 150 } 151 152 @Test 153 fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnMultipleDeviceItem() { 154 testScope.runTest { 155 `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null) 156 interactor.setDeviceItemFactoryListForTesting( 157 listOf(createFactory({ false }, deviceItem1), createFactory({ true }, deviceItem2)) 158 ) 159 160 val latest by collectLastValue(interactor.deviceItemUpdate) 161 interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD) 162 163 assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem2)) 164 } 165 } 166 167 @Test 168 fun testUpdateDeviceItems_sortByDisplayPriority() { 169 testScope.runTest { 170 `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null) 171 interactor.setDeviceItemFactoryListForTesting( 172 listOf( 173 createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1), 174 createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2) 175 ) 176 ) 177 interactor.setDisplayPriorityForTesting( 178 listOf( 179 DeviceItemType.SAVED_BLUETOOTH_DEVICE, 180 DeviceItemType.CONNECTED_BLUETOOTH_DEVICE 181 ) 182 ) 183 `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) 184 `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) 185 186 val latest by collectLastValue(interactor.deviceItemUpdate) 187 interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD) 188 189 assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1)) 190 } 191 } 192 193 @Test 194 fun testUpdateDeviceItems_sameType_sortByRecentlyConnected() { 195 testScope.runTest { 196 `when`(adapter.mostRecentlyConnectedDevices).thenReturn(listOf(device2, device1)) 197 interactor.setDeviceItemFactoryListForTesting( 198 listOf( 199 createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1), 200 createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2) 201 ) 202 ) 203 interactor.setDisplayPriorityForTesting( 204 listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) 205 ) 206 `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) 207 `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) 208 209 val latest by collectLastValue(interactor.deviceItemUpdate) 210 interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD) 211 212 assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1)) 213 } 214 } 215 216 private fun createFactory( 217 isFilterMatchFunc: (CachedBluetoothDevice) -> Boolean, 218 deviceItem: DeviceItem 219 ): DeviceItemFactory { 220 return object : DeviceItemFactory() { 221 override fun isFilterMatched( 222 context: Context, 223 cachedDevice: CachedBluetoothDevice, 224 audioManager: AudioManager 225 ) = isFilterMatchFunc(cachedDevice) 226 227 override fun create(context: Context, cachedDevice: CachedBluetoothDevice) = deviceItem 228 } 229 } 230 } 231