1 /*
2  * Copyright (C) 2015 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.tradefed.config.OptionClass;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.IFileEntry;
22 import com.android.tradefed.invoker.TestInformation;
23 import com.android.tradefed.log.LogUtil.CLog;
24 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
25 import com.android.tradefed.result.FileInputStreamSource;
26 import com.android.tradefed.result.ITestInvocationListener;
27 import com.android.tradefed.result.LogDataType;
28 import com.android.tradefed.result.TestDescription;
29 import com.android.tradefed.util.FileUtil;
30 import com.android.tradefed.util.proto.TfMetricProtoUtil;
31 
32 import java.io.BufferedReader;
33 import java.io.File;
34 import java.io.FileReader;
35 import java.io.IOException;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 
40 /**
41  * Camera2 stress test
42  * Since Camera stress test can drain the battery seriously. Need to split
43  * the test suite into separate test invocation for each test method.
44  * <p/>
45  */
46 @OptionClass(alias = "camera2-stress")
47 public class Camera2StressTest extends CameraTestBase {
48 
49     private static final String RESULT_FILE = "/sdcard/camera-out/stress.txt";
50     private static final String FAILURE_SCREENSHOT_DIR = "/sdcard/camera-screenshot/";
51     private static final String KEY_NUM_ATTEMPTS = "numAttempts";
52     private static final String KEY_ITERATION = "iteration";
53 
Camera2StressTest()54     public Camera2StressTest() {
55         setTestPackage("com.google.android.camera");
56         setTestClass("com.android.camera.stress.CameraStressTest");
57         setTestRunner("android.test.InstrumentationTestRunner");
58         setRuKey("CameraAppStress");
59         setTestTimeoutMs(6 * 60 * 60 * 1000);   // 6 hours
60         setLogcatOnFailure(true);
61     }
62 
63     /** {@inheritDoc} */
64     @Override
run(TestInformation testInfo, ITestInvocationListener listener)65     public void run(TestInformation testInfo, ITestInvocationListener listener)
66             throws DeviceNotAvailableException {
67         runInstrumentationTest(testInfo, listener, new CollectingListener(listener));
68     }
69 
70     /**
71      * A listener to collect the output from test run and fatal errors
72      */
73     private class CollectingListener extends CameraTestMetricsCollectionListener.DefaultCollectingListener {
74 
CollectingListener(ITestInvocationListener listener)75         public CollectingListener(ITestInvocationListener listener) {
76             super(listener);
77         }
78 
79         @Override
testEnded( TestDescription test, long endTime, HashMap<String, Metric> testMetrics)80         public void testEnded(
81                 TestDescription test, long endTime, HashMap<String, Metric> testMetrics) {
82             if (hasTestRunFatalError()) {
83                 CLog.v(
84                         "The instrumentation result not found. Fall back to get the metrics from a "
85                                 + "log file. errorMsg: %s",
86                         getErrorMessage());
87             }
88             // TODO: Will get the additional metrics to file to prevent result loss
89 
90             // Don't need to report the KEY_NUM_ATTEMPS to dashboard
91             // Iteration will be read from file
92             testMetrics.remove(KEY_NUM_ATTEMPTS);
93             testMetrics.remove(KEY_ITERATION);
94 
95             // add testMethod name to the metric
96             Map<String, String> namedTestMetrics = new HashMap<>();
97             for (Entry<String, Metric> entry : testMetrics.entrySet()) {
98                 namedTestMetrics.put(
99                         test.getTestName() + entry.getKey(),
100                         entry.getValue().getMeasurements().getSingleString());
101             }
102 
103             // parse the iterations metrics from the stress log files
104             parseLog(test.getTestName(), namedTestMetrics);
105 
106             postScreenshotOnFailure(test);
107             super.testEnded(test, endTime, TfMetricProtoUtil.upgradeConvert(namedTestMetrics));
108         }
109 
postScreenshotOnFailure(TestDescription test)110         private void postScreenshotOnFailure(TestDescription test) {
111             File tmpDir = null;
112             try {
113                 IFileEntry screenshotDir = getDevice().getFileEntry(FAILURE_SCREENSHOT_DIR);
114                 if (screenshotDir != null && screenshotDir.isDirectory()) {
115                     tmpDir = FileUtil.createTempDir("screenshot");
116                     for (IFileEntry remoteFile : screenshotDir.getChildren(false)) {
117                         if (remoteFile.isDirectory()) {
118                             continue;
119                         }
120                         if (!remoteFile.getName().contains(test.getTestName())) {
121                             CLog.v("Skipping the screenshot (%s) that doesn't match the current "
122                                     + "test (%s)", remoteFile.getName(), test.getTestName());
123                             continue;
124                         }
125                         File screenshot = new File(tmpDir, remoteFile.getName());
126                         if (!getDevice().pullFile(remoteFile.getFullPath(), screenshot)) {
127                             CLog.w("Could not pull screenshot: %s", remoteFile.getFullPath());
128                             continue;
129                         }
130                         testLog(
131                                 "screenshot_" + screenshot.getName(),
132                                 LogDataType.PNG,
133                                 new FileInputStreamSource(screenshot));
134                     }
135                 }
136             } catch (DeviceNotAvailableException e) {
137                 CLog.e(e);
138             } catch (IOException e) {
139                 CLog.e(e);
140             } finally {
141                 FileUtil.recursiveDelete(tmpDir);
142             }
143         }
144 
145         // Return null if failed to parse the result file or the test didn't even start.
parseLog(String testName, Map<String, String> testMetrics)146         private void parseLog(String testName, Map<String, String> testMetrics) {
147             try {
148                 File outputFile = FileUtil.createTempFile("stress", ".txt");
149                 getDevice().pullFile(RESULT_FILE, outputFile);
150                 if (outputFile == null) {
151                     throw new DeviceNotAvailableException(String.format("Failed to pull the result"
152                             + "file: %s", RESULT_FILE), getDevice().getSerialNumber());
153                 }
154                 BufferedReader reader = new BufferedReader(new FileReader(outputFile));
155                 String line;
156                 Map<String, String> resultMap = new HashMap<>();
157 
158                 // Parse results from log file that contain the key-value pairs.
159                 // eg. "numAttempts=10|iteration=9"
160                 try {
161                     while ((line = reader.readLine()) != null) {
162                         String[] pairs = line.split("\\|");
163                         for (String pair : pairs) {
164                             String[] keyValue = pair.split("=");
165                             // Each should be a pair of key and value.
166                             String key = keyValue[0].trim();
167                             String value = keyValue[1].trim();
168                             resultMap.put(key, value);
169                             CLog.v("%s: %s", key, value);
170                         }
171                     }
172                 } finally {
173                     reader.close();
174                 }
175 
176                 // Fail if a stress test doesn't start.
177                 if (0 == Integer.parseInt(resultMap.get(KEY_NUM_ATTEMPTS))) {
178                     CLog.w("Failed to start stress tests. test setup configured incorrectly?");
179                     return;
180                 }
181                 // Post the number of iterations only with the test name as key.
182                 testMetrics.put(testName, resultMap.get(KEY_ITERATION));
183             } catch (IOException e) {
184                 CLog.w("Couldn't parse the output log file:");
185                 CLog.e(e);
186             } catch (DeviceNotAvailableException e) {
187                 CLog.w("Could not pull file: %s, error:", RESULT_FILE);
188                 CLog.e(e);
189             } catch (NumberFormatException e) {
190                 CLog.w("Could not find the key in file: %s, error:", KEY_NUM_ATTEMPTS);
191                 CLog.e(e);
192             }
193         }
194     }
195 }
196