Add MapClientServiceTest
Bug: 237467631
Test: atest MapClientServiceTest
Change-Id: I212a7ead80ed2a289293b480862668f733828d2e
(cherry picked from commit cc75d744b264823e09ef4fd6c13e5358ba04731f)
Merged-In: I212a7ead80ed2a289293b480862668f733828d2e
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
index cfb2d5a..40bf81c 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -64,7 +64,8 @@
private AdapterService mAdapterService;
private DatabaseManager mDatabaseManager;
private static MapClientService sMapClientService;
- private MapBroadcastReceiver mMapReceiver;
+ @VisibleForTesting
+ MapBroadcastReceiver mMapReceiver;
public static boolean isEnabled() {
return BluetoothProperties.isProfileMapClientEnabled().orElse(false);
@@ -82,7 +83,8 @@
return sMapClientService;
}
- private static synchronized void setMapClientService(MapClientService instance) {
+ @VisibleForTesting
+ static synchronized void setMapClientService(MapClientService instance) {
if (DBG) {
Log.d(TAG, "setMapClientService(): set to: " + instance);
}
@@ -362,6 +364,7 @@
Log.d(TAG, "in Cleanup");
}
removeUncleanAccounts();
+ mMapInstanceMap.clear();
// TODO(b/72948646): should be moved to stop()
setMapClientService(null);
}
@@ -719,7 +722,8 @@
}
}
- private class MapBroadcastReceiver extends BroadcastReceiver {
+ @VisibleForTesting
+ class MapBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java
new file mode 100644
index 0000000..07e1266
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.mapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Intent;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.BluetoothMethodProxy;
+import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class MapClientServiceTest {
+ private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
+
+ @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
+
+ @Mock private AdapterService mAdapterService;
+ @Mock private DatabaseManager mDatabaseManager;
+
+ private MapClientService mService = null;
+ private BluetoothAdapter mAdapter = null;
+ private BluetoothDevice mRemoteDevice;
+
+ @Before
+ public void setUp() throws Exception {
+ Assume.assumeTrue("Ignore test when MapClientService is not enabled",
+ MapClientService.isEnabled());
+ MockitoAnnotations.initMocks(this);
+ TestUtils.setAdapterService(mAdapterService);
+ doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
+ doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+ TestUtils.startService(mServiceRule, MapClientService.class);
+ mService = MapClientService.getMapClientService();
+ assertThat(mService).isNotNull();
+ // Try getting the Bluetooth adapter
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ assertThat(mAdapter).isNotNull();
+ mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (!MapClientService.isEnabled()) {
+ return;
+ }
+ TestUtils.stopService(mServiceRule, MapClientService.class);
+ mService = MapClientService.getMapClientService();
+ assertThat(mService).isNull();
+ TestUtils.clearAdapterService(mAdapterService);
+ BluetoothMethodProxy.setInstanceForTesting(null);
+ }
+
+ @Test
+ public void initialize() {
+ assertThat(MapClientService.getMapClientService()).isNotNull();
+ }
+
+ @Test
+ public void setMapClientService_withNull() {
+ MapClientService.setMapClientService(null);
+
+ assertThat(MapClientService.getMapClientService()).isNull();
+ }
+
+ @Test
+ public void dump_callsStateMachineDump() {
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ StringBuilder builder = new StringBuilder();
+
+ mService.dump(builder);
+
+ verify(sm).dump(builder);
+ }
+
+ @Test
+ public void setConnectionPolicy() {
+ int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+ when(mDatabaseManager.setProfileConnectionPolicy(
+ mRemoteDevice, BluetoothProfile.MAP_CLIENT, connectionPolicy)).thenReturn(true);
+
+ assertThat(mService.setConnectionPolicy(mRemoteDevice, connectionPolicy)).isTrue();
+ }
+
+ @Test
+ public void getConnectionPolicy() {
+ int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+ when(mDatabaseManager.getProfileConnectionPolicy(
+ mRemoteDevice, BluetoothProfile.MAP_CLIENT)).thenReturn(connectionPolicy);
+
+ assertThat(mService.getConnectionPolicy(mRemoteDevice)).isEqualTo(connectionPolicy);
+ }
+
+ @Test
+ public void connect_whenPolicyIsForbidden_returnsFalse() {
+ int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ when(mDatabaseManager.getProfileConnectionPolicy(
+ mRemoteDevice, BluetoothProfile.MAP_CLIENT)).thenReturn(connectionPolicy);
+
+ assertThat(mService.connect(mRemoteDevice)).isFalse();
+ }
+
+ @Test
+ public void connect_whenPolicyIsAllowed_returnsTrue() {
+ int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+ when(mDatabaseManager.getProfileConnectionPolicy(
+ mRemoteDevice, BluetoothProfile.MAP_CLIENT)).thenReturn(connectionPolicy);
+
+ assertThat(mService.connect(mRemoteDevice)).isTrue();
+ }
+
+ @Test
+ public void disconnect_whenNotConnected_returnsFalse() {
+ assertThat(mService.disconnect(mRemoteDevice)).isFalse();
+ }
+
+ @Test
+ public void disconnect_whenConnected_returnsTrue() {
+ int connectionState = BluetoothProfile.STATE_CONNECTED;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ when(sm.getState()).thenReturn(connectionState);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+
+ assertThat(mService.disconnect(mRemoteDevice)).isTrue();
+
+ verify(sm).disconnect();
+ }
+
+ @Test
+ public void getConnectionState_whenNotConnected() {
+ assertThat(mService.getConnectionState(mRemoteDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void getConnectionState_whenConnected() {
+ int connectionState = BluetoothProfile.STATE_CONNECTED;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ when(sm.getState()).thenReturn(connectionState);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+
+ assertThat(mService.getConnectionState(mRemoteDevice)).isEqualTo(connectionState);
+ }
+
+ @Test
+ public void getConnectedDevices() {
+ int connectionState = BluetoothProfile.STATE_CONNECTED;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ BluetoothDevice[] bondedDevices = new BluetoothDevice[] {mRemoteDevice};
+ when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ when(sm.getState()).thenReturn(connectionState);
+
+ assertThat(mService.getConnectedDevices()).contains(mRemoteDevice);
+ }
+
+ @Test
+ public void getMceStateMachineForDevice() {
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+
+ assertThat(mService.getMceStateMachineForDevice(mRemoteDevice)).isEqualTo(sm);
+ }
+
+ @Test
+ public void getSupportedFeatures() {
+ int supportedFeatures = 100;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ when(sm.getSupportedFeatures()).thenReturn(supportedFeatures);
+
+ assertThat(mService.getSupportedFeatures(mRemoteDevice)).isEqualTo(supportedFeatures);
+ verify(sm).getSupportedFeatures();
+ }
+
+ @Test
+ public void setMessageStatus() {
+ String handle = "FFAB";
+ int status = 123;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ when(sm.setMessageStatus(handle, status)).thenReturn(true);
+
+ assertThat(mService.setMessageStatus(mRemoteDevice, handle, status)).isTrue();
+ verify(sm).setMessageStatus(handle, status);
+ }
+
+ @Test
+ public void getUnreadMessages() {
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ when(sm.getUnreadMessages()).thenReturn(true);
+
+ assertThat(mService.getUnreadMessages(mRemoteDevice)).isTrue();
+ verify(sm).getUnreadMessages();
+ }
+
+ @Test
+ public void cleanUpDevice() {
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+
+ mService.cleanupDevice(mRemoteDevice);
+
+ assertThat(mService.getInstanceMap()).doesNotContainKey(mRemoteDevice);
+ }
+
+ @Test
+ public void broadcastReceiver_withRandomAction_doesNothing() {
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+
+ Intent intent = new Intent("Test_random_action");
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+ mService.mMapReceiver.onReceive(mService, intent);
+
+ verify(sm, never()).disconnect();
+ }
+
+ @Test
+ public void broadcastReceiver_withActionAclDisconnected_withoutDevice_doesNothing() {
+ int connectionState = BluetoothProfile.STATE_CONNECTED;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ when(sm.getState()).thenReturn(connectionState);
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ // Device is not included in this intent
+ mService.mMapReceiver.onReceive(mService, intent);
+
+ verify(sm, never()).disconnect();
+ }
+
+ @Test
+ public void broadcastReceiver_withActionAclDisconnected_whenNotConnected_doesNothing() {
+ // No state machine exists for this device
+ Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+ mService.mMapReceiver.onReceive(mService, intent);
+ }
+
+ @Test
+ public void broadcastReceiver_withActionAclDisconnected_whenConnected_callsDisconnect() {
+ int connectionState = BluetoothProfile.STATE_CONNECTED;
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+ when(sm.getState()).thenReturn(connectionState);
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+ mService.mMapReceiver.onReceive(mService, intent);
+
+ verify(sm).disconnect();
+ }
+
+ @Test
+ public void broadcastReceiver_withActionSdpRecord_withoutMasRecord_doesNothing() {
+ MceStateMachine sm = mock(MceStateMachine.class);
+ mService.getInstanceMap().put(mRemoteDevice, sm);
+
+ Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+ intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
+ intent.putExtra(BluetoothDevice.EXTRA_UUID, BluetoothUuid.MAS);
+ // No MasRecord / searchStatus is included in this intent
+ mService.mMapReceiver.onReceive(mService, intent);
+ }
+}