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