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