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.Log; 21 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; 22 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.result.BugreportCollector; 26 import com.android.tradefed.result.BugreportCollector.Freq; 27 import com.android.tradefed.result.BugreportCollector.Noun; 28 import com.android.tradefed.result.BugreportCollector.Relation; 29 import com.android.tradefed.result.FileInputStreamSource; 30 import com.android.tradefed.result.ITestInvocationListener; 31 import com.android.tradefed.result.InputStreamSource; 32 import com.android.tradefed.result.LogDataType; 33 import com.android.tradefed.testtype.IDeviceTest; 34 import com.android.tradefed.testtype.IRemoteTest; 35 import com.android.tradefed.util.FileUtil; 36 import com.android.tradefed.util.RegexTrie; 37 import com.android.tradefed.util.StreamUtil; 38 39 import org.junit.Assert; 40 41 import java.io.File; 42 import java.io.FileInputStream; 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.HashMap; 48 import java.util.List; 49 import java.util.ListIterator; 50 import java.util.Map; 51 import java.util.concurrent.TimeUnit; 52 53 /** 54 * Runs the Video Editing Framework Performance Test.The performance test result 55 * is saved in /sdcard/VideoEditorPerformance.txt 56 * <p/> 57 * Note that this test will not run properly unless /sdcard is mounted and 58 * writable. 59 */ 60 public class VideoEditingPerformanceTest implements IDeviceTest, IRemoteTest { 61 private static final String LOG_TAG = "VideoEditingPerformanceTest"; 62 63 ITestDevice mTestDevice = null; 64 65 private static final String METRICS_RUN_NAME = "VideoEditor"; 66 67 //Max test timeout - 3 hrs 68 private static final int MAX_TEST_TIMEOUT = 3 * 60 * 60 * 1000; 69 70 // Constants for running the tests 71 private static final String TEST_CLASS_NAME = 72 "com.android.mediaframeworktest.performance.VideoEditorPerformance"; 73 private static final String TEST_PACKAGE_NAME = "com.android.mediaframeworktest"; 74 private static final String TEST_RUNNER_NAME = ".MediaFrameworkPerfTestRunner"; 75 76 private static final String OUTPUT_PATH = "VideoEditorPerformance.txt"; 77 78 private final RegexTrie<String> mPatternMap = new RegexTrie<>(); 79 80 public VideoEditingPerformanceTest() { 81 mPatternMap.put("ImageItemCreate", 82 "^.*Time taken to Create Media Image Item :(\\d+)"); 83 mPatternMap.put("mageItemAdd", 84 "^.*Time taken to add Media Image Item :(\\d+)"); 85 mPatternMap.put("ImageItemRemove", 86 "^.*Time taken to remove Media Image Item :(\\d+)"); 87 mPatternMap.put("ImageItemCreate640x480", 88 "^.*Time taken to Create Media Image Item.*640x480.*:(\\d+)"); 89 mPatternMap.put("ImageItemAdd640x480", 90 "^.*Time taken to add Media Image Item.*640x480.*:(\\d+)"); 91 mPatternMap.put("ImageItemRemove640x480", 92 "^.*Time taken to remove Media Image Item.*640x480.*:(\\d+)"); 93 mPatternMap.put("CrossFadeTransitionCreate", 94 "^.*Time taken to Create CrossFade Transition :(\\d+)"); 95 mPatternMap.put("CrossFadeTransitionAdd", 96 "^.*Time taken to add CrossFade Transition :(\\d+)"); 97 mPatternMap.put("CrossFadeTransitionRemove", 98 "^.*Time taken to remove CrossFade Transition :(\\d+)"); 99 mPatternMap.put("VideoItemCreate", 100 "^.*Time taken to Create Media Video Item :(\\d+)"); 101 mPatternMap.put("VideoItemAdd", 102 "^.*Time taken to Add Media Video Item :(\\d+)"); 103 mPatternMap.put("VideoItemRemove", 104 "^.*Time taken to remove Media Video Item :(\\d+)"); 105 mPatternMap.put("EffectOverlappingTransition", 106 "^.*Time taken to testPerformanceEffectOverlappingTransition :(\\d+.\\d+)"); 107 mPatternMap.put("ExportStoryboard", 108 "^.*Time taken to do ONE export of storyboard duration 69000 is :(\\d+)"); 109 mPatternMap.put("PreviewWithTransition", 110 "^.*Time taken to Generate Preview with transition :(\\d+.\\d+)"); 111 mPatternMap.put("OverlayCreate", 112 "^.*Time taken to add & create Overlay :(\\d+)"); 113 mPatternMap.put("OverlayRemove", 114 "^.*Time taken to remove Overlay :(\\d+)"); 115 mPatternMap.put("GetVideoThumbnails", 116 "^.*Duration taken to get Video Thumbnails :(\\d+)"); 117 mPatternMap.put("TransitionWithEffectOverlapping", 118 "^.*Time taken to TransitionWithEffectOverlapping :(\\d+.\\d+)"); 119 mPatternMap.put("MediaPropertiesGet", 120 "^.*Time taken to get Media Properties :(\\d+)"); 121 mPatternMap.put("AACLCAdd", 122 "^.*Time taken for 1st Audio Track.*AACLC.*:(\\d+)"); 123 mPatternMap.put("AMRNBAdd", 124 "^.*Time taken for 2nd Audio Track.*AMRNB.*:(\\d+)"); 125 mPatternMap.put("KenBurnGeneration", 126 "^.*Time taken to Generate KenBurn Effect :(\\d+.\\d+)"); 127 mPatternMap.put("ThumbnailsGeneration", 128 "^.*Time taken Thumbnail generation :(\\d+.\\d+)"); 129 mPatternMap.put("H264ThumbnailGeneration", 130 "^.*Time taken for Thumbnail generation :(\\d+.\\d+)"); 131 } 132 133 @Override 134 public void run(ITestInvocationListener listener) 135 throws DeviceNotAvailableException { 136 Assert.assertNotNull(mTestDevice); 137 138 IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner( 139 TEST_PACKAGE_NAME, TEST_RUNNER_NAME, mTestDevice.getIDevice()); 140 runner.setClassName(TEST_CLASS_NAME); 141 runner.setMaxTimeToOutputResponse(MAX_TEST_TIMEOUT, TimeUnit.MILLISECONDS); 142 143 BugreportCollector bugListener = new BugreportCollector(listener, 144 mTestDevice); 145 bugListener.addPredicate(new BugreportCollector.Predicate( 146 Relation.AFTER, Freq.EACH, Noun.TESTRUN)); 147 148 mTestDevice.runInstrumentationTests(runner, bugListener); 149 150 logOutputFiles(listener); 151 cleanResultFile(); 152 } 153 154 /** 155 * Clean up the test result file from test run 156 */ 157 private void cleanResultFile() throws DeviceNotAvailableException { 158 String extStore = 159 mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 160 mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, OUTPUT_PATH)); 161 } 162 163 /** 164 * Pull the output files from the device, add it to the logs, and also parse 165 * out the relevant test metrics and report them. 166 */ 167 private void logOutputFiles(ITestInvocationListener listener) 168 throws DeviceNotAvailableException { 169 File outputFile = null; 170 InputStreamSource outputSource = null; 171 try { 172 outputFile = mTestDevice.pullFileFromExternal(OUTPUT_PATH); 173 174 if (outputFile == null) { 175 return; 176 } 177 178 // Upload a verbatim copy of the output file 179 Log.d(LOG_TAG, String.format( 180 "Sending %d byte file %s into the logosphere!", 181 outputFile.length(), outputFile)); 182 outputSource = new FileInputStreamSource(outputFile); 183 listener.testLog(OUTPUT_PATH, LogDataType.TEXT, outputSource); 184 185 // Parse the output file to upload aggregated metrics 186 parseOutputFile(new FileInputStream(outputFile), listener); 187 } catch (IOException e) { 188 Log.e(LOG_TAG, String.format( 189 "IOException while reading or parsing output file: %s", e)); 190 } finally { 191 FileUtil.deleteFile(outputFile); 192 StreamUtil.cancel(outputSource); 193 } 194 } 195 196 /** 197 * Parse the relevant metrics from the Instrumentation test output file 198 */ 199 private void parseOutputFile(InputStream dataStream, 200 ITestInvocationListener listener) { 201 202 Map<String, String> runMetrics = new HashMap<>(); 203 204 // try to parse it 205 String contents; 206 try { 207 contents = StreamUtil.getStringFromStream(dataStream); 208 } catch (IOException e) { 209 Log.e(LOG_TAG, String.format( 210 "Got IOException during test processing: %s", e)); 211 return; 212 } 213 214 List<String> lines = Arrays.asList(contents.split("\n")); 215 ListIterator<String> lineIter = lines.listIterator(); 216 String line; 217 while (lineIter.hasNext()) { 218 line = lineIter.next(); 219 List<List<String>> capture = new ArrayList<>(1); 220 String key = mPatternMap.retrieve(capture, line); 221 if (key != null) { 222 Log.d(LOG_TAG, String.format("Got '%s' and captures '%s'", key, 223 capture.toString())); 224 } else if (line.isEmpty()) { 225 // ignore 226 continue; 227 } else { 228 Log.e(LOG_TAG, String.format("Got unmatched line: %s", line)); 229 continue; 230 } 231 runMetrics.put(key, capture.get(0).get(0)); 232 } 233 reportMetrics(listener, runMetrics); 234 } 235 236 /** 237 * Report run metrics by creating an empty test run to stick them in 238 * <p /> 239 * Exposed for unit testing 240 */ 241 void reportMetrics(ITestInvocationListener listener, 242 Map<String, String> metrics) { 243 Log.d(LOG_TAG, String.format("About to report metrics: %s", metrics)); 244 listener.testRunStarted(METRICS_RUN_NAME, 0); 245 listener.testRunEnded(0, metrics); 246 } 247 248 @Override 249 public void setDevice(ITestDevice device) { 250 mTestDevice = device; 251 } 252 253 @Override 254 public ITestDevice getDevice() { 255 return mTestDevice; 256 } 257 } 258