1 /* 2 * Copyright (C) 2017 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.settings.bluetooth; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.anyString; 22 import static org.mockito.Mockito.doAnswer; 23 import static org.mockito.Mockito.doCallRealMethod; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 28 import android.bluetooth.BluetoothAdapter; 29 import android.bluetooth.BluetoothDevice; 30 import android.content.Context; 31 32 import com.android.settings.R; 33 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 34 import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener; 35 import com.android.settingslib.bluetooth.LocalBluetoothManager; 36 37 import org.junit.Before; 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 import org.mockito.Answers; 41 import org.mockito.Mock; 42 import org.mockito.MockitoAnnotations; 43 import org.robolectric.RobolectricTestRunner; 44 import org.robolectric.RuntimeEnvironment; 45 import org.robolectric.annotation.Config; 46 import org.robolectric.shadow.api.Shadow; 47 48 import java.util.HashSet; 49 import java.util.Set; 50 51 @RunWith(RobolectricTestRunner.class) 52 @Config(shadows = {ShadowBluetoothAdapter.class}) 53 public class BluetoothSummaryUpdaterTest { 54 55 private static final String DEVICE_NAME = "Nightshade"; 56 private static final String DEVICE_KEYBOARD_NAME = "Bluetooth Keyboard"; 57 58 private Context mContext; 59 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 60 private LocalBluetoothManager mBluetoothManager; 61 @Mock 62 private BluetoothDevice mConnectedDevice; 63 @Mock 64 private BluetoothDevice mConnectedKeyBoardDevice; 65 @Mock 66 private SummaryListener mListener; 67 68 // Disabled by default 69 private final boolean[] mAdapterEnabled = {false}; 70 // Not connected by default 71 private final int[] mAdapterConnectionState = {BluetoothAdapter.STATE_DISCONNECTED}; 72 // Not connected by default 73 private final boolean[] mDeviceConnected = {false, false}; 74 private final Set<BluetoothDevice> mBondedDevices = new HashSet<>(); 75 private BluetoothSummaryUpdater mSummaryUpdater; 76 private ShadowBluetoothAdapter mShadowBluetoothAdapter; 77 78 @Before setUp()79 public void setUp() { 80 MockitoAnnotations.initMocks(this); 81 mContext = RuntimeEnvironment.application.getApplicationContext(); 82 doCallRealMethod().when(mListener).onSummaryChanged(anyString()); 83 mSummaryUpdater = new BluetoothSummaryUpdater(mContext, mListener, mBluetoothManager); 84 // Setup first device 85 doReturn(DEVICE_NAME).when(mConnectedDevice).getName(); 86 doAnswer(invocation -> mDeviceConnected[0]).when(mConnectedDevice).isConnected(); 87 // Setup second device 88 doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName(); 89 doAnswer(invocation -> mDeviceConnected[1]).when(mConnectedKeyBoardDevice).isConnected(); 90 // Setup BluetoothAdapter 91 mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); 92 mShadowBluetoothAdapter.setEnabled(mAdapterEnabled[0]); 93 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 94 mShadowBluetoothAdapter.setConnectionState(mAdapterConnectionState[0]); 95 } 96 97 @Test register_true_shouldRegisterListener()98 public void register_true_shouldRegisterListener() { 99 mSummaryUpdater.register(true); 100 101 verify(mBluetoothManager.getEventManager()).registerCallback(mSummaryUpdater); 102 } 103 104 @Test register_false_shouldUnregisterListener()105 public void register_false_shouldUnregisterListener() { 106 mSummaryUpdater.register(false); 107 108 verify(mBluetoothManager.getEventManager()).unregisterCallback(mSummaryUpdater); 109 } 110 111 @Test register_true_shouldSendSummaryChange()112 public void register_true_shouldSendSummaryChange() { 113 mShadowBluetoothAdapter.setEnabled(true); 114 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 115 mBondedDevices.add(mConnectedDevice); 116 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 117 mDeviceConnected[0] = true; 118 119 mSummaryUpdater.register(true); 120 121 verify(mListener).onSummaryChanged( 122 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME)); 123 } 124 125 @Test onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary()126 public void onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary() { 127 // These states should be ignored 128 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 129 mBondedDevices.add(mConnectedDevice); 130 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 131 mDeviceConnected[0] = true; 132 133 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); 134 135 verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); 136 } 137 138 @Test onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary()139 public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() { 140 mShadowBluetoothAdapter.setEnabled(true); 141 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 142 mBondedDevices.add(mConnectedDevice); 143 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 144 mDeviceConnected[0] = true; 145 146 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); 147 148 verify(mListener).onSummaryChanged( 149 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME)); 150 } 151 152 @Test onBluetoothStateChanged_btEnabled_connectedMisMatch_shouldSendNotConnected()153 public void onBluetoothStateChanged_btEnabled_connectedMisMatch_shouldSendNotConnected() { 154 mShadowBluetoothAdapter.setEnabled(true); 155 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 156 mBondedDevices.add(mConnectedDevice); 157 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 158 // State mismatch 159 mDeviceConnected[0] = false; 160 161 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); 162 163 verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); 164 } 165 166 @Test onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage()167 public void onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage() { 168 mShadowBluetoothAdapter.setEnabled(true); 169 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTED); 170 mBondedDevices.add(mConnectedDevice); 171 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 172 // This should be ignored 173 mDeviceConnected[0] = true; 174 175 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON); 176 177 verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); 178 } 179 180 @Test onBluetoothStateChanged_ConnectedDisabledEnabled_shouldSendDisconnectedSummary()181 public void onBluetoothStateChanged_ConnectedDisabledEnabled_shouldSendDisconnectedSummary() { 182 mShadowBluetoothAdapter.setEnabled(true); 183 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTED); 184 mBondedDevices.add(mConnectedDevice); 185 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 186 mDeviceConnected[0] = false; 187 188 mSummaryUpdater.register(true); 189 verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); 190 191 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 192 mDeviceConnected[0] = true; 193 mSummaryUpdater.onConnectionStateChanged(null /* device */, 194 BluetoothAdapter.STATE_CONNECTED); 195 verify(mListener).onSummaryChanged( 196 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME)); 197 198 mShadowBluetoothAdapter.setEnabled(false); 199 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); 200 verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); 201 202 // Turning ON means not enabled 203 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON); 204 // There should still be only one invocation of disabled message 205 verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); 206 207 mShadowBluetoothAdapter.setEnabled(true); 208 mDeviceConnected[0] = false; 209 mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); 210 verify(mListener, times(2)).onSummaryChanged(mContext.getString(R.string.disconnected)); 211 verify(mListener, times(4)).onSummaryChanged(anyString()); 212 } 213 214 @Test onConnectionStateChanged_connected_shouldSendConnectedMessage()215 public void onConnectionStateChanged_connected_shouldSendConnectedMessage() { 216 mShadowBluetoothAdapter.setEnabled(true); 217 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 218 mBondedDevices.add(mConnectedDevice); 219 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 220 mDeviceConnected[0] = true; 221 222 mSummaryUpdater.onConnectionStateChanged(null /* device */, 223 BluetoothAdapter.STATE_CONNECTED); 224 225 verify(mListener).onSummaryChanged( 226 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME)); 227 } 228 229 @Test onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage()230 public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() { 231 mShadowBluetoothAdapter.setEnabled(true); 232 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTED); 233 mBondedDevices.add(mConnectedDevice); 234 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 235 mDeviceConnected[0] = false; 236 237 mSummaryUpdater.onConnectionStateChanged(null /* device */, 238 BluetoothAdapter.STATE_CONNECTED); 239 240 verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); 241 } 242 243 @Test onConnectionStateChanged_noBondedDevice_shouldSendDisconnectedMessage()244 public void onConnectionStateChanged_noBondedDevice_shouldSendDisconnectedMessage() { 245 mShadowBluetoothAdapter.setEnabled(true); 246 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED); 247 248 mSummaryUpdater.onConnectionStateChanged(null /* device */, 249 BluetoothAdapter.STATE_CONNECTED); 250 251 verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected)); 252 } 253 254 @Test onConnectionStateChanged_connecting_shouldSendConnectingMessage()255 public void onConnectionStateChanged_connecting_shouldSendConnectingMessage() { 256 // No need for bonded devices 257 mShadowBluetoothAdapter.setEnabled(true); 258 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTING); 259 260 mSummaryUpdater.onConnectionStateChanged(null /* device */, 261 BluetoothAdapter.STATE_CONNECTING); 262 263 verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connecting)); 264 } 265 266 @Test onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage()267 public void onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage() { 268 // No need for bonded devices 269 mShadowBluetoothAdapter.setEnabled(true); 270 mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTING); 271 272 mSummaryUpdater.onConnectionStateChanged(null /* device */, 273 BluetoothAdapter.STATE_DISCONNECTING); 274 275 verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnecting)); 276 } 277 278 @Test getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary()279 public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() { 280 mBondedDevices.add(mConnectedDevice); 281 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 282 mDeviceConnected[0] = true; 283 final String expectedSummary = 284 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME); 285 286 assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary); 287 } 288 289 @Test getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary()290 public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() { 291 mBondedDevices.add(mConnectedDevice); 292 mBondedDevices.add(mConnectedKeyBoardDevice); 293 mShadowBluetoothAdapter.setBondedDevices(mBondedDevices); 294 295 mDeviceConnected[0] = true; 296 mDeviceConnected[1] = true; 297 final String expectedSummary = 298 mContext.getString(R.string.bluetooth_connected_multiple_devices_summary); 299 300 assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary); 301 } 302 303 private class SummaryListener implements OnSummaryChangeListener { 304 String summary; 305 306 @Override onSummaryChanged(String summary)307 public void onSummaryChanged(String summary) { 308 this.summary = summary; 309 } 310 } 311 } 312