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.config.Option; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.result.BugreportCollector; 27 import com.android.tradefed.result.BugreportCollector.Freq; 28 import com.android.tradefed.result.BugreportCollector.Noun; 29 import com.android.tradefed.result.BugreportCollector.Relation; 30 import com.android.tradefed.result.FileInputStreamSource; 31 import com.android.tradefed.result.ITestInvocationListener; 32 import com.android.tradefed.result.InputStreamSource; 33 import com.android.tradefed.result.LogDataType; 34 import com.android.tradefed.testtype.IDeviceTest; 35 import com.android.tradefed.testtype.IRemoteTest; 36 import com.android.tradefed.util.FileUtil; 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.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 import java.util.regex.Matcher; 53 import java.util.regex.Pattern; 54 55 /** 56 * Runs the Video Editing Framework Memory Tests. This test exercise the basic 57 * functionality of video editing test and capture the memory usage. The memory 58 * usage test out is saved in /sdcard/VideoEditorStressMemOutput.txt and 59 * VideoEditorMediaServerMemoryLog.txt. 60 * <p/> 61 * Note that this test will not run properly unless /sdcard is mounted and 62 * writable. 63 */ 64 public class VideoEditingMemoryTest implements IDeviceTest, IRemoteTest { 65 private static final String LOG_TAG = "VideoEditorMemoryTest"; 66 67 ITestDevice mTestDevice = null; 68 69 // Constants for running the tests 70 private static final String TEST_CLASS_NAME = 71 "com.android.mediaframeworktest.stress.VideoEditorStressTest"; 72 private static final String TEST_PACKAGE_NAME = "com.android.mediaframeworktest"; 73 private static final String TEST_RUNNER_NAME = ".MediaPlayerStressTestRunner"; 74 75 //Max test timeout - 3 hrs 76 private static final int MAX_TEST_TIMEOUT = 3 * 60 * 60 * 1000; 77 78 /* 79 * Pattern to find the test case name and test result. 80 * Example of a matching line: 81 * testStressAddRemoveEffects total diff = 0 82 * The first string 'testStressAddRemoveEffects' represent the dashboard key and 83 * the last string represent the test result. 84 */ 85 public static final Pattern TOTAL_MEM_DIFF_PATTERN = 86 Pattern.compile("(.+?)\\s.*diff.*\\s(-?\\d+)"); 87 88 public Map<String, String> mRunMetrics = new HashMap<>(); 89 public Map<String, String> mKeyMap = new HashMap<>(); 90 91 @Option(name = "getHeapDump", description = "Collect the heap") 92 private boolean mGetHeapDump = false; 93 VideoEditingMemoryTest()94 public VideoEditingMemoryTest() { 95 mKeyMap.put("VideoEditorStressMemOutput.txt", "VideoEditorMemory"); 96 mKeyMap.put("VideoEditorMediaServerMemoryLog.txt", 97 "VideoEditorMemoryMediaServer"); 98 } 99 100 101 @Override run(ITestInvocationListener listener)102 public void run(ITestInvocationListener listener) 103 throws DeviceNotAvailableException { 104 Assert.assertNotNull(mTestDevice); 105 106 IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner( 107 TEST_PACKAGE_NAME, TEST_RUNNER_NAME, mTestDevice.getIDevice()); 108 runner.setClassName(TEST_CLASS_NAME); 109 runner.setMaxTimeToOutputResponse(MAX_TEST_TIMEOUT, TimeUnit.MILLISECONDS); 110 if (mGetHeapDump) { 111 runner.addInstrumentationArg("get_heap_dump", "getNativeHeap"); 112 } 113 114 BugreportCollector bugListener = new BugreportCollector(listener, 115 mTestDevice); 116 bugListener.addPredicate(new BugreportCollector.Predicate( 117 Relation.AFTER, Freq.EACH, Noun.TESTRUN)); 118 119 mTestDevice.runInstrumentationTests(runner, bugListener); 120 121 logOutputFiles(listener); 122 cleanResultFile(); 123 } 124 125 /** 126 * Clean up the test result file from test run 127 */ cleanResultFile()128 private void cleanResultFile() throws DeviceNotAvailableException { 129 String extStore = 130 mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 131 for(String outFile : mKeyMap.keySet()) { 132 mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, 133 outFile)); 134 } 135 if (mGetHeapDump) { 136 mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, 137 "*.dump")); 138 } 139 } 140 uploadHeapDumpFiles(ITestInvocationListener listener)141 private void uploadHeapDumpFiles(ITestInvocationListener listener) 142 throws DeviceNotAvailableException { 143 // Pull and upload the heap dump output files. 144 InputStreamSource outputSource = null; 145 File outputFile = null; 146 147 String extStore = 148 mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 149 150 String out = mTestDevice.executeShellCommand(String.format("ls %s/%s", 151 extStore, "*.dump")); 152 String heapOutputFiles[] = out.split("\n"); 153 154 for (String heapOutputFile : heapOutputFiles) { 155 try { 156 outputFile = mTestDevice.pullFile(heapOutputFile.trim()); 157 if (outputFile == null) { 158 continue; 159 } 160 outputSource = new FileInputStreamSource(outputFile); 161 listener.testLog(heapOutputFile, LogDataType.TEXT, outputSource); 162 } finally { 163 FileUtil.deleteFile(outputFile); 164 StreamUtil.cancel(outputSource); 165 } 166 } 167 } 168 169 /** 170 * Pull the output files from the device, add it to the logs, and also parse 171 * out the relevant test metrics and report them. 172 */ logOutputFiles(ITestInvocationListener listener)173 private void logOutputFiles(ITestInvocationListener listener) 174 throws DeviceNotAvailableException { 175 File outputFile = null; 176 InputStreamSource outputSource = null; 177 178 if (mGetHeapDump) { 179 // Upload all the heap dump files. 180 uploadHeapDumpFiles(listener); 181 } 182 for (String resultFile : mKeyMap.keySet()) { 183 try { 184 outputFile = mTestDevice.pullFileFromExternal(resultFile); 185 186 if (outputFile == null) { 187 return; 188 } 189 190 // Upload a verbatim copy of the output file 191 Log.d(LOG_TAG, String.format( 192 "Sending %d byte file %s into the logosphere!", 193 outputFile.length(), outputFile)); 194 outputSource = new FileInputStreamSource(outputFile); 195 listener.testLog(resultFile, LogDataType.TEXT, outputSource); 196 197 // Parse the output file to upload aggregated metrics 198 parseOutputFile(new FileInputStream(outputFile), listener, resultFile); 199 } catch (IOException e) { 200 Log.e( 201 LOG_TAG, 202 String.format("IOException while reading or parsing output file: %s", e)); 203 } finally { 204 FileUtil.deleteFile(outputFile); 205 StreamUtil.cancel(outputSource); 206 } 207 } 208 } 209 210 /** 211 * Parse the relevant metrics from the Instrumentation test output file 212 */ parseOutputFile(InputStream dataStream, ITestInvocationListener listener, String outputFile)213 private void parseOutputFile(InputStream dataStream, 214 ITestInvocationListener listener, String outputFile) { 215 216 // try to parse it 217 String contents; 218 try { 219 contents = StreamUtil.getStringFromStream(dataStream); 220 } catch (IOException e) { 221 Log.e(LOG_TAG, String.format( 222 "Got IOException during test processing: %s", e)); 223 return; 224 } 225 226 List<String> lines = Arrays.asList(contents.split("\n")); 227 ListIterator<String> lineIter = lines.listIterator(); 228 229 String line; 230 String key; 231 String memOut; 232 233 while (lineIter.hasNext()){ 234 line = lineIter.next(); 235 236 // Look for the total diff 237 Matcher m = TOTAL_MEM_DIFF_PATTERN.matcher(line); 238 if (m.matches()){ 239 //First group match with the test key name. 240 key = m.group(1); 241 //Second group match witht the test result. 242 memOut = m.group(2); 243 mRunMetrics.put(key, memOut); 244 } 245 } 246 reportMetrics(listener, outputFile); 247 } 248 249 /** 250 * Report run metrics by creating an empty test run to stick them in 251 * <p /> 252 * Exposed for unit testing 253 */ reportMetrics(ITestInvocationListener listener, String outputFile)254 void reportMetrics(ITestInvocationListener listener, String outputFile) { 255 Log.d(LOG_TAG, String.format("About to report metrics: %s", mRunMetrics)); 256 listener.testRunStarted(mKeyMap.get(outputFile), 0); 257 listener.testRunEnded(0, TfMetricProtoUtil.upgradeConvert(mRunMetrics)); 258 } 259 260 @Override setDevice(ITestDevice device)261 public void setDevice(ITestDevice device) { 262 mTestDevice = device; 263 } 264 265 @Override getDevice()266 public ITestDevice getDevice() { 267 return mTestDevice; 268 } 269 } 270