From d996f17ea97b2c592338bcfbc93056d0224da1cd Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 21 Oct 2016 12:07:39 -0700 Subject: [PATCH] Add AVRCP unit tests and switch to using mockable interfaces Add a mockable interface for MediaController in order to add unit tests for AddressedMediaPlayer. Bug: 32416674 Test: Tested manually Change-Id: I081c37915fc15e58f3de37c3a16e03747503860c --- .../bluetooth/avrcp/AddressedMediaPlayer.java | 1 - src/com/android/bluetooth/avrcp/Avrcp.java | 23 ++- .../bluetooth/avrcp/AvrcpHelperClasses.java | 1 - .../bluetooth/avrcp/BrowsedMediaPlayer.java | 4 +- .../bluetooth/avrcp/mockable/MediaController.java | 224 +++++++++++++++++++++ tests/Android.mk | 11 +- tests/AndroidManifest.xml | 2 +- .../tests/avrcp/AddressedMediaPlayerTest.java | 93 +++++++++ .../{ => tests}/gatt/GattServiceTest.java | 0 9 files changed, 343 insertions(+), 16 deletions(-) create mode 100644 src/com/android/bluetooth/avrcp/mockable/MediaController.java create mode 100644 tests/src/com/android/bluetooth/tests/avrcp/AddressedMediaPlayerTest.java rename tests/src/com/android/bluetooth/{ => tests}/gatt/GattServiceTest.java (100%) diff --git a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java index bb3136a2..d66c4963 100644 --- a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java +++ b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java @@ -19,7 +19,6 @@ package com.android.bluetooth.avrcp; import android.bluetooth.BluetoothAvrcp; import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; -import android.media.session.MediaController; import android.media.MediaDescription; import android.media.MediaMetadata; import android.os.Bundle; diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java index f135cb8e..130ed69a 100644 --- a/src/com/android/bluetooth/avrcp/Avrcp.java +++ b/src/com/android/bluetooth/avrcp/Avrcp.java @@ -33,7 +33,6 @@ import android.content.SharedPreferences; import android.media.AudioManager; import android.media.MediaDescription; import android.media.MediaMetadata; -import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; import android.media.session.MediaSessionManager; @@ -1592,17 +1591,22 @@ public final class Avrcp { new MediaSessionManager.OnActiveSessionsChangedListener() { @Override - public void onActiveSessionsChanged(List mediaControllerList) { + public void onActiveSessionsChanged(List mediaControllerList) { if (DEBUG) Log.v(TAG, "received onActiveSessionsChanged"); - if (isAvailablePlayersChanged(mediaControllerList)) { + List mediaControllerListTemp = new ArrayList(); + for (android.media.session.MediaController temp : mediaControllerList) { + mediaControllerListTemp.add(MediaController.wrap(temp)); + } + + if (isAvailablePlayersChanged(mediaControllerListTemp)) { // rebuild the list cached locally in this file buildMediaPlayersList(); // inform the remote device that the player list has changed sendAvailablePlayersChanged(); - } else if (isAddressedPlayerChanged(mediaControllerList)) { - int newAddrPlayerID = getNewAddrPlayerID(mediaControllerList.get(0) + } else if (isAddressedPlayerChanged(mediaControllerListTemp)) { + int newAddrPlayerID = getNewAddrPlayerID(mediaControllerListTemp.get(0) .getPackageName()); // inform the remote device that the addressed player has changed sendAddressedPlayerChanged(newAddrPlayerID); @@ -1819,14 +1823,15 @@ public final class Avrcp { mMediaPlayerInfoList.clear(); /* Initializing all media players */ - for (MediaController mediaController : getActiveControllersList()) { - initMediaPlayer(mediaController); + for (android.media.session.MediaController mediaController : getActiveControllersList()) { + initMediaPlayer(MediaController.wrap(mediaController)); } } /* Using session manager apis, getting the list of active media controllers */ - private List getActiveControllersList() { - List controllersList = new ArrayList(); + private List getActiveControllersList() { + List controllersList = + new ArrayList(); controllersList = mMediaSessionManager.getActiveSessions(null); Log.i(TAG, "getActiveControllersList: " + controllersList.size() + " controllers"); return controllersList; diff --git a/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java b/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java index 9855a272..6a502380 100644 --- a/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java +++ b/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java @@ -16,7 +16,6 @@ package com.android.bluetooth.avrcp; -import android.media.session.MediaController; import android.media.session.MediaSession; import java.util.List; diff --git a/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java b/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java index 3e3c33c0..8c454684 100644 --- a/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java +++ b/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java @@ -22,7 +22,6 @@ import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.browse.MediaBrowser; import android.media.browse.MediaBrowser.MediaItem; -import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; import android.util.Log; @@ -223,7 +222,8 @@ class BrowsedMediaPlayer { } if (isError == false) { - mMediaController = new MediaController(mContext, token); + mMediaController = MediaController.wrap( + new android.media.session.MediaController(mContext, token)); /* get root folder items */ mMediaBrowser.subscribe(mRootFolderUid, folderItemsCb); } diff --git a/src/com/android/bluetooth/avrcp/mockable/MediaController.java b/src/com/android/bluetooth/avrcp/mockable/MediaController.java new file mode 100644 index 00000000..032bf2bd --- /dev/null +++ b/src/com/android/bluetooth/avrcp/mockable/MediaController.java @@ -0,0 +1,224 @@ +package com.android.bluetooth.avrcp; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.PendingIntent; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.MediaMetadata; +import android.media.Rating; +import android.media.VolumeProvider; +import android.media.session.PlaybackState; +import android.media.session.MediaSession; +import android.media.session.MediaSession.QueueItem; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +public class MediaController { + public android.media.session.MediaController mDelegate; + public android.media.session.MediaController.TransportControls mTransportDelegate; + public TransportControls mTransportControls; + + @Nullable + public static MediaController wrap(@Nullable android.media.session.MediaController delegate) { + return (delegate != null) ? new MediaController(delegate) : null; + } + + public MediaController(@NonNull android.media.session.MediaController delegate) { + mDelegate = delegate; + mTransportDelegate = delegate.getTransportControls(); + mTransportControls = new TransportControls(); + } + + public android.media.session.MediaController getWrappedInstance() { + return mDelegate; + } + + public @NonNull TransportControls getTransportControls() { + return mTransportControls; + } + + public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { + return mDelegate.dispatchMediaButtonEvent(keyEvent); + } + + public @Nullable PlaybackState getPlaybackState() { + return mDelegate.getPlaybackState(); + } + + public @Nullable MediaMetadata getMetadata() { + return mDelegate.getMetadata(); + } + + public @Nullable List getQueue() { + return mDelegate.getQueue(); + } + + public @Nullable CharSequence getQueueTitle() { + return mDelegate.getQueueTitle(); + } + + public @Nullable Bundle getExtras() { + return mDelegate.getExtras(); + } + + public int getRatingType() { + return mDelegate.getRatingType(); + } + + public long getFlags() { + return mDelegate.getFlags(); + } + + public @Nullable android.media.session.MediaController.PlaybackInfo getPlaybackInfo() { + return mDelegate.getPlaybackInfo(); + } + + public @Nullable PendingIntent getSessionActivity() { + return mDelegate.getSessionActivity(); + } + + public @NonNull MediaSession.Token getSessionToken() { + return mDelegate.getSessionToken(); + } + + public void setVolumeTo(int value, int flags) { + mDelegate.setVolumeTo(value, flags); + } + + public void adjustVolume(int direction, int flags) { + mDelegate.adjustVolume(direction, flags); + } + + public void registerCallback(@NonNull Callback callback) { + //TODO(apanicke): Add custom callback struct to be able to analyze and + // delegate callbacks + mDelegate.registerCallback(callback); + } + + public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) { + mDelegate.registerCallback(callback, handler); + } + + public void unregisterCallback(@NonNull Callback callback) { + mDelegate.unregisterCallback(callback); + } + + public void sendCommand(@NonNull String command, @Nullable Bundle args, + @Nullable ResultReceiver cb) { + mDelegate.sendCommand(command, args, cb); + } + + public String getPackageName() { + return mDelegate.getPackageName(); + } + + public String getTag() { + return mDelegate.getTag(); + } + + public boolean controlsSameSession(MediaController other) { + return mDelegate.controlsSameSession(other.getWrappedInstance()); + } + + public boolean controlsSameSession(android.media.session.MediaController other) { + return mDelegate.controlsSameSession(other); + } + + public static abstract class Callback extends android.media.session.MediaController.Callback { } + + public class TransportControls { + + public void prepare() { + mTransportDelegate.prepare(); + } + + public void prepareFromMediaId(String mediaId, Bundle extras) { + mTransportDelegate.prepareFromMediaId(mediaId, extras); + } + + public void prepareFromSearch(String query, Bundle extras) { + mTransportDelegate.prepareFromSearch(query, extras); + } + + public void prepareFromUri(Uri uri, Bundle extras) { + mTransportDelegate.prepareFromUri(uri, extras); + } + + public void play() { + mTransportDelegate.play(); + } + + public void playFromMediaId(String mediaId, Bundle extras) { + mTransportDelegate.playFromMediaId(mediaId, extras); + } + + public void playFromSearch(String query, Bundle extras) { + mTransportDelegate.playFromSearch(query, extras); + } + + public void playFromUri(Uri uri, Bundle extras) { + mTransportDelegate.playFromUri(uri, extras); + } + + public void skipToQueueItem(long id) { + mTransportDelegate.skipToQueueItem(id); + } + + public void pause() { + mTransportDelegate.pause(); + } + + public void stop() { + mTransportDelegate.stop(); + } + + public void seekTo(long pos) { + mTransportDelegate.seekTo(pos); + } + + public void fastForward() { + mTransportDelegate.fastForward(); + } + + public void skipToNext() { + mTransportDelegate.skipToNext(); + } + + public void rewind() { + mTransportDelegate.rewind(); + } + + public void skipToPrevious() { + mTransportDelegate.skipToPrevious(); + } + + public void setRating(Rating rating) { + mTransportDelegate.setRating(rating); + } + + public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, + @Nullable Bundle args) { + mTransportDelegate.sendCustomAction(customAction, args); + } + + public void sendCustomAction(@NonNull String action, @Nullable Bundle args) { + mTransportDelegate.sendCustomAction(action, args); + } + } +} + diff --git a/tests/Android.mk b/tests/Android.mk index 32f0111f..e2a2e82f 100755 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -5,8 +5,15 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := javax.obex android.test.runner telephony-common libprotobuf-java-micro -LOCAL_STATIC_JAVA_LIBRARIES := com.android.emailcommon littlemock dexmaker +LOCAL_JAVA_LIBRARIES := \ + javax.obex android.test.runner \ + telephony-common \ + libprotobuf-java-micro + +LOCAL_STATIC_JAVA_LIBRARIES := \ + com.android.emailcommon \ + android-support-test \ + mockito-target # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index cf128db8..b4358c8d 100755 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -64,7 +64,7 @@ the package of com.android.bluetooth. To run the tests use the command: "adb shell am instrument -w com.android.bluetooth.tests/android.test.InstrumentationTestRunner" --> - diff --git a/tests/src/com/android/bluetooth/tests/avrcp/AddressedMediaPlayerTest.java b/tests/src/com/android/bluetooth/tests/avrcp/AddressedMediaPlayerTest.java new file mode 100644 index 00000000..81773a4d --- /dev/null +++ b/tests/src/com/android/bluetooth/tests/avrcp/AddressedMediaPlayerTest.java @@ -0,0 +1,93 @@ +package com.android.bluetooth.avrcp; + +import android.bluetooth.BluetoothAvrcp; +import android.media.session.MediaSession; +import android.media.session.MediaSession.QueueItem; +import android.media.MediaDescription; +import android.media.MediaMetadata; +import android.os.Bundle; +import android.test.AndroidTestCase; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; + +import static org.mockito.Mockito.*; + +public class AddressedMediaPlayerTest extends AndroidTestCase { + + public void testHandlePassthroughCmd() { + MediaController mockController = mock(com.android.bluetooth.avrcp.MediaController.class); + MediaController.TransportControls mockTransport = mock(MediaController.TransportControls.class); + AvrcpMediaRspInterface mockRspInterface = mock(AvrcpMediaRspInterface.class); + + when(mockController.getTransportControls()).thenReturn(mockTransport); + AddressedMediaPlayer myMediaPlayer = new AddressedMediaPlayer(mockRspInterface); + + + // Test rewind + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_REWIND, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).rewind(); + + // Test fast forward + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).fastForward(); + + // Test play + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_PLAY, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).play(); + + // Test pause + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_PAUSE, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).pause(); + + // Test stop + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_STOP, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).stop(); + + // Test skip to next + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_FORWARD, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).skipToNext(); + + // Test skip backwards + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verify(mockTransport).skipToPrevious(); + + // Test invalid key + myMediaPlayer.handlePassthroughCmd(0xFF, + AvrcpConstants.KEY_STATE_PRESS, + null, + mockController); + verifyNoMoreInteractions(mockTransport); + + // Test key release + myMediaPlayer.handlePassthroughCmd(BluetoothAvrcp.PASSTHROUGH_ID_PLAY, + AvrcpConstants.KEY_STATE_RELEASE, + null, + mockController); + verifyNoMoreInteractions(mockTransport); + } +} diff --git a/tests/src/com/android/bluetooth/gatt/GattServiceTest.java b/tests/src/com/android/bluetooth/tests/gatt/GattServiceTest.java similarity index 100% rename from tests/src/com/android/bluetooth/gatt/GattServiceTest.java rename to tests/src/com/android/bluetooth/tests/gatt/GattServiceTest.java -- 2.11.0