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