1 /*
2  * 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.settings.bluetooth;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.Mockito.doNothing;
23 import static org.mockito.Mockito.doReturn;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.bluetooth.BluetoothAdapter;
30 import android.bluetooth.BluetoothDevice;
31 import android.bluetooth.BluetoothProfile;
32 import android.content.Context;
33 import android.content.res.Resources;
34 import android.graphics.drawable.Drawable;
35 import android.util.Pair;
36 
37 import androidx.test.core.app.ApplicationProvider;
38 
39 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
40 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
41 import com.android.settingslib.bluetooth.LocalBluetoothManager;
42 
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Answers;
48 import org.mockito.Mock;
49 import org.mockito.junit.MockitoJUnit;
50 import org.mockito.junit.MockitoRule;
51 import org.robolectric.RobolectricTestRunner;
52 import org.robolectric.annotation.Config;
53 import org.robolectric.shadow.api.Shadow;
54 
55 /** Tests for {@link BluetoothDevicePairingDetailBase}. */
56 @RunWith(RobolectricTestRunner.class)
57 @Config(shadows = {
58         ShadowBluetoothAdapter.class,
59         com.android.settings.testutils.shadow.ShadowFragment.class,
60 })
61 public class BluetoothDevicePairingDetailBaseTest {
62 
63     @Rule
64     public final MockitoRule mockito = MockitoJUnit.rule();
65 
66     public static final String KEY_DEVICE_LIST_GROUP = "test_key";
67 
68     private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
69     private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1";
70     private final Context mContext = ApplicationProvider.getApplicationContext();
71 
72     @Mock
73     private Resources mResource;
74     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
75     private LocalBluetoothManager mLocalManager;
76     @Mock
77     private CachedBluetoothDevice mCachedBluetoothDevice;
78     @Mock
79     private Drawable mDrawable;
80     private BluetoothAdapter mBluetoothAdapter;
81     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
82     private BluetoothProgressCategory mAvailableDevicesCategory;
83     private BluetoothDevice mBluetoothDevice;
84     private TestBluetoothDevicePairingDetailBase mFragment;
85 
86     @Before
setUp()87     public void setUp() {
88         mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
89         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
90         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
91         when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
92         final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
93         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
94         mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
95 
96         mFragment = spy(new TestBluetoothDevicePairingDetailBase());
97         when(mFragment.findPreference(KEY_DEVICE_LIST_GROUP)).thenReturn(mAvailableDevicesCategory);
98         doReturn(mContext).when(mFragment).getContext();
99         doReturn(mResource).when(mFragment).getResources();
100         mFragment.mDeviceListGroup = mAvailableDevicesCategory;
101         mFragment.mLocalManager = mLocalManager;
102         mFragment.mBluetoothAdapter = mBluetoothAdapter;
103         mFragment.initPreferencesFromPreferenceScreen();
104 
105     }
106 
107     @Test
startScanning_startScanAndRemoveDevices()108     public void startScanning_startScanAndRemoveDevices() {
109         mFragment.enableScanning();
110 
111         verify(mFragment).startScanning();
112         verify(mAvailableDevicesCategory).removeAll();
113     }
114 
115     @Test
updateContent_stateOn()116     public void updateContent_stateOn() {
117         mFragment.updateContent(BluetoothAdapter.STATE_ON);
118 
119         assertThat(mBluetoothAdapter.isEnabled()).isTrue();
120         verify(mFragment).enableScanning();
121     }
122 
123     @Test
updateContent_stateOff_finish()124     public void updateContent_stateOff_finish() {
125         mFragment.updateContent(BluetoothAdapter.STATE_OFF);
126 
127         verify(mFragment).finish();
128     }
129 
130     @Test
updateBluetooth_bluetoothOff_turnOnBluetooth()131     public void updateBluetooth_bluetoothOff_turnOnBluetooth() {
132         mShadowBluetoothAdapter.setEnabled(false);
133 
134         mFragment.updateBluetooth();
135 
136         assertThat(mBluetoothAdapter.isEnabled()).isTrue();
137     }
138 
139     @Test
updateBluetooth_bluetoothOn_updateState()140     public void updateBluetooth_bluetoothOn_updateState() {
141         mShadowBluetoothAdapter.setEnabled(true);
142         doNothing().when(mFragment).updateContent(anyInt());
143 
144         mFragment.updateBluetooth();
145 
146         verify(mFragment).updateContent(anyInt());
147     }
148 
149     @Test
onBluetoothStateChanged_whenTurnedOnBTShowToast()150     public void onBluetoothStateChanged_whenTurnedOnBTShowToast() {
151         doNothing().when(mFragment).updateContent(anyInt());
152 
153         mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
154 
155         verify(mFragment).showBluetoothTurnedOnToast();
156     }
157 
158     @Test
onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish()159     public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
160         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
161         mFragment.mSelectedList.add(mBluetoothDevice);
162         mFragment.mSelectedList.add(device);
163 
164         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
165         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
166 
167         mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
168                 BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
169 
170         verify(mFragment).finish();
171     }
172 
173     @Test
onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing()174     public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
175         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
176         mFragment.mSelectedList.add(device);
177 
178         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
179         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
180 
181         mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
182                 BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
183 
184         // not crash
185     }
186 
187     @Test
onProfileConnectionStateChanged_deviceDisconnected_doNothing()188     public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
189         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
190         mFragment.mSelectedList.add(mBluetoothDevice);
191         mFragment.mSelectedList.add(device);
192 
193         when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
194         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
195 
196         mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
197                 BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
198 
199         // not crash
200     }
201 
202     @Test
onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed()203     public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
204         final BluetoothDevicePreference preference =
205                 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
206                         true, BluetoothDevicePreference.SortType.TYPE_FIFO);
207         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
208         mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
209 
210         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
211         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
212 
213         mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
214                 BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
215 
216         assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0);
217     }
218 
219     @Test
onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing()220     public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
221         final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
222         final BluetoothDevicePreference preference =
223                 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
224                         true, BluetoothDevicePreference.SortType.TYPE_FIFO);
225         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
226         final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
227         mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
228 
229         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
230         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
231         when(cachedDevice.isConnected()).thenReturn(true);
232         when(cachedDevice.getDevice()).thenReturn(device2);
233         when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
234         when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
235 
236         mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
237                 BluetoothAdapter.STATE_CONNECTED);
238 
239         // not crash
240     }
241 
242     private static class TestBluetoothDevicePairingDetailBase extends
243             BluetoothDevicePairingDetailBase {
244 
TestBluetoothDevicePairingDetailBase()245         TestBluetoothDevicePairingDetailBase() {
246             super();
247         }
248 
249         @Override
getMetricsCategory()250         public int getMetricsCategory() {
251             return 0;
252         }
253 
254         @Override
getDeviceListKey()255         public String getDeviceListKey() {
256             return KEY_DEVICE_LIST_GROUP;
257         }
258 
259         @Override
getPreferenceScreenResId()260         protected int getPreferenceScreenResId() {
261             return 0;
262         }
263 
264         @Override
getLogTag()265         protected String getLogTag() {
266             return "test_tag";
267         }
268     }
269 }
270