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.never;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 
26 import android.bluetooth.BluetoothAdapter;
27 import android.bluetooth.BluetoothDevice;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.graphics.drawable.Drawable;
31 import android.util.Pair;
32 
33 import androidx.preference.Preference;
34 
35 import com.android.settings.SettingsActivity;
36 import com.android.settings.connecteddevice.DevicePreferenceCallback;
37 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
38 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
39 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
40 import com.android.settingslib.bluetooth.LocalBluetoothManager;
41 
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.mockito.ArgumentCaptor;
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.shadow.api.Shadow;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 
56 @RunWith(RobolectricTestRunner.class)
57 @Config(shadows = {ShadowBluetoothAdapter.class})
58 public class BluetoothDeviceUpdaterTest {
59 
60     private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
61     private static final String SUB_MAC_ADDRESS = "05:52:C7:0B:D8:3C";
62     private static final String TEST_NAME = "test_name";
63 
64     @Mock
65     private DevicePreferenceCallback mDevicePreferenceCallback;
66     @Mock
67     private CachedBluetoothDevice mCachedBluetoothDevice;
68     @Mock
69     private CachedBluetoothDevice mSubCachedBluetoothDevice;
70     @Mock
71     private BluetoothDevice mBluetoothDevice;
72     @Mock
73     private BluetoothDevice mSubBluetoothDevice;
74     @Mock
75     private SettingsActivity mSettingsActivity;
76     @Mock
77     private LocalBluetoothManager mLocalManager;
78     @Mock
79     private CachedBluetoothDeviceManager mCachedDeviceManager;
80     @Mock
81     private Drawable mDrawable;
82 
83     private Context mContext;
84     private TestBluetoothDeviceUpdater mBluetoothDeviceUpdater;
85     private BluetoothDevicePreference mPreference;
86     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
87     private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
88 
89     @Before
setUp()90     public void setUp() {
91         MockitoAnnotations.initMocks(this);
92 
93         Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
94         mContext = RuntimeEnvironment.application;
95         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
96         mCachedDevices.add(mCachedBluetoothDevice);
97         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
98         when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
99         when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
100         when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
101         when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
102         when(mSubBluetoothDevice.getAddress()).thenReturn(SUB_MAC_ADDRESS);
103         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
104 
105         mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
106                 /* showDeviceWithoutNames= */ false,
107                 BluetoothDevicePreference.SortType.TYPE_DEFAULT);
108         mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mContext,
109                 mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */  0);
110         mBluetoothDeviceUpdater.setPrefContext(mContext);
111     }
112 
113     @Test
testAddPreference_deviceExist_doNothing()114     public void testAddPreference_deviceExist_doNothing() {
115         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
116 
117         mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice);
118 
119         verify(mDevicePreferenceCallback, never()).onDeviceAdded(any(Preference.class));
120     }
121 
122     @Test
testAddPreference_deviceNotExist_addPreference()123     public void testAddPreference_deviceNotExist_addPreference() {
124         mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice);
125 
126         final Preference preference = mBluetoothDeviceUpdater.mPreferenceMap.get(mBluetoothDevice);
127         assertThat(preference).isNotNull();
128         verify(mDevicePreferenceCallback).onDeviceAdded(preference);
129     }
130 
131     @Test
testRemovePreference_deviceExist_removePreference()132     public void testRemovePreference_deviceExist_removePreference() {
133         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
134 
135         mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice);
136 
137         verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
138         assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
139     }
140 
141     @Test
testOnDeviceDeleted_deviceExists_removePreference()142     public void testOnDeviceDeleted_deviceExists_removePreference() {
143         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
144 
145         mBluetoothDeviceUpdater.onDeviceDeleted(mCachedBluetoothDevice);
146 
147         verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
148         assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
149     }
150 
151     @Test
testRemovePreference_deviceNotExist_doNothing()152     public void testRemovePreference_deviceNotExist_doNothing() {
153         mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice);
154 
155         verify(mDevicePreferenceCallback, never()).onDeviceRemoved(any(Preference.class));
156     }
157 
158     @Test
testRemovePreference_subDeviceExist_removePreference()159     public void testRemovePreference_subDeviceExist_removePreference() {
160         when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
161         mBluetoothDeviceUpdater.mPreferenceMap.put(mSubBluetoothDevice, mPreference);
162 
163         assertThat(mBluetoothDeviceUpdater.mPreferenceMap.
164                 containsKey(mSubBluetoothDevice)).isTrue();
165         mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice);
166 
167         verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
168         assertThat(mBluetoothDeviceUpdater.mPreferenceMap.
169                 containsKey(mSubBluetoothDevice)).isFalse();
170     }
171 
172     @Test
testDeviceProfilesListener_click_startBluetoothDeviceDetailPage()173     public void testDeviceProfilesListener_click_startBluetoothDeviceDetailPage() {
174         mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mSettingsActivity,
175                 mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */  0);
176 
177         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
178         mBluetoothDeviceUpdater.mDeviceProfilesListener.onGearClick(mPreference);
179 
180         verify(mSettingsActivity).startActivity(intentCaptor.capture());
181         assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
182                 .isEqualTo(BluetoothDeviceDetailsFragment.class.getName());
183     }
184 
185     @Test
isDeviceConnected_deviceConnected()186     public void isDeviceConnected_deviceConnected() {
187         doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
188         doReturn(true).when(mBluetoothDevice).isConnected();
189 
190         assertThat(mBluetoothDeviceUpdater.isDeviceConnected(mCachedBluetoothDevice)).isTrue();
191     }
192 
193     @Test
isDeviceConnected_deviceNotConnected()194     public void isDeviceConnected_deviceNotConnected() {
195         doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
196         doReturn(false).when(mBluetoothDevice).isConnected();
197 
198         assertThat(mBluetoothDeviceUpdater.isDeviceConnected(mCachedBluetoothDevice)).isFalse();
199     }
200 
201     @Test
registerCallback_localBluetoothManagerNull_shouldNotCrash()202     public void registerCallback_localBluetoothManagerNull_shouldNotCrash() {
203         mBluetoothDeviceUpdater.mLocalManager = null;
204 
205         // Shouldn't crash
206         mBluetoothDeviceUpdater.registerCallback();
207     }
208 
209     @Test
unregisterCallback_localBluetoothManagerNull_shouldNotCrash()210     public void unregisterCallback_localBluetoothManagerNull_shouldNotCrash() {
211         mBluetoothDeviceUpdater.mLocalManager = null;
212 
213         // Shouldn't crash
214         mBluetoothDeviceUpdater.unregisterCallback();
215     }
216 
217     @Test
forceUpdate_bluetoothDisabled_removeAllDevicesFromPreference()218     public void forceUpdate_bluetoothDisabled_removeAllDevicesFromPreference() {
219         mShadowBluetoothAdapter.setEnabled(false);
220         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
221 
222         mBluetoothDeviceUpdater.forceUpdate();
223 
224         verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
225         assertThat(mBluetoothDeviceUpdater.mPreferenceMap).isEmpty();
226     }
227 
228     @Test
forceUpdate_bluetoothEnabled_addPreference()229     public void forceUpdate_bluetoothEnabled_addPreference() {
230         mShadowBluetoothAdapter.setEnabled(true);
231         mBluetoothDeviceUpdater.forceUpdate();
232 
233         verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
234     }
235 
236     @Test
onBluetoothStateChanged_bluetoothStateIsOn_forceUpdate()237     public void onBluetoothStateChanged_bluetoothStateIsOn_forceUpdate() {
238         mShadowBluetoothAdapter.setEnabled(true);
239         mBluetoothDeviceUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
240 
241         verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
242     }
243 
244     @Test
onBluetoothStateChanged_bluetoothStateIsOff_removeAllDevicesFromPreference()245     public void onBluetoothStateChanged_bluetoothStateIsOff_removeAllDevicesFromPreference() {
246         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
247 
248         mBluetoothDeviceUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
249 
250         verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
251         assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
252     }
253 
254     @Test
havePreference_refreshPreference()255     public void havePreference_refreshPreference() {
256         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
257         mPreference.setTitle("fake_name");
258 
259         when(mCachedBluetoothDevice.getName()).thenReturn(TEST_NAME);
260         mBluetoothDeviceUpdater.refreshPreference();
261 
262         assertThat(mPreference.getTitle()).isEqualTo(TEST_NAME);
263     }
264 
265     public static class TestBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
TestBluetoothDeviceUpdater(Context context, DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager, int metricsCategory)266         public TestBluetoothDeviceUpdater(Context context,
267                 DevicePreferenceCallback devicePreferenceCallback,
268                 LocalBluetoothManager localManager, int metricsCategory) {
269             super(context, devicePreferenceCallback, localManager, metricsCategory);
270         }
271 
272         @Override
isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice)273         public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
274             return true;
275         }
276 
277         @Override
getPreferenceKey()278         protected String getPreferenceKey() {
279             return "test_bt";
280         }
281     }
282 }
283