Home | History | Annotate | Download | only in tests
      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