/* * Copyright (C) 2008 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.mediaframeworktest.unit; import android.util.Log; import android.media.MediaRecorder; import android.test.AndroidTestCase; /** * A template class for running a method under test in all possible * states of a MediaRecorder object. * * @see com.android.mediaframeworktest.unit.MediaRecorderStopStateUnitTest * for an example of using this class. * * A typical concrete unit test class would implement the * MediaRecorderMethodUnderTest interface and have a reference to an object of * this class. Then it calls runTestOnMethod() to actually perform the unit * tests. It is recommended that the toString() method of the concrete unit test * class be overridden to use the actual method name under test for logging * purpose. * */ class MediaRecorderStateUnitTestTemplate extends AndroidTestCase { public static final String RECORD_OUTPUT_PATH = "/sdcard/recording.3gp"; public static final int OUTPUT_FORMAT= MediaRecorder.OutputFormat.THREE_GPP; public static final int AUDIO_ENCODER = MediaRecorder.AudioEncoder.AMR_NB; public static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC; private static final String TAG = "MediaRecorderStateUnitTest"; private MediaRecorderStateErrors mStateErrors = new MediaRecorderStateErrors(); private MediaRecorder mMediaRecorder = new MediaRecorder(); private MediaRecorderStateErrors.MediaRecorderState mMediaRecorderState = null; private MediaRecorderMethodUnderTest mMethodUnderTest = null; /** * Runs the given method under test in all possible states of a MediaRecorder * object. * * @param testMethod the method under test. */ public void runTestOnMethod(MediaRecorderMethodUnderTest testMethod) { mMethodUnderTest = testMethod; if (mMethodUnderTest != null) { // Method under test has been set? checkMethodUnderTestInAllPossibleStates(); mMethodUnderTest.checkStateErrors(mStateErrors); cleanUp(); } } /* * Calls method under test in the given state of the MediaRecorder object. * * @param state the MediaRecorder state in which the method under test is called. */ private void callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState state) { Log.v(TAG, "call " + mMethodUnderTest + ": started in state " + state); setMediaRecorderToState(state); try { mMethodUnderTest.invokeMethodUnderTest(mMediaRecorder); } catch(Exception e) { setStateError(mMediaRecorderState, true); } Log.v(TAG, "call " + mMethodUnderTest + ": ended in state " + state); } /* * The following setMediaRecorderToXXXStateXXX methods sets the MediaRecorder * object to the corresponding state, given the assumption that reset() * always resets the MediaRecorder object to Initial (after reset) state. */ private void setMediaRecorderToInitialStateAfterReset() { try { mMediaRecorder.reset(); } catch(Exception e) { fail("setMediaRecorderToInitialStateAfterReset: Exception " + e.getClass().getName() + " was thrown."); } } // FIXME: // In the past, stop() == reset(). // However, this is no longer true. The plan is to have a STOPPED state. // and from STOPPED state, start can be called without the need to // do the recording configuration again. private void setMediaRecorderToInitialStateAfterStop() { try { mMediaRecorder.reset(); /* mMediaRecorder.setAudioSource(AUDIO_SOURCE); mMediaRecorder.setOutputFormat(OUTPUT_FORMAT); mMediaRecorder.setAudioEncoder(AUDIO_ENCODER); mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH); mMediaRecorder.prepare(); mMediaRecorder.start(); mMediaRecorder.stop(); */ } catch(Exception e) { fail("setMediaRecorderToInitialStateAfterReset: Exception " + e.getClass().getName() + " was thrown."); } } private void setMediaRecorderToInitializedState() { try { mMediaRecorder.reset(); if (mMethodUnderTest.toString() != "setAudioSource()") { mMediaRecorder.setAudioSource(AUDIO_SOURCE); } } catch(Exception e) { fail("setMediaRecorderToInitializedState: Exception " + e.getClass().getName() + " was thrown."); } } private void setMediaRecorderToPreparedState() { try { mMediaRecorder.reset(); mMediaRecorder.setAudioSource(AUDIO_SOURCE); mMediaRecorder.setOutputFormat(OUTPUT_FORMAT); mMediaRecorder.setAudioEncoder(AUDIO_ENCODER); mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH); mMediaRecorder.prepare(); } catch(Exception e) { fail("setMediaRecorderToPreparedState: Exception " + e.getClass().getName() + " was thrown."); } } private void setMediaRecorderToRecordingState() { try { mMediaRecorder.reset(); mMediaRecorder.setAudioSource(AUDIO_SOURCE); mMediaRecorder.setOutputFormat(OUTPUT_FORMAT); mMediaRecorder.setAudioEncoder(AUDIO_ENCODER); mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH); mMediaRecorder.prepare(); mMediaRecorder.start(); } catch(Exception e) { fail("setMediaRecorderToRecordingState: Exception " + e.getClass().getName() + " was thrown."); } } private void setMediaRecorderToDataSourceConfiguredState() { try { mMediaRecorder.reset(); mMediaRecorder.setAudioSource(AUDIO_SOURCE); mMediaRecorder.setOutputFormat(OUTPUT_FORMAT); /* Skip setAudioEncoder() and setOutputFile() calls if * the method under test is setAudioEncoder() since this * method can only be called once even in the DATASOURCECONFIGURED state */ if (mMethodUnderTest.toString() != "setAudioEncoder()") { mMediaRecorder.setAudioEncoder(AUDIO_ENCODER); } if (mMethodUnderTest.toString() != "setOutputFile()") { mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH); } } catch(Exception e) { fail("setMediaRecorderToDataSourceConfiguredState: Exception " + e.getClass().getName() + " was thrown."); } } /* * There are a lot of ways to force the MediaRecorder object to enter * the Error state. We arbitrary choose one here. */ private void setMediaRecorderToErrorState() { try { mMediaRecorder.reset(); /* Skip setAudioSource() if the method under test is setAudioEncoder() * Because, otherwise, it is valid to call setAudioEncoder() after * start() since start() will fail, and then the mMediaRecorder * won't be set to the Error state */ if (mMethodUnderTest.toString() != "setAudioEncoder()") { mMediaRecorder.setAudioSource(AUDIO_SOURCE); } /* Skip setOutputFormat if the method under test is setOutputFile() * Because, otherwise, it is valid to call setOutputFile() after * start() since start() will fail, and then the mMediaRecorder * won't be set to the Error state */ if (mMethodUnderTest.toString() != "setOutputFile()") { mMediaRecorder.setOutputFormat(OUTPUT_FORMAT); } mMediaRecorder.start(); } catch(Exception e) { if (!(e instanceof IllegalStateException)) { fail("setMediaRecorderToErrorState: Exception " + e.getClass().getName() + " was thrown."); } } Log.v(TAG, "setMediaRecorderToErrorState: done."); } /* * Sets the state of the MediaRecorder object to the specified one. * * @param state the state of the MediaRecorder object. */ private void setMediaRecorderToState(MediaRecorderStateErrors.MediaRecorderState state) { mMediaRecorderState = state; switch(state) { case INITIAL: // Does nothing. break; case INITIAL_AFTER_RESET: setMediaRecorderToInitialStateAfterReset(); break; case INITIAL_AFTER_STOP: setMediaRecorderToInitialStateAfterStop(); break; case INITIALIZED: setMediaRecorderToInitializedState(); break; case DATASOURCECONFIGURED: setMediaRecorderToDataSourceConfiguredState(); break; case PREPARED: setMediaRecorderToPreparedState(); break; case RECORDING: setMediaRecorderToRecordingState(); break; case ERROR: setMediaRecorderToErrorState(); break; } } /* * Sets the error value of the corresponding state to the given error. * * @param state the state of the MediaRecorder object. * @param error the value of the state error to be set. */ private void setStateError(MediaRecorderStateErrors.MediaRecorderState state, boolean error) { switch(state) { case INITIAL: mStateErrors.errorInInitialState = error; break; case INITIAL_AFTER_RESET: mStateErrors.errorInInitialStateAfterReset = error; break; case INITIAL_AFTER_STOP: mStateErrors.errorInInitialStateAfterStop = error; break; case INITIALIZED: mStateErrors.errorInInitializedState = error; break; case DATASOURCECONFIGURED: mStateErrors.errorInDataSourceConfiguredState = error; break; case PREPARED: mStateErrors.errorInPreparedState = error; break; case RECORDING: mStateErrors.errorInRecordingState = error; break; case ERROR: mStateErrors.errorInErrorState = error; break; } } private void checkInitialState() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIAL); } private void checkInitialStateAfterReset() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIAL_AFTER_RESET); } private void checkInitialStateAfterStop() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIAL_AFTER_STOP); } private void checkInitializedState() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIALIZED); } private void checkPreparedState() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.PREPARED); } private void checkRecordingState() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.RECORDING); } private void checkDataSourceConfiguredState() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.DATASOURCECONFIGURED); } private void checkErrorState() { callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.ERROR); } /* * Checks the given method under test in all possible states of the MediaRecorder object. */ private void checkMethodUnderTestInAllPossibleStates() { // Must be called first. checkInitialState(); // The sequence of the following method calls should not // affect the test results. checkErrorState(); checkInitialStateAfterReset(); checkInitialStateAfterStop(); checkInitializedState(); checkRecordingState(); checkDataSourceConfiguredState(); checkPreparedState(); } /* * Cleans up all the internal object references. */ private void cleanUp() { mMediaRecorder.release(); mMediaRecorder = null; mMediaRecorderState = null; mStateErrors = null; mMethodUnderTest = null; } }