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