Chromium Code Reviews| Index: webrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java |
| diff --git a/webrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java b/webrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0d077f37e7067dcf102a4b677ab8ec516816217e |
| --- /dev/null |
| +++ b/webrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java |
| @@ -0,0 +1,271 @@ |
| +/* |
| + * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
| + * |
| + * Use of this source code is governed by a BSD-style license |
| + * that can be found in the LICENSE file in the root of the source |
| + * tree. An additional intellectual property rights grant can be found |
| + * in the file PATENTS. All contributing project authors may |
| + * be found in the AUTHORS file in the root of the source tree. |
| + */ |
| + |
| +package org.appspot.apprtc; |
| + |
| +import static org.junit.Assert.assertEquals; |
| +import static org.junit.Assert.assertNotNull; |
| +import static org.junit.Assert.assertNull; |
| +import static org.junit.Assert.assertFalse; |
| +import static org.junit.Assert.assertTrue; |
| +import static org.mockito.Mockito.doCallRealMethod; |
| +import static org.mockito.Mockito.mock; |
| +import static org.mockito.Mockito.never; |
| +import static org.mockito.Mockito.times; |
| +import static org.mockito.Mockito.verify; |
| +import static org.mockito.Mockito.when; |
| + |
| +import android.bluetooth.BluetoothAdapter; |
| +import android.bluetooth.BluetoothDevice; |
| +import android.bluetooth.BluetoothHeadset; |
| +import android.bluetooth.BluetoothProfile; |
| +import android.content.BroadcastReceiver; |
| +import android.content.Context; |
| +import android.content.Intent; |
| +import android.content.IntentFilter; |
| +import android.media.AudioManager; |
| +import android.util.Log; |
| +import java.util.LinkedList; |
| +import java.util.List; |
| +import org.appspot.apprtc.AppRTCBluetoothManager.State; |
| +import org.chromium.testing.local.LocalRobolectricTestRunner; |
| +import org.junit.Before; |
| +import org.junit.Test; |
| +import org.junit.runner.RunWith; |
| +import org.robolectric.annotation.Config; |
| +import org.robolectric.shadows.ShadowApplication; |
| +import org.robolectric.shadows.ShadowLog; |
| + |
| +/** |
| + * Verifies basic behavior of the AppRTCBluetoothManager class. |
| + * Note that the test object uses an AppRTCAudioManager (injected in ctor), |
| + * but a mocked version is used instead. Hence, the parts "driven" by the AppRTC |
| + * audio manager are not included in this test. |
| + */ |
| +@RunWith(LocalRobolectricTestRunner.class) |
| +@Config(manifest = Config.NONE) |
| +public class BluetoothManagerTest { |
| + private static final String TAG = "BluetoothManagerTest"; |
| + private static final String BLUETOOTH_TEST_DEVICE_NAME = "BluetoothTestDevice"; |
| + |
| + private BroadcastReceiver bluetoothHeadsetStateReceiver; |
| + private BluetoothProfile.ServiceListener bluetoothServiceListener; |
| + private BluetoothHeadset mockedBluetoothHeadset; |
| + private BluetoothDevice mockedBluetoothDevice; |
| + private List<BluetoothDevice> mockedBluetoothDeviceList; |
| + private AppRTCBluetoothManager bluetoothManager; |
| + private AppRTCAudioManager mockedAppRtcAudioManager; |
| + private AudioManager mockedAudioManager; |
| + private Context context; |
| + |
| + @Before |
| + public void setUp() { |
| + ShadowLog.stream = System.out; |
| + context = ShadowApplication.getInstance().getApplicationContext(); |
| + mockedAppRtcAudioManager = mock(AppRTCAudioManager.class); |
| + mockedAudioManager = mock(AudioManager.class); |
| + mockedBluetoothHeadset = mock(BluetoothHeadset.class); |
| + mockedBluetoothDevice = mock(BluetoothDevice.class); |
| + mockedBluetoothDeviceList = new LinkedList<BluetoothDevice>(); |
| + |
| + // Simulate that bluetooth SCO audio is available by default. |
| + when(mockedAudioManager.isBluetoothScoAvailableOffCall()).thenReturn(true); |
| + |
| + // Create the test object and override protected methods for this test. |
| + bluetoothManager = new AppRTCBluetoothManager(context, mockedAppRtcAudioManager) { |
| + @Override |
| + protected AudioManager getAudioManager(Context context) { |
| + Log.d(TAG, "getAudioManager"); |
| + return mockedAudioManager; |
| + } |
| + |
| + @Override |
| + protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { |
| + Log.d(TAG, "registerReceiver"); |
| + if (filter.hasAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) |
| + && filter.hasAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { |
| + // Gives access to the real broadcast receiver so the test can use it. |
| + bluetoothHeadsetStateReceiver = receiver; |
| + } |
| + } |
| + |
| + @Override |
| + protected void unregisterReceiver(BroadcastReceiver receiver) { |
| + Log.d(TAG, "unregisterReceiver"); |
| + if (receiver == bluetoothHeadsetStateReceiver) { |
| + bluetoothHeadsetStateReceiver = null; |
| + } |
| + } |
| + |
| + @Override |
| + protected boolean getBluetoothProfileProxy( |
| + Context context, BluetoothProfile.ServiceListener listener, int profile) { |
| + Log.d(TAG, "getBluetoothProfileProxy"); |
| + if (profile == BluetoothProfile.HEADSET) { |
| + // Allows the test to access the real Bluetooth service listener object. |
| + bluetoothServiceListener = listener; |
| + } |
| + return true; |
| + } |
| + |
| + @Override |
| + protected boolean hasPermission(Context context, String permission) { |
| + Log.d(TAG, "hasPermission(" + permission + ")"); |
| + // Ensure that the client asks for Bluetooth permission. |
| + return (permission == android.Manifest.permission.BLUETOOTH); |
| + } |
| + |
| + @Override |
| + protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) { |
| + // Do nothing in tests. No need to mock BluetoothAdapter. |
| + } |
| + }; |
| + } |
| + |
| + // Verify that Bluetooth service listener for headset profile is properly initialized. |
| + @Test |
| + public void testBluetoothServiceListenerInitialized() { |
| + bluetoothManager.start(); |
| + assertNotNull(bluetoothServiceListener); |
| + verify(mockedAppRtcAudioManager, never()).updateAudioDeviceState(); |
| + } |
| + |
| + // Verify that broadcast receivers for Bluetooth SCO audio state and Bluetooth headset state |
| + // are properly registered and unregistered. |
| + @Test |
| + public void testBluetoothBroadcastReceiversAreRegistered() { |
| + bluetoothManager.start(); |
| + assertNotNull(bluetoothHeadsetStateReceiver); |
| + bluetoothManager.stop(); |
| + assertNull(bluetoothHeadsetStateReceiver); |
| + } |
| + |
| + // Verify that the Bluetooth manager starts and stops with correct states. |
| + @Test |
| + public void testBluetoothDefaultStartStopStates() { |
| + bluetoothManager.start(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + bluetoothManager.stop(); |
| + assertEquals(bluetoothManager.getState(), State.UNINITIALIZED); |
| + } |
| + |
| + // Verify correct state after receiving BluetoothServiceListener.onServiceConnected() |
| + // when no BT device is enabled. |
| + @Test |
| + public void testBluetoothServiceListenerConnectedWithNoHeadset() { |
| + bluetoothManager.start(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + simulateBluetoothServiceConnectedWithNoConnectedHeadset(); |
| + verify(mockedAppRtcAudioManager, times(1)).updateAudioDeviceState(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + } |
| + |
| + // Verify correct state after receiving BluetoothServiceListener.onServiceConnected() |
| + // when one emulated (test) BT device is enabled. Android does not support more than |
| + // one connected BT headset. |
| + @Test |
| + public void testBluetoothServiceListenerConnectedWithHeadset() { |
| + bluetoothManager.start(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + simulateBluetoothServiceConnectedWithConnectedHeadset(); |
| + verify(mockedAppRtcAudioManager, times(1)).updateAudioDeviceState(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_AVAILABLE); |
| + } |
| + |
| + // Verify correct state after receiving BluetoothProfile.ServiceListener.onServiceDisconnected(). |
| + @Test |
| + public void testBluetoothServiceListenerDisconnected() { |
| + bluetoothManager.start(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + simulateBluetoothServiceDisconnected(); |
| + verify(mockedAppRtcAudioManager, times(1)).updateAudioDeviceState(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + } |
| + |
| + // Verify correct state after BluetoothServiceListener.onServiceConnected() and |
| + // the intent indicating that the headset is actually connected. Both these callbacks |
| + // results in calls to updateAudioDeviceState() on the AppRTC audio manager. |
| + // No BT SCO is enabled here to keep the test limited. |
| + @Test |
| + public void testBluetoothHeadsetConnected() { |
| + bluetoothManager.start(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + simulateBluetoothServiceConnectedWithConnectedHeadset(); |
| + simulateBluetoothHeadsetConnected(); |
| + verify(mockedAppRtcAudioManager, times(2)).updateAudioDeviceState(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_AVAILABLE); |
| + } |
| + |
| + // Verify correct state sequence for a case when a BT headset is available, |
| + // followed by BT SCO audio being enabled and then stopped. |
| + @Test |
| + public void testBluetoothScoAudioStartAndStop() { |
|
magjed_webrtc
2016/12/10 19:34:45
This is exactly what I wanted. It looks very clean
henrika_webrtc
2016/12/12 14:13:25
Thanks. Agree that it was very useful to add and l
|
| + bluetoothManager.start(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE); |
| + simulateBluetoothServiceConnectedWithConnectedHeadset(); |
| + assertEquals(bluetoothManager.getState(), State.HEADSET_AVAILABLE); |
| + bluetoothManager.startScoAudio(); |
| + assertEquals(bluetoothManager.getState(), State.SCO_CONNECTING); |
| + simulateBluetoothScoConnectionConnected(); |
| + assertEquals(bluetoothManager.getState(), State.SCO_CONNECTED); |
| + bluetoothManager.stopScoAudio(); |
| + simulateBluetoothScoConnectionDisconnected(); |
| + assertEquals(bluetoothManager.getState(), State.SCO_DISCONNECTING); |
| + bluetoothManager.stop(); |
| + assertEquals(bluetoothManager.getState(), State.UNINITIALIZED); |
| + verify(mockedAppRtcAudioManager, times(3)).updateAudioDeviceState(); |
| + } |
| + |
| + /** |
| + * Private helper methods. |
| + */ |
| + private void simulateBluetoothServiceConnectedWithNoConnectedHeadset() { |
| + mockedBluetoothDeviceList.clear(); |
| + when(mockedBluetoothHeadset.getConnectedDevices()).thenReturn(mockedBluetoothDeviceList); |
| + bluetoothServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mockedBluetoothHeadset); |
| + // In real life, the AppRTC audio manager makes this call. |
| + bluetoothManager.updateDevice(); |
| + } |
| + |
| + private void simulateBluetoothServiceConnectedWithConnectedHeadset() { |
| + mockedBluetoothDeviceList.clear(); |
| + mockedBluetoothDeviceList.add(mockedBluetoothDevice); |
| + when(mockedBluetoothHeadset.getConnectedDevices()).thenReturn(mockedBluetoothDeviceList); |
| + when(mockedBluetoothDevice.getName()).thenReturn(BLUETOOTH_TEST_DEVICE_NAME); |
| + bluetoothServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mockedBluetoothHeadset); |
| + // In real life, the AppRTC audio manager makes this call. |
| + bluetoothManager.updateDevice(); |
| + } |
| + |
| + private void simulateBluetoothServiceDisconnected() { |
| + bluetoothServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); |
| + } |
| + |
| + private void simulateBluetoothHeadsetConnected() { |
| + Intent intent = new Intent(); |
| + intent.setAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); |
| + intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_CONNECTED); |
| + bluetoothHeadsetStateReceiver.onReceive(context, intent); |
| + } |
| + |
| + private void simulateBluetoothScoConnectionConnected() { |
| + Intent intent = new Intent(); |
| + intent.setAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); |
| + intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_CONNECTED); |
| + bluetoothHeadsetStateReceiver.onReceive(context, intent); |
| + } |
| + |
| + private void simulateBluetoothScoConnectionDisconnected() { |
| + Intent intent = new Intent(); |
| + intent.setAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); |
| + intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED); |
| + bluetoothHeadsetStateReceiver.onReceive(context, intent); |
| + } |
| +} |