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 android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.content.Context;
24 
25 import com.android.settings.R;
26 import com.android.settings.SettingsRobolectricTestRunner;
27 import com.android.settings.TestConfig;
28 import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
29 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
30 import com.android.settingslib.bluetooth.LocalBluetoothManager;
31 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
32 
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.mockito.Answers;
37 import org.mockito.Mock;
38 import org.mockito.MockitoAnnotations;
39 import org.robolectric.RuntimeEnvironment;
40 import org.robolectric.annotation.Config;
41 
42 import java.util.ArrayList;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Set;
46 
47 import static org.mockito.Mockito.doReturn;
48 import static org.mockito.Mockito.mock;
49 import static org.mockito.Mockito.spy;
50 import static org.mockito.Mockito.verify;
51 import static org.mockito.Mockito.when;
52 
53 @RunWith(SettingsRobolectricTestRunner.class)
54 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
55 public class BluetoothSummaryUpdaterTest {
56     private static final String DEVICE_NAME = "Nightshade";
57     private static final String DEVICE_KEYBOARD_NAME = "Bluetooth Keyboard";
58 
59     private Context mContext;
60     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
61     private LocalBluetoothManager mBluetoothManager;
62     @Mock
63     private LocalBluetoothAdapter mBtAdapter;
64     @Mock
65     private BluetoothDevice mConnectedDevice;
66     @Mock
67     private BluetoothDevice mConnectedKeyBoardDevice;
68     @Mock
69     private SummaryListener mListener;
70 
71     private BluetoothSummaryUpdater mSummaryUpdater;
72 
73     @Before
setUp()74     public void setUp() {
75         MockitoAnnotations.initMocks(this);
76         when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBtAdapter);
77         when(mBtAdapter.isEnabled()).thenReturn(true);
78         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
79         mContext = RuntimeEnvironment.application.getApplicationContext();
80         mSummaryUpdater = new BluetoothSummaryUpdater(mContext, mListener, mBluetoothManager);
81     }
82 
83     @Test
register_true_shouldRegisterListener()84     public void register_true_shouldRegisterListener() {
85         mSummaryUpdater.register(true);
86 
87         verify(mBluetoothManager.getEventManager()).registerCallback(mSummaryUpdater);
88     }
89 
90     @Test
register_false_shouldUnregisterListener()91     public void register_false_shouldUnregisterListener() {
92         mSummaryUpdater.register(false);
93 
94         verify(mBluetoothManager.getEventManager()).unregisterCallback(mSummaryUpdater);
95     }
96 
97     @Test
register_true_shouldSendSummaryChange()98     public void register_true_shouldSendSummaryChange() {
99         prepareConnectedDevice(false);
100 
101         mSummaryUpdater.register(true);
102 
103         verify(mListener).onSummaryChanged(
104                 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
105     }
106 
107     @Test
onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary()108     public void onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary() {
109         mSummaryUpdater.register(true);
110         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
111 
112         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
113     }
114 
115     @Test
onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary()116     public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() {
117         prepareConnectedDevice(false);
118 
119         mSummaryUpdater.register(true);
120         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
121 
122         verify(mListener).onSummaryChanged(
123                 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
124     }
125 
126     @Test
onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage()127     public void onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage() {
128         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
129         mSummaryUpdater.register(true);
130         mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
131 
132         verify(mListener).onSummaryChanged(
133                 mContext.getString(R.string.disconnected));
134     }
135 
136     @Test
onConnectionStateChanged_connected_shouldSendConnectedMessage()137     public void onConnectionStateChanged_connected_shouldSendConnectedMessage() {
138         final List<CachedBluetoothDevice> devices = new ArrayList<>();
139         devices.add(mock(CachedBluetoothDevice.class));
140         when(devices.get(0).isConnected()).thenReturn(true);
141         when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy())
142                 .thenReturn(devices);
143         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
144         prepareConnectedDevice(false);
145 
146         mSummaryUpdater.register(true);
147 
148         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED);
149         mSummaryUpdater.onConnectionStateChanged(null /* device */,
150                 BluetoothAdapter.STATE_CONNECTED);
151 
152         verify(mListener).onSummaryChanged(
153                 mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
154     }
155 
156     @Test
onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage()157     public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() {
158         mSummaryUpdater.register(true);
159         mSummaryUpdater.onConnectionStateChanged(null /* device */,
160                 BluetoothAdapter.STATE_CONNECTED);
161 
162         verify(mListener).onSummaryChanged(
163                 mContext.getString(R.string.disconnected));
164     }
165 
166     @Test
onConnectionStateChanged_connecting_shouldSendConnectingMessage()167     public void onConnectionStateChanged_connecting_shouldSendConnectingMessage() {
168         mSummaryUpdater.register(true);
169         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
170         mSummaryUpdater.onConnectionStateChanged(null /* device */,
171                 BluetoothAdapter.STATE_CONNECTING);
172 
173         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connecting));
174     }
175 
176     @Test
onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage()177     public void onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage() {
178         mSummaryUpdater.register(true);
179         when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTING);
180         mSummaryUpdater.onConnectionStateChanged(null /* device */,
181                 BluetoothAdapter.STATE_DISCONNECTING);
182 
183         verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnecting));
184     }
185 
186     @Test
getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary()187     public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() {
188         prepareConnectedDevice(false);
189         final String expectedSummary = mContext.getString(R.string.bluetooth_connected_summary,
190                 DEVICE_NAME);
191 
192         assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
193     }
194 
195     @Test
getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary()196     public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() {
197         prepareConnectedDevice(true);
198         final String expectedSummary = mContext.getString(
199                 R.string.bluetooth_connected_multiple_devices_summary);
200 
201         assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
202     }
203 
prepareConnectedDevice(boolean multipleDevices)204     private void prepareConnectedDevice(boolean multipleDevices) {
205         final Set<BluetoothDevice> devices = new HashSet<>();
206         doReturn(DEVICE_NAME).when(mConnectedDevice).getName();
207         doReturn(true).when(mConnectedDevice).isConnected();
208         devices.add(mConnectedDevice);
209         if (multipleDevices) {
210             // Add one more device if we need to test multiple devices
211             doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName();
212             doReturn(true).when(mConnectedKeyBoardDevice).isConnected();
213             devices.add(mConnectedKeyBoardDevice);
214         }
215 
216         doReturn(devices).when(mBtAdapter).getBondedDevices();
217     }
218 
219     private class SummaryListener implements OnSummaryChangeListener {
220         String summary;
221 
222         @Override
onSummaryChanged(String summary)223         public void onSummaryChanged(String summary) {
224             this.summary = summary;
225         }
226     }
227 
228 }
229