1 /* 2 * Copyright (C) 2011 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.media.tests; 18 19 import com.android.ddmlib.IDevice; 20 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; 21 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 22 import com.android.tradefed.device.DeviceNotAvailableException; 23 import com.android.tradefed.device.ITestDevice; 24 import com.android.tradefed.log.LogUtil.CLog; 25 import com.android.tradefed.result.FileInputStreamSource; 26 import com.android.tradefed.result.ITestInvocationListener; 27 import com.android.tradefed.result.InputStreamSource; 28 import com.android.tradefed.result.LogDataType; 29 import com.android.tradefed.testtype.IDeviceTest; 30 import com.android.tradefed.testtype.IRemoteTest; 31 import com.android.tradefed.util.FileUtil; 32 import com.android.tradefed.util.StreamUtil; 33 34 import org.junit.Assert; 35 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.IOException; 39 import java.io.InputStream; 40 import java.util.Arrays; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.ListIterator; 44 import java.util.Map; 45 import java.util.concurrent.TimeUnit; 46 import java.util.regex.Matcher; 47 import java.util.regex.Pattern; 48 49 /** 50 * Runs the Media stress testcases. 51 * FIXME: more details 52 * <p/> 53 * Note that this test will not run properly unless /sdcard is mounted and writable. 54 */ 55 public class MediaStressTest implements IDeviceTest, IRemoteTest { 56 57 ITestDevice mTestDevice = null; 58 private static final String METRICS_RUN_NAME = "VideoRecordingStress"; 59 60 //Max test timeout - 2 hrs 61 private static final int MAX_TEST_TIMEOUT = 2 * 60 * 60 * 1000; 62 63 // Constants for running the tests 64 private static final String TEST_CLASS_NAME = 65 "com.android.mediaframeworktest.stress.MediaRecorderStressTest"; 66 private static final String TEST_PACKAGE_NAME = "com.android.mediaframeworktest"; 67 private static final String TEST_RUNNER_NAME = ".MediaRecorderStressTestRunner"; 68 69 // Constants for parsing the output file 70 private static final Pattern EXPECTED_LOOP_COUNT_PATTERN = 71 Pattern.compile("Total number of loops:\\s*(\\d+)"); 72 private static final Pattern ACTUAL_LOOP_COUNT_PATTERN = 73 Pattern.compile("No of loop:.*,\\s*(\\d+)\\s*"); 74 private static final String OUTPUT_PATH = "mediaStressOutput.txt"; 75 76 @Override 77 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 78 Assert.assertNotNull(mTestDevice); 79 80 IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(TEST_PACKAGE_NAME, 81 TEST_RUNNER_NAME, mTestDevice.getIDevice()); 82 runner.setClassName(TEST_CLASS_NAME); 83 runner.setMaxTimeToOutputResponse(MAX_TEST_TIMEOUT, TimeUnit.MILLISECONDS); 84 85 cleanTmpFiles(); 86 mTestDevice.runInstrumentationTests(runner, listener); 87 logOutputFile(listener); 88 cleanTmpFiles(); 89 } 90 91 /** 92 * Clean up temp files from test runs 93 */ 94 private void cleanTmpFiles() throws DeviceNotAvailableException { 95 String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 96 mTestDevice.executeShellCommand(String.format("rm %s/temp*.3gp", extStore)); 97 mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, OUTPUT_PATH)); 98 } 99 100 /** 101 * Pull the output file from the device, add it to the logs, and also parse out the relevant 102 * test metrics and report them. 103 */ 104 private void logOutputFile(ITestInvocationListener listener) 105 throws DeviceNotAvailableException { 106 File outputFile = null; 107 InputStreamSource outputSource = null; 108 try { 109 outputFile = mTestDevice.pullFileFromExternal(OUTPUT_PATH); 110 111 if (outputFile == null) { 112 return; 113 } 114 115 CLog.d("Sending %d byte file %s into the logosphere!", outputFile.length(), outputFile); 116 outputSource = new FileInputStreamSource(outputFile); 117 listener.testLog(OUTPUT_PATH, LogDataType.TEXT, outputSource); 118 parseOutputFile(outputFile, listener); 119 } finally { 120 FileUtil.deleteFile(outputFile); 121 StreamUtil.cancel(outputSource); 122 } 123 } 124 125 /** 126 * Parse the relevant metrics from the Instrumentation test output file 127 */ 128 private void parseOutputFile(File outputFile, ITestInvocationListener listener) { 129 Map<String, String> runMetrics = new HashMap<>(); 130 Map<String, String> stanzaKeyMap = new HashMap<>(); 131 stanzaKeyMap.put("testStressRecordVideoAndPlayback1080P", "VideoRecordPlayback1080P"); 132 stanzaKeyMap.put("testStressRecordVideoAndPlayback720P", "VideoRecordPlayback720P"); 133 stanzaKeyMap.put("testStressRecordVideoAndPlayback480P", "VideoRecordPlayback480P"); 134 stanzaKeyMap.put("testStressTimeLapse", "TimeLapseRecord"); 135 136 // try to parse it 137 String contents; 138 try { 139 InputStream dataStream = new FileInputStream(outputFile); 140 contents = StreamUtil.getStringFromStream(dataStream); 141 } catch (IOException e) { 142 CLog.e("IOException while parsing the output file:"); 143 CLog.e(e); 144 return; 145 } 146 147 List<String> lines = Arrays.asList(contents.split("\n")); 148 ListIterator<String> lineIter = lines.listIterator(); 149 String line; 150 while (lineIter.hasNext()) { 151 line = lineIter.next(); 152 String key = null; 153 154 if (stanzaKeyMap.containsKey(line)) { 155 key = stanzaKeyMap.get(line); 156 } else { 157 CLog.d("Got unexpected line: %s", line); 158 continue; 159 } 160 161 Integer countExpected = getIntFromOutput(lineIter, EXPECTED_LOOP_COUNT_PATTERN); 162 Integer countActual = getIntFromOutput(lineIter, ACTUAL_LOOP_COUNT_PATTERN); 163 int value = coalesceLoopCounts(countActual, countExpected); 164 runMetrics.put(key, Integer.toString(value)); 165 } 166 167 reportMetrics(listener, runMetrics); 168 } 169 170 /** 171 * Report run metrics by creating an empty test run to stick them in 172 * <p /> 173 * Exposed for unit testing 174 */ 175 void reportMetrics(ITestInvocationListener listener, Map<String, String> metrics) { 176 // Create an empty testRun to report the parsed runMetrics 177 CLog.d("About to report metrics: %s", metrics); 178 listener.testRunStarted(METRICS_RUN_NAME, 0); 179 listener.testRunEnded(0, metrics); 180 } 181 182 /** 183 * Use the provided {@link Pattern} to parse a number out of the output file 184 */ 185 private Integer getIntFromOutput(ListIterator<String> lineIter, Pattern numPattern) { 186 Integer retval = null; 187 String line = null; 188 if (lineIter.hasNext()) { 189 line = lineIter.next(); 190 Matcher m = numPattern.matcher(line); 191 if (m.matches()) { 192 retval = Integer.parseInt(m.group(1)); 193 } else { 194 CLog.e("Couldn't match pattern %s against line '%s'", numPattern, line); 195 } 196 } else { 197 CLog.e("Encounted EOF while trying to match pattern %s", numPattern); 198 } 199 200 return retval; 201 } 202 203 /** 204 * Given an actual and an expected iteration count, determine a single metric to report. 205 */ 206 private int coalesceLoopCounts(Integer actual, Integer expected) { 207 if (expected == null || expected <= 0) { 208 return -1; 209 } else if (actual == null) { 210 return expected; 211 } else { 212 return actual; 213 } 214 } 215 216 @Override 217 public void setDevice(ITestDevice device) { 218 mTestDevice = device; 219 } 220 221 @Override 222 public ITestDevice getDevice() { 223 return mTestDevice; 224 } 225 } 226