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.media.MediaRecorder;
21 import android.test.AndroidTestCase;
22 
23 /**
24  * A template class for running a method under test in all possible
25  * states of a MediaRecorder object.
26  *
27  * @see com.android.mediaframeworktest.unit.MediaRecorderStopStateUnitTest
28  * for an example of using this class.
29  *
30  * A typical concrete unit test class would implement the
31  * MediaRecorderMethodUnderTest interface and have a reference to an object of
32  * this class. Then it calls runTestOnMethod() to actually perform the unit
33  * tests. It is recommended that the toString() method of the concrete unit test
34  * class be overridden to use the actual method name under test for logging
35  * purpose.
36  *
37  */
38 class MediaRecorderStateUnitTestTemplate extends AndroidTestCase {
39     public static final String RECORD_OUTPUT_PATH = "/sdcard/recording.3gp";
40     public static final int OUTPUT_FORMAT= MediaRecorder.OutputFormat.THREE_GPP;
41     public static final int AUDIO_ENCODER = MediaRecorder.AudioEncoder.AMR_NB;
42     public static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
43     private static final String TAG = "MediaRecorderStateUnitTest";
44     private MediaRecorderStateErrors mStateErrors = new MediaRecorderStateErrors();
45     private MediaRecorder mMediaRecorder = new MediaRecorder();
46     private MediaRecorderStateErrors.MediaRecorderState mMediaRecorderState = null;
47     private MediaRecorderMethodUnderTest mMethodUnderTest = null;
48 
49     /**
50      * Runs the given method under test in all possible states of a MediaRecorder
51      * object.
52      *
53      * @param testMethod the method under test.
54      */
runTestOnMethod(MediaRecorderMethodUnderTest testMethod)55     public void runTestOnMethod(MediaRecorderMethodUnderTest testMethod) {
56         mMethodUnderTest = testMethod;
57         if (mMethodUnderTest != null) {  // Method under test has been set?
58             checkMethodUnderTestInAllPossibleStates();
59             mMethodUnderTest.checkStateErrors(mStateErrors);
60             cleanUp();
61         }
62     }
63 
64     /*
65      * Calls method under test in the given state of the MediaRecorder object.
66      *
67      * @param state the MediaRecorder state in which the method under test is called.
68      */
callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState state)69     private void callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState state) {
70         Log.v(TAG, "call " + mMethodUnderTest + ": started in state " + state);
71         setMediaRecorderToState(state);
72         try {
73             mMethodUnderTest.invokeMethodUnderTest(mMediaRecorder);
74         } catch(Exception e) {
75             setStateError(mMediaRecorderState, true);
76         }
77         Log.v(TAG, "call " + mMethodUnderTest + ": ended in state " + state);
78     }
79 
80     /*
81      * The following setMediaRecorderToXXXStateXXX methods sets the MediaRecorder
82      * object to the corresponding state, given the assumption that reset()
83      * always resets the MediaRecorder object to Initial (after reset) state.
84      */
setMediaRecorderToInitialStateAfterReset()85     private void setMediaRecorderToInitialStateAfterReset() {
86         try {
87             mMediaRecorder.reset();
88         } catch(Exception e) {
89             fail("setMediaRecorderToInitialStateAfterReset: Exception " + e.getClass().getName() + " was thrown.");
90         }
91     }
92 
93     // FIXME:
94     // In the past, stop() == reset().
95     // However, this is no longer true. The plan is to have a STOPPED state.
96     // and from STOPPED state, start can be called without the need to
97     // do the recording configuration again.
setMediaRecorderToInitialStateAfterStop()98     private void setMediaRecorderToInitialStateAfterStop() {
99         try {
100             mMediaRecorder.reset();
101 /*
102             mMediaRecorder.setAudioSource(AUDIO_SOURCE);
103             mMediaRecorder.setOutputFormat(OUTPUT_FORMAT);
104             mMediaRecorder.setAudioEncoder(AUDIO_ENCODER);
105             mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH);
106             mMediaRecorder.prepare();
107             mMediaRecorder.start();
108             mMediaRecorder.stop();
109 */
110         } catch(Exception e) {
111             fail("setMediaRecorderToInitialStateAfterReset: Exception " + e.getClass().getName() + " was thrown.");
112         }
113     }
114 
setMediaRecorderToInitializedState()115     private void setMediaRecorderToInitializedState() {
116         try {
117             mMediaRecorder.reset();
118             if (mMethodUnderTest.toString() != "setAudioSource()") {
119                 mMediaRecorder.setAudioSource(AUDIO_SOURCE);
120             }
121         } catch(Exception e) {
122             fail("setMediaRecorderToInitializedState: Exception " + e.getClass().getName() + " was thrown.");
123         }
124     }
125 
setMediaRecorderToPreparedState()126     private void setMediaRecorderToPreparedState() {
127         try {
128             mMediaRecorder.reset();
129             mMediaRecorder.setAudioSource(AUDIO_SOURCE);
130             mMediaRecorder.setOutputFormat(OUTPUT_FORMAT);
131             mMediaRecorder.setAudioEncoder(AUDIO_ENCODER);
132             mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH);
133             mMediaRecorder.prepare();
134         } catch(Exception e) {
135             fail("setMediaRecorderToPreparedState: Exception " + e.getClass().getName() + " was thrown.");
136         }
137     }
138 
setMediaRecorderToRecordingState()139     private void setMediaRecorderToRecordingState() {
140         try {
141             mMediaRecorder.reset();
142             mMediaRecorder.setAudioSource(AUDIO_SOURCE);
143             mMediaRecorder.setOutputFormat(OUTPUT_FORMAT);
144             mMediaRecorder.setAudioEncoder(AUDIO_ENCODER);
145             mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH);
146             mMediaRecorder.prepare();
147             mMediaRecorder.start();
148         } catch(Exception e) {
149             fail("setMediaRecorderToRecordingState: Exception " + e.getClass().getName() + " was thrown.");
150         }
151     }
152 
setMediaRecorderToDataSourceConfiguredState()153     private void setMediaRecorderToDataSourceConfiguredState() {
154         try {
155             mMediaRecorder.reset();
156             mMediaRecorder.setAudioSource(AUDIO_SOURCE);
157             mMediaRecorder.setOutputFormat(OUTPUT_FORMAT);
158 
159             /* Skip setAudioEncoder() and setOutputFile() calls if
160              * the method under test is setAudioEncoder() since this
161              * method can only be called once even in the DATASOURCECONFIGURED state
162              */
163             if (mMethodUnderTest.toString() != "setAudioEncoder()") {
164                 mMediaRecorder.setAudioEncoder(AUDIO_ENCODER);
165             }
166 
167             if (mMethodUnderTest.toString() != "setOutputFile()") {
168                 mMediaRecorder.setOutputFile(RECORD_OUTPUT_PATH);
169             }
170         } catch(Exception e) {
171             fail("setMediaRecorderToDataSourceConfiguredState: Exception " + e.getClass().getName() + " was thrown.");
172         }
173     }
174 
175     /*
176      * There are a lot of ways to force the MediaRecorder object to enter
177      * the Error state. We arbitrary choose one here.
178      */
setMediaRecorderToErrorState()179     private void setMediaRecorderToErrorState() {
180         try {
181             mMediaRecorder.reset();
182 
183             /* Skip setAudioSource() if the method under test is setAudioEncoder()
184              * Because, otherwise, it is valid to call setAudioEncoder() after
185              * start() since start() will fail, and then the mMediaRecorder
186              * won't be set to the Error state
187              */
188             if (mMethodUnderTest.toString() != "setAudioEncoder()") {
189                 mMediaRecorder.setAudioSource(AUDIO_SOURCE);
190             }
191 
192             /* Skip setOutputFormat if the method under test is setOutputFile()
193              *  Because, otherwise, it is valid to call setOutputFile() after
194              * start() since start() will fail, and then the mMediaRecorder
195              * won't be set to the Error state
196              */
197             if (mMethodUnderTest.toString() != "setOutputFile()") {
198                 mMediaRecorder.setOutputFormat(OUTPUT_FORMAT);
199             }
200 
201             mMediaRecorder.start();
202         } catch(Exception e) {
203             if (!(e instanceof IllegalStateException)) {
204                 fail("setMediaRecorderToErrorState: Exception " + e.getClass().getName() + " was thrown.");
205             }
206         }
207         Log.v(TAG, "setMediaRecorderToErrorState: done.");
208     }
209 
210     /*
211      * Sets the state of the MediaRecorder object to the specified one.
212      *
213      * @param state the state of the MediaRecorder object.
214      */
setMediaRecorderToState(MediaRecorderStateErrors.MediaRecorderState state)215     private void setMediaRecorderToState(MediaRecorderStateErrors.MediaRecorderState state) {
216         mMediaRecorderState = state;
217         switch(state) {
218             case INITIAL:
219                 // Does nothing.
220                 break;
221             case INITIAL_AFTER_RESET:
222                 setMediaRecorderToInitialStateAfterReset();
223                 break;
224             case INITIAL_AFTER_STOP:
225                 setMediaRecorderToInitialStateAfterStop();
226                 break;
227             case INITIALIZED:
228                 setMediaRecorderToInitializedState();
229                 break;
230             case DATASOURCECONFIGURED:
231                 setMediaRecorderToDataSourceConfiguredState();
232                 break;
233             case PREPARED:
234                 setMediaRecorderToPreparedState();
235                 break;
236             case RECORDING:
237                 setMediaRecorderToRecordingState();
238                 break;
239             case ERROR:
240                 setMediaRecorderToErrorState();
241                 break;
242         }
243     }
244 
245     /*
246      * Sets the error value of the corresponding state to the given error.
247      *
248      * @param state the state of the MediaRecorder object.
249      * @param error the value of the state error to be set.
250      */
setStateError(MediaRecorderStateErrors.MediaRecorderState state, boolean error)251     private void setStateError(MediaRecorderStateErrors.MediaRecorderState state, boolean error) {
252         switch(state) {
253             case INITIAL:
254                 mStateErrors.errorInInitialState = error;
255                 break;
256             case INITIAL_AFTER_RESET:
257                 mStateErrors.errorInInitialStateAfterReset = error;
258                 break;
259             case INITIAL_AFTER_STOP:
260                 mStateErrors.errorInInitialStateAfterStop = error;
261                 break;
262             case INITIALIZED:
263                 mStateErrors.errorInInitializedState = error;
264                 break;
265             case DATASOURCECONFIGURED:
266                 mStateErrors.errorInDataSourceConfiguredState = error;
267                 break;
268             case PREPARED:
269                 mStateErrors.errorInPreparedState = error;
270                 break;
271             case RECORDING:
272                 mStateErrors.errorInRecordingState = error;
273                 break;
274             case ERROR:
275                 mStateErrors.errorInErrorState = error;
276                 break;
277         }
278     }
279 
checkInitialState()280     private void checkInitialState() {
281         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIAL);
282     }
283 
checkInitialStateAfterReset()284     private void checkInitialStateAfterReset() {
285         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIAL_AFTER_RESET);
286     }
287 
checkInitialStateAfterStop()288     private void checkInitialStateAfterStop() {
289         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIAL_AFTER_STOP);
290     }
291 
checkInitializedState()292     private void checkInitializedState() {
293         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.INITIALIZED);
294     }
295 
checkPreparedState()296     private void checkPreparedState() {
297         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.PREPARED);
298     }
299 
checkRecordingState()300     private void checkRecordingState() {
301         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.RECORDING);
302     }
303 
checkDataSourceConfiguredState()304     private void checkDataSourceConfiguredState() {
305         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.DATASOURCECONFIGURED);
306     }
307 
checkErrorState()308     private void checkErrorState() {
309         callMediaRecorderMethodUnderTestInState(MediaRecorderStateErrors.MediaRecorderState.ERROR);
310     }
311 
312     /*
313      * Checks the given method under test in all possible states of the MediaRecorder object.
314      */
checkMethodUnderTestInAllPossibleStates()315     private void checkMethodUnderTestInAllPossibleStates() {
316         // Must be called first.
317         checkInitialState();
318 
319         // The sequence of the following method calls should not
320         // affect the test results.
321         checkErrorState();
322         checkInitialStateAfterReset();
323         checkInitialStateAfterStop();
324         checkInitializedState();
325         checkRecordingState();
326         checkDataSourceConfiguredState();
327         checkPreparedState();
328     }
329 
330     /*
331      * Cleans up all the internal object references.
332      */
cleanUp()333     private void cleanUp() {
334         mMediaRecorder.release();
335         mMediaRecorder = null;
336         mMediaRecorderState = null;
337         mStateErrors = null;
338         mMethodUnderTest = null;
339     }
340 }
341