1 /*
2  * Copyright (C) 2012 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.testrunner.IRemoteAndroidTestRunner;
21 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.device.DeviceNotAvailableException;
24 import com.android.tradefed.device.ITestDevice;
25 import com.android.tradefed.log.LogUtil.CLog;
26 import com.android.tradefed.result.BugreportCollector;
27 import com.android.tradefed.result.ITestInvocationListener;
28 import com.android.tradefed.result.InputStreamSource;
29 import com.android.tradefed.result.LogDataType;
30 import com.android.tradefed.testtype.IDeviceTest;
31 import com.android.tradefed.testtype.IRemoteTest;
32 import com.android.tradefed.util.StreamUtil;
33 import com.android.tradefed.util.proto.TfMetricProtoUtil;
34 
35 import org.junit.Assert;
36 
37 import java.io.BufferedReader;
38 import java.io.File;
39 import java.io.FileReader;
40 import java.io.IOException;
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.ListIterator;
44 import java.util.Map;
45 import java.util.concurrent.TimeUnit;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
48 
49 /**
50  * Camera zoom stress test that increments the camera's zoom level across the
51  * entire range [min, max], taking a picture at each level.
52  */
53 public class CameraSettingsTest implements IDeviceTest, IRemoteTest {
54 
55     private static final String ZOOM_STANZA = "testStressCameraZoom";
56     private static final String SCENE_MODES_STANZA = "testStressCameraSceneModes";
57     private static final Pattern EXPECTED_LOOP_COUNT_PATTERN =
58             Pattern.compile("(Total number of loops:)(\\s*)(\\d+)");
59     private static final Pattern ACTUAL_LOOP_COUNT_PATTERN =
60             Pattern.compile("(No of loop:)(.*,\\s)(\\d+)$");
61 
62     private static final String TEST_CLASS_NAME =
63             "com.android.mediaframeworktest.stress.CameraStressTest";
64     private static final String TEST_PACKAGE_NAME = "com.android.mediaframeworktest";
65     private static final String TEST_RUNNER_NAME =
66             "com.android.mediaframeworktest.CameraStressTestRunner";
67     private static final String TEST_RU = "CameraApplicationStress";
68 
69     private final String mOutputPath = "cameraStressOutput.txt";
70     private static final int MAX_TIME_OUT = 90 * 60 * 1000; //90 mins
71 
72     @Option(name="testMethodName", description="Used to specify a specific test method to run")
73     private String mTestMethodName = null;
74 
75     ITestDevice mTestDevice = null;
76 
77     /**
78      * {@inheritDoc}
79      */
80     @Override
run(ITestInvocationListener listener)81     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
82         Assert.assertNotNull(mTestDevice);
83 
84         IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(TEST_PACKAGE_NAME,
85                 TEST_RUNNER_NAME, mTestDevice.getIDevice());
86         runner.setClassName(TEST_CLASS_NAME);
87 
88         if (mTestMethodName != null) {
89             runner.setMethodName(TEST_CLASS_NAME, mTestMethodName);
90         }
91         runner.setMaxTimeToOutputResponse(MAX_TIME_OUT, TimeUnit.MILLISECONDS);
92 
93         BugreportCollector bugListener = new BugreportCollector(listener, mTestDevice);
94         bugListener.addPredicate(BugreportCollector.AFTER_FAILED_TESTCASES);
95         bugListener.setDescriptiveName(this.getClass().getName());
96         Assert.assertTrue(mTestDevice.runInstrumentationTests(runner, bugListener));
97 
98         Map<String, String> metrics = parseOutputFile();
99         reportMetrics(bugListener, TEST_RU, metrics);
100         cleanupDevice();
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @Override
setDevice(ITestDevice device)107     public void setDevice(ITestDevice device) {
108         mTestDevice = device;
109     }
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
getDevice()115     public ITestDevice getDevice() {
116         return mTestDevice;
117     }
118 
119     /**
120      * Wipes the device's external memory of test collateral from prior runs.
121      *
122      * @throws DeviceNotAvailableException If the device is unavailable or
123      *         something happened while deleting files
124      */
cleanupDevice()125     private void cleanupDevice() throws DeviceNotAvailableException {
126         String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE);
127         mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, mOutputPath));
128     }
129 
130     /**
131      * Parses the output file generated by the underlying instrumentation test
132      * and returns it to the main driver for later reporting.
133      *
134      * @return The {@link Map} that contains metrics for the test.
135      * @throws DeviceNotAvailableException If the device is unavailable or
136      *         something happened while deleting files
137      */
parseOutputFile()138     private Map<String, String> parseOutputFile() throws DeviceNotAvailableException {
139         File outputFile = null;
140         BufferedReader reader = null;
141         ArrayList<String> lines = new ArrayList<String>();
142         String line = null;
143         String key = null;
144         Integer expectedCount = null;
145         Integer actualCount = null;
146         ListIterator<String> listIterator = null;
147         Map<String, String> metrics = new HashMap<String, String>();
148 
149         // Read in data
150         try {
151             outputFile = mTestDevice.pullFileFromExternal(mOutputPath);
152             reader = new BufferedReader(new FileReader(outputFile));
153 
154             while ((line = reader.readLine()) != null) {
155                 if (!line.isEmpty()) {
156                     lines.add(line);
157                 }
158             }
159         } catch (IOException e) {
160             CLog.e(String.format("IOException reading from file: %s", e.toString()));
161         } finally {
162             StreamUtil.close(reader);
163         }
164 
165         // Output file looks like:
166         // Test name:
167         // Total number of loops: 123
168         // No of loop: 0, 1, 2, 3, ..., 122 (0 based)
169         // Note that the actual count should be +1 as the # of loop is 0 based.
170         listIterator = lines.listIterator();
171 
172         while (listIterator.hasNext()) {
173             line = listIterator.next();
174             CLog.d(String.format("Parsing line: \"%s\"", line));
175 
176             if (ZOOM_STANZA.equals(line)) {
177                 key = "CameraZoom";
178             } else if (SCENE_MODES_STANZA.equals(line)) {
179                 key = "CameraSceneMode";
180             }
181 
182             Matcher expectedMatcher = EXPECTED_LOOP_COUNT_PATTERN.matcher(line);
183             if (expectedMatcher.matches()) {
184                 expectedCount = Integer.valueOf(expectedMatcher.group(3));
185                 CLog.d(String.format("Found expected count for key \"%s\": %s",
186                         key, expectedCount));
187             }
188 
189             Matcher actualMatcher = ACTUAL_LOOP_COUNT_PATTERN.matcher(line);
190             if (actualMatcher.matches()) {
191                 actualCount = 1 + Integer.valueOf(actualMatcher.group(3));
192                 CLog.d(String.format("Found actual count for key \"%s\": %s", key, actualCount));
193             }
194 
195             if ((key != null) && (expectedCount != null) && (actualCount != null)) {
196                 metrics.put(key, String.format("%d", actualCount));
197                 key = null;
198                 expectedCount = null;
199                 actualCount = null;
200             }
201         }
202 
203         return metrics;
204     }
205 
206     /**
207      * Report run metrics by creating an empty test run to stick them in.
208      *
209      * @param listener The {@link ITestInvocationListener} of test results
210      * @param runName The test name
211      * @param metrics The {@link Map} that contains metrics for the given test
212      */
reportMetrics(ITestInvocationListener listener, String runName, Map<String, String> metrics)213     private void reportMetrics(ITestInvocationListener listener, String runName,
214             Map<String, String> metrics) {
215         InputStreamSource bugreport = mTestDevice.getBugreport();
216         listener.testLog("bugreport", LogDataType.BUGREPORT, bugreport);
217         bugreport.close();
218 
219         CLog.d(String.format("About to report metrics: %s", metrics));
220         listener.testRunStarted(runName, 0);
221         listener.testRunEnded(0, TfMetricProtoUtil.upgradeConvert(metrics));
222     }
223 }
224