• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.log.LogUtil.CLog;
23 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
24 import com.android.tradefed.result.ITestInvocationListener;
25 import com.android.tradefed.result.TestDescription;
26 import com.android.tradefed.util.FileUtil;
27 import com.android.tradefed.util.proto.TfMetricProtoUtil;
28 
29 import java.io.BufferedReader;
30 import java.io.File;
31 import java.io.FileReader;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38 
39 /**
40  * Camera2 framework stress test
41  * This is a test invocation that runs stress tests against Camera2 framework (API & HAL) to
42  * isolate stability issues from Camera application.
43  * This invocation uses a Camera2InstrumentationTestRunner to run a set of
44  * Camera framework stress tests.
45  */
46 @OptionClass(alias = "camera2-framework-stress")
47 public class Camera2FrameworkStressTest extends CameraTestBase {
48 
49     // Keys in instrumentation test metrics
50     private static final String RESULT_DIR =
51             "/storage/emulated/0/Android/sandbox/com.android.mediaframeworktest/camera-out";
52     private static final String RESULT_FILE_FORMAT = RESULT_DIR + "fwk-stress_camera_%s.txt";
53     private static final Pattern RESULT_FILE_REGEX = Pattern.compile(
54             "^fwk-stress_camera_(?<id>.+).txt");
55     private static final String KEY_NUM_ATTEMPTS = "numAttempts";
56     private static final String KEY_ITERATION = "iteration";
57 
Camera2FrameworkStressTest()58     public Camera2FrameworkStressTest() {
59         // Note that default value in constructor will be overridden by the passing option from
60         // a command line.
61         setTestPackage("com.android.mediaframeworktest");
62         setTestRunner("com.android.mediaframeworktest.Camera2InstrumentationTestRunner");
63         setRuKey("CameraFrameworkStress");
64         setTestTimeoutMs(2 * 60 * 60 * 1000);   // 2 hours
65         setLogcatOnFailure(true);
66     }
67 
68     /**
69      * {@inheritDoc}
70      */
71     @Override
run(ITestInvocationListener listener)72     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
73         runInstrumentationTest(listener, new CollectingListener(listener));
74     }
75 
76     /**
77      * A listener to collect the output from test run and fatal errors
78      */
79     private class CollectingListener extends DefaultCollectingListener {
80 
CollectingListener(ITestInvocationListener listener)81         public CollectingListener(ITestInvocationListener listener) {
82             super(listener);
83         }
84 
85         @Override
handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics)86         public void handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics) {
87             if (testMetrics == null) {
88                 return; // No-op if there is nothing to post.
89             }
90             for (Map.Entry<String, String> metric : testMetrics.entrySet()) {
91                 getAggregatedMetrics().put(metric.getKey(), metric.getValue());
92             }
93         }
94 
95         @Override
testEnded( TestDescription test, long endTime, HashMap<String, Metric> testMetrics)96         public void testEnded(
97                 TestDescription test, long endTime, HashMap<String, Metric> testMetrics) {
98             if (hasTestRunFatalError()) {
99                 CLog.v("The instrumentation result not found. Fall back to get the metrics from a "
100                         + "log file. errorMsg: %s", getCollectingListener().getErrorMessage());
101             }
102 
103             // For stress test, parse the metrics from a log file.
104             testMetrics = TfMetricProtoUtil.upgradeConvert(parseLog(test.getTestName()));
105             super.testEnded(test, endTime, testMetrics);
106         }
107 
108         // Return null if failed to parse the result file or the test didn't even start.
parseLog(String testName)109         private Map<String, String> parseLog(String testName) {
110             Map<String, String> postMetrics = new HashMap<String, String>();
111             String resultFilePath;
112             try {
113                 for (String cameraId : getCameraIdList(RESULT_DIR)) {
114                     File outputFile = FileUtil.createTempFile("fwk-stress", ".txt");
115                     resultFilePath = getResultFilePath(cameraId);
116                     getDevice().pullFile(resultFilePath, outputFile);
117                     if (outputFile == null) {
118                         throw new DeviceNotAvailableException(String.format("Failed to pull the "
119                                 + "result file: %s", resultFilePath),
120                                 getDevice().getSerialNumber());
121                     }
122 
123                     BufferedReader reader = new BufferedReader(new FileReader(outputFile));
124                     String line;
125                     Map<String, String> resultMap = new HashMap<String, String>();
126 
127                     // Parse results from log file that contain the key-value pairs.
128                     // eg. "numAttempts=10|iteration=9[|cameraId=0]"
129                     try {
130                         while ((line = reader.readLine()) != null) {
131                             String[] pairs = line.split("\\|");
132                             for (String pair : pairs) {
133                                 String[] keyValue = pair.split("=");
134                                 // Each should be a pair of key and value.
135                                 String key = keyValue[0].trim();
136                                 String value = keyValue[1].trim();
137                                 resultMap.put(key, value);
138                             }
139                         }
140                     } finally {
141                         reader.close();
142                     }
143 
144                     // Fail if a stress test doesn't start.
145                     if (0 == Integer.parseInt(resultMap.get(KEY_NUM_ATTEMPTS))) {
146                         CLog.w("Failed to start stress tests. test setup configured incorrectly?");
147                         return null;
148                     }
149                     // Post the number of iterations only with the key name associated with
150                     // test name and camera ID.
151                     String keyName = testName + "_" + cameraId;
152                     postMetrics.put(keyName, resultMap.get(KEY_ITERATION));
153                 }
154             } catch (IOException e) {
155                 CLog.w("Couldn't parse the output log file");
156                 CLog.e(e);
157             } catch (DeviceNotAvailableException e) {
158                 CLog.w("Could not pull file: %s, error:", RESULT_DIR);
159                 CLog.e(e);
160             } catch (NumberFormatException e) {
161                 CLog.w("Could not find the key in file: %s, error:", KEY_NUM_ATTEMPTS);
162                 CLog.e(e);
163             }
164             return postMetrics;
165         }
166     }
167 
getCameraIdList(String resultDir)168     private ArrayList<String> getCameraIdList(String resultDir) throws DeviceNotAvailableException {
169         // The result files are created per each camera ID
170         ArrayList<String> cameraIds = new ArrayList<>();
171         IFileEntry dirEntry = getDevice().getFileEntry(resultDir);
172         if (dirEntry != null) {
173             for (IFileEntry file : dirEntry.getChildren(false)) {
174                 String fileName = file.getName();
175                 Matcher matcher = RESULT_FILE_REGEX.matcher(fileName);
176                 if (matcher.matches()) {
177                     cameraIds.add(matcher.group("id"));
178                 }
179             }
180         }
181 
182         if (cameraIds.isEmpty()) {
183             CLog.w("No camera ID is found in %s. The resultToFile instrumentation argument is set"
184                     + " to false?", resultDir);
185         }
186         return cameraIds;
187     }
188 
getResultFilePath(String cameraId)189     private String getResultFilePath(String cameraId) {
190         return String.format(RESULT_FILE_FORMAT, cameraId);
191     }
192 }
193