Home | History | Annotate | Download | only in unit
      1 /*
      2  * Copyright (C) 2008 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.mediaframeworktest.unit;
     18 
     19 import android.util.Log;
     20 import android.os.Looper;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.media.MediaPlayer;
     24 import android.test.AndroidTestCase;
     25 import com.android.mediaframeworktest.MediaNames;
     26 
     27 /**
     28  * A template class for running a method under test in all possible
     29  * states of a MediaPlayer object.
     30  *
     31  * @see com.android.mediaframeworktest.unit.MediaPlayerSeekToStateUnitTest
     32  * for an example of using this class.
     33  *
     34  * A typical concrete unit test class would implement the
     35  * MediaPlayerMethodUnderTest interface and have a reference to an object of
     36  * this class. Then it calls runTestOnMethod() to actually perform the unit
     37  * tests.
     38  *
     39  */
     40 class MediaPlayerStateUnitTestTemplate extends AndroidTestCase {
     41     private static final String TEST_PATH = MediaNames.TEST_PATH_1;
     42     private static final String TAG = "MediaPlayerStateUnitTestTemplate";
     43     private static final int SEEK_TO_END  = 135110;  // Milliseconds.
     44     private static int WAIT_FOR_COMMAND_TO_COMPLETE = 1000;  // Milliseconds.
     45 
     46     private MediaPlayerStateErrors mStateErrors = new MediaPlayerStateErrors();
     47     private MediaPlayer mMediaPlayer = null;
     48     private boolean mInitialized = false;
     49     private boolean mOnCompletionHasBeenCalled = false;
     50     private MediaPlayerStateErrors.MediaPlayerState mMediaPlayerState = null;
     51     private Looper mLooper = null;
     52     private final Object lock = new Object();
     53     private MediaPlayerMethodUnderTest mMethodUnderTest = null;
     54 
     55     // An Handler object is absolutely necessary for receiving callback
     56     // messages from MediaPlayer objects.
     57     private Handler mHandler = new Handler() {
     58         @Override
     59         public void handleMessage(Message msg) {
     60             /*
     61             switch(msg.what) {
     62                 case MediaPlayerStateErrors.MEDIA_PLAYER_ERROR:
     63                     Log.v(TAG, "handleMessage: received MEDIA_PLAYER_ERROR message");
     64                     break;
     65                 default:
     66                     Log.v(TAG, "handleMessage: received unknown message");
     67                 break;
     68             }
     69             */
     70         }
     71     };
     72 
     73     /**
     74      * Runs the given method under test in all possible states of a MediaPlayer
     75      * object.
     76      *
     77      * @param testMethod the method under test.
     78      */
     79     public void runTestOnMethod(MediaPlayerMethodUnderTest testMethod) {
     80         mMethodUnderTest = testMethod;
     81         if (mMethodUnderTest != null) {  // Method under test has been set?
     82             initializeMessageLooper();
     83             synchronized(lock) {
     84                 try {
     85                     lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
     86                 } catch(Exception e) {
     87                     Log.v(TAG, "runTestOnMethod: wait was interrupted.");
     88                 }
     89             }
     90             assertTrue(mInitialized);  // mMediaPlayer has been initialized?
     91             checkMethodUnderTestInAllPossibleStates();
     92             terminateMessageLooper();   // Release message looper thread.
     93             assertTrue(mOnCompletionHasBeenCalled);
     94             mMethodUnderTest.checkStateErrors(mStateErrors);
     95             cleanUp();
     96         }
     97     }
     98 
     99     /*
    100      * Initializes the message looper so that the MediaPlayer object can
    101      * receive the callback messages.
    102      */
    103     private void initializeMessageLooper() {
    104         new Thread() {
    105             @Override
    106             public void run() {
    107                 // Set up a looper to be used by mMediaPlayer.
    108                 Looper.prepare();
    109 
    110                 // Save the looper so that we can terminate this thread
    111                 // after we are done with it.
    112                 mLooper = Looper.myLooper();
    113 
    114                 mMediaPlayer = new MediaPlayer();
    115                 mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
    116                     public boolean onError(MediaPlayer player, int what, int extra) {
    117                         Log.v(TAG, "onError has been called.");
    118                         synchronized(lock) {
    119                             Log.v(TAG, "notify lock.");
    120                             setStateError(mMediaPlayerState, true);
    121                             if (mMediaPlayerState != MediaPlayerStateErrors.MediaPlayerState.ERROR) {
    122                                 notifyStateError();
    123                             }
    124                             lock.notify();
    125                         }
    126                         return true;
    127                     }
    128                 });
    129                 mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    130                     public void onCompletion(MediaPlayer player) {
    131                         Log.v(TAG, "onCompletion has been called.");
    132                         synchronized(lock) {
    133                             if (mMediaPlayerState == MediaPlayerStateErrors.MediaPlayerState.PLAYBACK_COMPLETED) {
    134                                 mOnCompletionHasBeenCalled = true;
    135                             }
    136                             lock.notify();
    137                         }
    138                     }
    139                 });
    140                 synchronized(lock) {
    141                     mInitialized = true;
    142                     lock.notify();
    143                 }
    144                 Looper.loop();  // Blocks forever until Looper.quit() is called.
    145                 Log.v(TAG, "initializeMessageLooper: quit.");
    146             }
    147         }.start();
    148     }
    149 
    150     /*
    151      * Calls method under test in the given state of the MediaPlayer object.
    152      *
    153      * @param state the MediaPlayer state in which the method under test is called.
    154      */
    155     private void callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState state) {
    156         Log.v(TAG, "call " + mMethodUnderTest + ": started in state " + state);
    157         setMediaPlayerToState(state);
    158         mMethodUnderTest.invokeMethodUnderTest(mMediaPlayer);
    159         synchronized(lock) {
    160             try {
    161                 lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
    162            } catch(Exception e) {
    163                Log.v(TAG, "callMediaPlayerMethodUnderTestInState: wait is interrupted in state " + state);
    164            }
    165         }
    166         Log.v(TAG, "call " + mMethodUnderTest + ": ended in state " + state);
    167     }
    168 
    169     /*
    170      * The following setMediaPlayerToXXXStateXXX methods sets the MediaPlayer
    171      * object to the corresponding state, given the assumption that reset()
    172      * always resets the MediaPlayer object to Idle (after reset) state.
    173      */
    174     private void setMediaPlayerToIdleStateAfterReset() {
    175         try {
    176             mMediaPlayer.reset();
    177             mMediaPlayer.setDataSource(TEST_PATH);
    178             mMediaPlayer.prepare();
    179             mMediaPlayer.reset();
    180         } catch(Exception e) {
    181             Log.v(TAG, "setMediaPlayerToIdleStateAfterReset: Exception " + e.getClass().getName() + " was thrown.");
    182             assertTrue(false);
    183         }
    184     }
    185 
    186     private void setMediaPlayerToInitializedState() {
    187         try {
    188             mMediaPlayer.reset();
    189             mMediaPlayer.setDataSource(TEST_PATH);
    190         } catch(Exception e) {
    191             Log.v(TAG, "setMediaPlayerToInitializedState: Exception " + e.getClass().getName() + " was thrown.");
    192             assertTrue(false);
    193         }
    194     }
    195 
    196     private void setMediaPlayerToPreparedState() {
    197         try {
    198             mMediaPlayer.reset();
    199             mMediaPlayer.setDataSource(TEST_PATH);
    200             mMediaPlayer.prepare();
    201         } catch(Exception e) {
    202             Log.v(TAG, "setMediaPlayerToPreparedState: Exception " + e.getClass().getName() + " was thrown.");
    203             assertTrue(false);
    204         }
    205     }
    206 
    207     private void setMediaPlayerToPreparedStateAfterStop() {
    208         try {
    209             mMediaPlayer.reset();
    210             mMediaPlayer.setDataSource(TEST_PATH);
    211             mMediaPlayer.prepare();
    212             mMediaPlayer.start();
    213             mMediaPlayer.stop();
    214             mMediaPlayer.prepare();
    215         } catch(Exception e) {
    216             Log.v(TAG, "setMediaPlayerToPreparedStateAfterStop: Exception " + e.getClass().getName() + " was thrown.");
    217             assertTrue(false);
    218         }
    219     }
    220 
    221     private void setMediaPlayerToStartedState() {
    222         try {
    223             mMediaPlayer.reset();
    224             mMediaPlayer.setDataSource(TEST_PATH);
    225             mMediaPlayer.prepare();
    226             mMediaPlayer.start();
    227         } catch(Exception e) {
    228             Log.v(TAG, "setMediaPlayerToStartedState: Exception " + e.getClass().getName() + " was thrown.");
    229             assertTrue(false);
    230         }
    231     }
    232 
    233     private void setMediaPlayerToStartedStateAfterPause() {
    234         try {
    235             mMediaPlayer.reset();
    236             mMediaPlayer.setDataSource(TEST_PATH);
    237             mMediaPlayer.prepare();
    238             mMediaPlayer.start();
    239             mMediaPlayer.pause();
    240 
    241             // pause() is an asynchronous call and returns immediately, but
    242             // PV player engine may take quite a while to actually set the
    243             // player state to Paused; if we call start() right after pause()
    244             // without waiting, start() may fail.
    245             try {
    246                 Thread.sleep(MediaNames.PAUSE_WAIT_TIME);
    247             } catch(Exception ie) {
    248                 Log.v(TAG, "sleep was interrupted and terminated prematurely");
    249             }
    250 
    251             mMediaPlayer.start();
    252         } catch(Exception e) {
    253             Log.v(TAG, "setMediaPlayerToStartedStateAfterPause: Exception " + e.getClass().getName() + " was thrown.");
    254             assertTrue(false);
    255         }
    256     }
    257 
    258     private void setMediaPlayerToPausedState() {
    259         try {
    260             mMediaPlayer.reset();
    261             mMediaPlayer.setDataSource(TEST_PATH);
    262             mMediaPlayer.prepare();
    263             mMediaPlayer.start();
    264             mMediaPlayer.pause();
    265         } catch(Exception e) {
    266             Log.v(TAG, "setMediaPlayerToPausedState: Exception " + e.getClass().getName() + " was thrown.");
    267             assertTrue(false);
    268         }
    269     }
    270 
    271     private void setMediaPlayerToStoppedState() {
    272         try {
    273             mMediaPlayer.reset();
    274             mMediaPlayer.setDataSource(TEST_PATH);
    275             mMediaPlayer.prepare();
    276             mMediaPlayer.start();
    277             mMediaPlayer.stop();
    278         } catch(Exception e) {
    279             Log.v(TAG, "setMediaPlayerToStoppedState: Exception " + e.getClass().getName() + " was thrown.");
    280             assertTrue(false);
    281         }
    282     }
    283 
    284     private void setMediaPlayerToPlaybackCompletedState() {
    285         try {
    286             mMediaPlayer.reset();
    287             mMediaPlayer.setDataSource(TEST_PATH);
    288             mMediaPlayer.prepare();
    289             mMediaPlayer.seekTo(SEEK_TO_END);
    290             mMediaPlayer.start();
    291             synchronized(lock) {
    292                 try {
    293                     lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
    294                 } catch(Exception e) {
    295                     Log.v(TAG, "setMediaPlayerToPlaybackCompletedState: wait was interrupted.");
    296                 }
    297             }
    298         } catch(Exception e) {
    299             Log.v(TAG, "setMediaPlayerToPlaybackCompletedState: Exception " + e.getClass().getName() + " was thrown.");
    300             assertTrue(false);
    301         }
    302         Log.v(TAG, "setMediaPlayerToPlaybackCompletedState: done.");
    303     }
    304 
    305     /*
    306      * There are a lot of ways to force the MediaPlayer object to enter
    307      * the Error state. The impact (such as onError is called or not) highly
    308      * depends on how the Error state is entered.
    309      */
    310     private void setMediaPlayerToErrorState() {
    311         try {
    312             mMediaPlayer.reset();
    313             mMediaPlayer.setDataSource(TEST_PATH);
    314             mMediaPlayer.start();
    315             synchronized(lock) {
    316                 try {
    317                     lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
    318                 } catch(Exception e) {
    319                     Log.v(TAG, "setMediaPlayerToErrorState: wait was interrupted.");
    320                 }
    321             }
    322         } catch(Exception e) {
    323             Log.v(TAG, "setMediaPlayerToErrorState: Exception " + e.getClass().getName() + " was thrown.");
    324             assertTrue(e instanceof IllegalStateException);
    325         }
    326         Log.v(TAG, "setMediaPlayerToErrorState: done.");
    327     }
    328 
    329     /*
    330      * Sets the state of the MediaPlayer object to the specified one.
    331      *
    332      * @param state the state of the MediaPlayer object.
    333      */
    334     private void setMediaPlayerToState(MediaPlayerStateErrors.MediaPlayerState state) {
    335         mMediaPlayerState = state;
    336         switch(state) {
    337             case IDLE:
    338                 // Does nothing.
    339                 break;
    340             case IDLE_AFTER_RESET:
    341                 setMediaPlayerToIdleStateAfterReset();
    342                 break;
    343             case INITIALIZED:
    344                 setMediaPlayerToInitializedState();
    345                 break;
    346             case PREPARED:
    347                 setMediaPlayerToPreparedState();
    348                 break;
    349             case PREPARED_AFTER_STOP:
    350                 setMediaPlayerToPreparedStateAfterStop();
    351                 break;
    352             case STARTED:
    353                 setMediaPlayerToStartedState();
    354                 break;
    355             case STARTED_AFTER_PAUSE:
    356                 setMediaPlayerToStartedStateAfterPause();
    357                 break;
    358             case PAUSED:
    359                 setMediaPlayerToPausedState();
    360                 break;
    361             case STOPPED:
    362                 setMediaPlayerToStoppedState();
    363                 break;
    364             case PLAYBACK_COMPLETED:
    365                 setMediaPlayerToPlaybackCompletedState();
    366                 break;
    367             case ERROR:
    368                 setMediaPlayerToErrorState();
    369                 break;
    370         }
    371     }
    372 
    373     /*
    374      * Sets the error value of the corresponding state to the given error.
    375      *
    376      * @param state the state of the MediaPlayer object.
    377      * @param error the value of the state error to be set.
    378      */
    379     private void setStateError(MediaPlayerStateErrors.MediaPlayerState state, boolean error) {
    380         switch(state) {
    381             case IDLE:
    382                 mStateErrors.errorInIdleState = error;
    383                 break;
    384             case IDLE_AFTER_RESET:
    385                 mStateErrors.errorInIdleStateAfterReset = error;
    386                 break;
    387             case INITIALIZED:
    388                 mStateErrors.errorInInitializedState = error;
    389                 break;
    390             case PREPARED:
    391                 mStateErrors.errorInPreparedState = error;
    392                 break;
    393             case PREPARED_AFTER_STOP:
    394                 mStateErrors.errorInPreparedStateAfterStop = error;
    395                 break;
    396             case STARTED:
    397                 mStateErrors.errorInStartedState = error;
    398                 break;
    399             case STARTED_AFTER_PAUSE:
    400                 mStateErrors.errorInStartedStateAfterPause = error;
    401                 break;
    402             case PAUSED:
    403                 mStateErrors.errorInPausedState = error;
    404                 break;
    405             case STOPPED:
    406                 mStateErrors.errorInStoppedState = error;
    407                 break;
    408             case PLAYBACK_COMPLETED:
    409                 mStateErrors.errorInPlaybackCompletedState = error;
    410                 break;
    411             case ERROR:
    412                 mStateErrors.errorInErrorState = error;
    413                 break;
    414         }
    415     }
    416 
    417     private void notifyStateError() {
    418         mHandler.sendMessage(mHandler.obtainMessage(MediaPlayerStateErrors.MEDIA_PLAYER_ERROR));
    419     }
    420 
    421     private void checkIdleState() {
    422         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.IDLE);
    423     }
    424 
    425     private void checkIdleStateAfterReset() {
    426         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.IDLE_AFTER_RESET);
    427     }
    428 
    429     private void checkInitializedState() {
    430         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.INITIALIZED);
    431     }
    432 
    433     private void checkPreparedState() {
    434         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PREPARED);
    435     }
    436 
    437     private void checkPreparedStateAfterStop() {
    438         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PREPARED_AFTER_STOP);
    439     }
    440 
    441     private void checkStartedState() {
    442         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.STARTED);
    443     }
    444 
    445     private void checkPausedState() {
    446         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PAUSED);
    447     }
    448 
    449     private void checkStartedStateAfterPause() {
    450         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.STARTED_AFTER_PAUSE);
    451     }
    452 
    453     private void checkStoppedState() {
    454         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.STOPPED);
    455     }
    456 
    457     private void checkPlaybackCompletedState() {
    458         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PLAYBACK_COMPLETED);
    459     }
    460 
    461     private void checkErrorState() {
    462         callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.ERROR);
    463     }
    464 
    465     /*
    466      * Checks the given method under test in all possible states of the MediaPlayer object.
    467      */
    468     private void checkMethodUnderTestInAllPossibleStates() {
    469         // Must be called first.
    470         checkIdleState();
    471 
    472         // The sequence of the following method calls should not
    473         // affect the test results.
    474         checkErrorState();
    475         checkIdleStateAfterReset();
    476         checkInitializedState();
    477         checkStartedState();
    478         checkStartedStateAfterPause();
    479         checkPausedState();
    480         checkPreparedState();
    481 
    482         checkPreparedStateAfterStop();
    483 
    484         checkPlaybackCompletedState();
    485         checkStoppedState();
    486     }
    487 
    488     /*
    489      * Terminates the message looper thread.
    490      */
    491     private void terminateMessageLooper() {
    492         mLooper.quit();
    493         mMediaPlayer.release();
    494     }
    495 
    496     /*
    497      * Cleans up all the internal object references.
    498      */
    499     private void cleanUp() {
    500         mMediaPlayer = null;
    501         mMediaPlayerState = null;
    502         mLooper = null;
    503         mStateErrors = null;
    504         mMethodUnderTest = null;
    505     }
    506 }
    507