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