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