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 */ runTestOnMethod(MediaPlayerMethodUnderTest testMethod)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 */ initializeMessageLooper()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 */ callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState state)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 */ setMediaPlayerToIdleStateAfterReset()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 setMediaPlayerToInitializedState()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 setMediaPlayerToPreparedState()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 setMediaPlayerToPreparedStateAfterStop()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 setMediaPlayerToStartedState()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 setMediaPlayerToStartedStateAfterPause()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 setMediaPlayerToPausedState()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 setMediaPlayerToStoppedState()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 setMediaPlayerToPlaybackCompletedState()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 */ setMediaPlayerToErrorState()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 */ setMediaPlayerToState(MediaPlayerStateErrors.MediaPlayerState state)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 */ setStateError(MediaPlayerStateErrors.MediaPlayerState state, boolean error)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 notifyStateError()417 private void notifyStateError() { 418 mHandler.sendMessage(mHandler.obtainMessage(MediaPlayerStateErrors.MEDIA_PLAYER_ERROR)); 419 } 420 checkIdleState()421 private void checkIdleState() { 422 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.IDLE); 423 } 424 checkIdleStateAfterReset()425 private void checkIdleStateAfterReset() { 426 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.IDLE_AFTER_RESET); 427 } 428 checkInitializedState()429 private void checkInitializedState() { 430 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.INITIALIZED); 431 } 432 checkPreparedState()433 private void checkPreparedState() { 434 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PREPARED); 435 } 436 checkPreparedStateAfterStop()437 private void checkPreparedStateAfterStop() { 438 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PREPARED_AFTER_STOP); 439 } 440 checkStartedState()441 private void checkStartedState() { 442 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.STARTED); 443 } 444 checkPausedState()445 private void checkPausedState() { 446 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PAUSED); 447 } 448 checkStartedStateAfterPause()449 private void checkStartedStateAfterPause() { 450 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.STARTED_AFTER_PAUSE); 451 } 452 checkStoppedState()453 private void checkStoppedState() { 454 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.STOPPED); 455 } 456 checkPlaybackCompletedState()457 private void checkPlaybackCompletedState() { 458 callMediaPlayerMethodUnderTestInState(MediaPlayerStateErrors.MediaPlayerState.PLAYBACK_COMPLETED); 459 } 460 checkErrorState()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 */ checkMethodUnderTestInAllPossibleStates()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 */ terminateMessageLooper()491 private void terminateMessageLooper() { 492 mLooper.quit(); 493 mMediaPlayer.release(); 494 } 495 496 /* 497 * Cleans up all the internal object references. 498 */ cleanUp()499 private void cleanUp() { 500 mMediaPlayer = null; 501 mMediaPlayerState = null; 502 mLooper = null; 503 mStateErrors = null; 504 mMethodUnderTest = null; 505 } 506 } 507