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 package com.android.settings.bluetooth; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.Mockito.doReturn; 22 import static org.mockito.Mockito.mock; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.bluetooth.BluetoothDevice; 30 import android.content.Context; 31 import android.graphics.drawable.Drawable; 32 import android.os.UserManager; 33 import android.util.Pair; 34 import android.view.ContextThemeWrapper; 35 36 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 37 import com.android.settings.R; 38 import com.android.settings.testutils.FakeFeatureFactory; 39 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; 40 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 41 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 42 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 import org.robolectric.RobolectricTestRunner; 49 import org.robolectric.RuntimeEnvironment; 50 import org.robolectric.annotation.Config; 51 import org.robolectric.util.ReflectionHelpers; 52 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.Comparator; 56 import java.util.List; 57 58 @RunWith(RobolectricTestRunner.class) 59 @Config(shadows = {ShadowAlertDialogCompat.class}) 60 public class BluetoothDevicePreferenceTest { 61 private static final boolean SHOW_DEVICES_WITHOUT_NAMES = true; 62 private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; 63 private static final String MAC_ADDRESS_2 = "05:52:C7:0B:D8:3C"; 64 private static final String MAC_ADDRESS_3 = "06:52:C7:0B:D8:3C"; 65 private static final String MAC_ADDRESS_4 = "07:52:C7:0B:D8:3C"; 66 private static final Comparator<BluetoothDevicePreference> COMPARATOR = 67 Comparator.naturalOrder(); 68 private static final String FAKE_DESCRIPTION = "fake_description"; 69 70 private Context mContext; 71 @Mock 72 private CachedBluetoothDevice mCachedBluetoothDevice; 73 @Mock 74 private CachedBluetoothDevice mCachedDevice1; 75 @Mock 76 private CachedBluetoothDevice mCachedDevice2; 77 @Mock 78 private CachedBluetoothDevice mCachedDevice3; 79 @Mock 80 private Drawable mDrawable; 81 82 private FakeFeatureFactory mFakeFeatureFactory; 83 private MetricsFeatureProvider mMetricsFeatureProvider; 84 private BluetoothDevicePreference mPreference; 85 private List<BluetoothDevicePreference> mPreferenceList = new ArrayList<>(); 86 87 @Before setUp()88 public void setUp() { 89 MockitoAnnotations.initMocks(this); 90 Context context = spy(RuntimeEnvironment.application.getApplicationContext()); 91 mContext = new ContextThemeWrapper(context, R.style.Theme_Settings); 92 mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); 93 mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider(); 94 when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS); 95 when(mCachedBluetoothDevice.getDrawableWithDescription()) 96 .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); 97 when(mCachedDevice1.getAddress()).thenReturn(MAC_ADDRESS_2); 98 when(mCachedDevice1.getDrawableWithDescription()) 99 .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); 100 when(mCachedDevice2.getAddress()).thenReturn(MAC_ADDRESS_3); 101 when(mCachedDevice2.getDrawableWithDescription()) 102 .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); 103 when(mCachedDevice3.getAddress()).thenReturn(MAC_ADDRESS_4); 104 when(mCachedDevice3.getDrawableWithDescription()) 105 .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION)); 106 mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, 107 SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT); 108 } 109 110 @Test onClicked_deviceConnected_shouldLogBluetoothDisconnectEvent()111 public void onClicked_deviceConnected_shouldLogBluetoothDisconnectEvent() { 112 when(mCachedBluetoothDevice.isConnected()).thenReturn(true); 113 114 mPreference.onClicked(); 115 116 verify(mMetricsFeatureProvider) 117 .action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT); 118 } 119 120 @Test onClicked_deviceBonded_shouldLogBluetoothConnectEvent()121 public void onClicked_deviceBonded_shouldLogBluetoothConnectEvent() { 122 when(mCachedBluetoothDevice.isConnected()).thenReturn(false); 123 when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); 124 125 mPreference.onClicked(); 126 127 verify(mMetricsFeatureProvider) 128 .action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT); 129 } 130 131 @Test onClicked_deviceNotBonded_shouldLogBluetoothPairEvent()132 public void onClicked_deviceNotBonded_shouldLogBluetoothPairEvent() { 133 when(mCachedBluetoothDevice.isConnected()).thenReturn(false); 134 when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); 135 when(mCachedBluetoothDevice.startPairing()).thenReturn(true); 136 when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(true); 137 138 mPreference.onClicked(); 139 140 verify(mMetricsFeatureProvider) 141 .action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); 142 verify(mMetricsFeatureProvider, never()) 143 .action(mContext, 144 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); 145 } 146 147 @Test onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent()148 public void onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent() { 149 when(mCachedBluetoothDevice.isConnected()).thenReturn(false); 150 when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); 151 when(mCachedBluetoothDevice.startPairing()).thenReturn(true); 152 when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(false); 153 154 mPreference.onClicked(); 155 156 verify(mMetricsFeatureProvider) 157 .action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); 158 verify(mMetricsFeatureProvider) 159 .action(mContext, 160 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); 161 } 162 163 @Test getSecondTargetResource_shouldBeGearIconLayout()164 public void getSecondTargetResource_shouldBeGearIconLayout() { 165 assertThat(mPreference.getSecondTargetResId()).isEqualTo(R.layout.preference_widget_gear); 166 } 167 168 @Test shouldHideSecondTarget_noDevice_shouldReturnTrue()169 public void shouldHideSecondTarget_noDevice_shouldReturnTrue() { 170 ReflectionHelpers.setField(mPreference, "mCachedDevice", null); 171 172 assertThat(mPreference.shouldHideSecondTarget()).isTrue(); 173 } 174 175 @Test shouldHideSecondTarget_notBond_shouldReturnTrue()176 public void shouldHideSecondTarget_notBond_shouldReturnTrue() { 177 when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); 178 179 assertThat(mPreference.shouldHideSecondTarget()).isTrue(); 180 } 181 182 @Test shouldHideSecondTarget_hasUserRestriction_shouldReturnTrue()183 public void shouldHideSecondTarget_hasUserRestriction_shouldReturnTrue() { 184 final UserManager um = mock(UserManager.class); 185 ReflectionHelpers.setField(mPreference, "mUserManager", um); 186 when(um.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(true); 187 188 assertThat(mPreference.shouldHideSecondTarget()).isTrue(); 189 } 190 191 @Test shouldHideSecondTarget_hasBoundDeviceAndNoRestriction_shouldReturnFalse()192 public void shouldHideSecondTarget_hasBoundDeviceAndNoRestriction_shouldReturnFalse() { 193 when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); 194 final UserManager um = mock(UserManager.class); 195 ReflectionHelpers.setField(mPreference, "mUserManager", um); 196 when(um.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(false); 197 198 assertThat(mPreference.shouldHideSecondTarget()).isFalse(); 199 } 200 201 @Test isVisible_showDeviceWithoutNames_visible()202 public void isVisible_showDeviceWithoutNames_visible() { 203 doReturn(false).when(mCachedBluetoothDevice).hasHumanReadableName(); 204 BluetoothDevicePreference preference = 205 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, 206 SHOW_DEVICES_WITHOUT_NAMES, 207 BluetoothDevicePreference.SortType.TYPE_DEFAULT); 208 209 assertThat(preference.isVisible()).isTrue(); 210 } 211 212 @Test isVisible_hideDeviceWithoutNames_invisible()213 public void isVisible_hideDeviceWithoutNames_invisible() { 214 doReturn(false).when(mCachedBluetoothDevice).hasHumanReadableName(); 215 BluetoothDevicePreference preference = 216 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, 217 false, BluetoothDevicePreference.SortType.TYPE_DEFAULT); 218 219 assertThat(preference.isVisible()).isFalse(); 220 } 221 222 @Test setNeedNotifyHierarchyChanged_updateValue()223 public void setNeedNotifyHierarchyChanged_updateValue() { 224 mPreference.setNeedNotifyHierarchyChanged(true); 225 226 assertThat(mPreference.mNeedNotifyHierarchyChanged).isTrue(); 227 } 228 229 @Test compareTo_sortTypeFIFO()230 public void compareTo_sortTypeFIFO() { 231 final BluetoothDevicePreference preference3 = new BluetoothDevicePreference(mContext, 232 mCachedDevice3, SHOW_DEVICES_WITHOUT_NAMES, 233 BluetoothDevicePreference.SortType.TYPE_FIFO); 234 final BluetoothDevicePreference preference2 = new BluetoothDevicePreference(mContext, 235 mCachedDevice2, SHOW_DEVICES_WITHOUT_NAMES, 236 BluetoothDevicePreference.SortType.TYPE_FIFO); 237 final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(mContext, 238 mCachedDevice1, SHOW_DEVICES_WITHOUT_NAMES, 239 BluetoothDevicePreference.SortType.TYPE_FIFO); 240 241 mPreferenceList.add(preference1); 242 mPreferenceList.add(preference2); 243 mPreferenceList.add(preference3); 244 Collections.sort(mPreferenceList, COMPARATOR); 245 246 assertThat(mPreferenceList.get(0).getCachedDevice().getAddress()) 247 .isEqualTo(preference3.getCachedDevice().getAddress()); 248 assertThat(mPreferenceList.get(1).getCachedDevice().getAddress()) 249 .isEqualTo(preference2.getCachedDevice().getAddress()); 250 assertThat(mPreferenceList.get(2).getCachedDevice().getAddress()) 251 .isEqualTo(preference1.getCachedDevice().getAddress()); 252 } 253 254 @Test compareTo_sortTypeDefault()255 public void compareTo_sortTypeDefault() { 256 final BluetoothDevicePreference preference3 = new BluetoothDevicePreference(mContext, 257 mCachedDevice3, SHOW_DEVICES_WITHOUT_NAMES, 258 BluetoothDevicePreference.SortType.TYPE_DEFAULT); 259 final BluetoothDevicePreference preference2 = new BluetoothDevicePreference(mContext, 260 mCachedDevice2, SHOW_DEVICES_WITHOUT_NAMES, 261 BluetoothDevicePreference.SortType.TYPE_DEFAULT); 262 final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(mContext, 263 mCachedDevice1, SHOW_DEVICES_WITHOUT_NAMES, 264 BluetoothDevicePreference.SortType.TYPE_DEFAULT); 265 266 mPreferenceList.add(preference1); 267 mPreferenceList.add(preference2); 268 mPreferenceList.add(preference3); 269 Collections.sort(mPreferenceList, COMPARATOR); 270 271 assertThat(mPreferenceList.get(0).getCachedDevice().getAddress()) 272 .isEqualTo(preference1.getCachedDevice().getAddress()); 273 assertThat(mPreferenceList.get(1).getCachedDevice().getAddress()) 274 .isEqualTo(preference2.getCachedDevice().getAddress()); 275 assertThat(mPreferenceList.get(2).getCachedDevice().getAddress()) 276 .isEqualTo(preference3.getCachedDevice().getAddress()); 277 } 278 279 @Test onAttached_callbackNotRemoved_doNotRegisterCallback()280 public void onAttached_callbackNotRemoved_doNotRegisterCallback() { 281 mPreference.onAttached(); 282 283 verify(mCachedBluetoothDevice, never()).unregisterCallback(any()); 284 } 285 286 @Test onAttached_callbackRemoved_registerCallback()287 public void onAttached_callbackRemoved_registerCallback() { 288 mPreference.onPrepareForRemoval(); 289 mPreference.onAttached(); 290 291 verify(mCachedBluetoothDevice, times(2)).registerCallback(any()); 292 } 293 } 294