1 /* 2 * Copyright (C) 2019 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.log.LogUtil; 20 import com.android.tradefed.metrics.proto.MetricMeasurement; 21 import com.android.tradefed.result.CollectingTestListener; 22 import com.android.tradefed.result.FileInputStreamSource; 23 import com.android.tradefed.result.ITestInvocationListener; 24 import com.android.tradefed.result.InputStreamSource; 25 import com.android.tradefed.result.LogDataType; 26 import com.android.tradefed.result.TestDescription; 27 import com.android.tradefed.util.StreamUtil; 28 import com.android.tradefed.util.proto.TfMetricProtoUtil; 29 30 import java.io.File; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 public class CameraTestMetricsCollectionListener { 35 36 protected static abstract class AbstractCameraTestMetricsCollectionListener extends CollectingTestListener { 37 private ITestInvocationListener mListener; 38 private Map<String, String> mMetrics = new HashMap<>(); 39 private Map<String, String> mFatalErrors = new HashMap<>(); 40 private CameraTestBase mCameraTestBase; 41 42 private static final String INCOMPLETE_TEST_ERR_MSG_PREFIX = 43 "Test failed to run to completion. Reason: 'Instrumentation run failed"; 44 AbstractCameraTestMetricsCollectionListener(ITestInvocationListener listener)45 public AbstractCameraTestMetricsCollectionListener(ITestInvocationListener listener) { 46 mListener = listener; 47 mCameraTestBase = new CameraTestBase(); 48 } 49 /** 50 * Report the end of an individual camera test and delegate handling the collected metrics to 51 * subclasses. Do not override testEnded to manipulate the test metrics after each test. 52 * Instead, use handleMetricsOnTestEnded. 53 * 54 * @param test identifies the test 55 * @param testMetrics a {@link Map} of the metrics emitted 56 */ 57 @Override testEnded( TestDescription test, long endTime, HashMap<String, MetricMeasurement.Metric> testMetrics)58 public void testEnded( 59 TestDescription test, 60 long endTime, 61 HashMap<String, MetricMeasurement.Metric> testMetrics) { 62 super.testEnded(test, endTime, testMetrics); 63 handleMetricsOnTestEnded(test, TfMetricProtoUtil.compatibleConvert(testMetrics)); 64 stopDumping(test); 65 mListener.testEnded(test, endTime, testMetrics); 66 } 67 68 @Override testStarted(TestDescription test, long startTime)69 public void testStarted(TestDescription test, long startTime) { 70 super.testStarted(test, startTime); 71 startDumping(test); 72 mListener.testStarted(test, startTime); 73 } 74 75 @Override testFailed(TestDescription test, String trace)76 public void testFailed(TestDescription test, String trace) { 77 super.testFailed(test, trace); 78 // If the test failed to run to complete, this is an exceptional case. 79 // Let this test run fail so that it can rerun. 80 if (trace.startsWith(INCOMPLETE_TEST_ERR_MSG_PREFIX)) { 81 mFatalErrors.put(test.getTestName(), trace); 82 LogUtil.CLog.d("Test (%s) failed due to fatal error : %s", test.getTestName(), trace); 83 } 84 mListener.testFailed(test, trace); 85 } 86 87 @Override testRunFailed(String errorMessage)88 public void testRunFailed(String errorMessage) { 89 super.testRunFailed(errorMessage); 90 mFatalErrors.put(mCameraTestBase.getRuKey(), errorMessage); 91 } 92 93 @Override testRunEnded( long elapsedTime, HashMap<String, MetricMeasurement.Metric> runMetrics)94 public void testRunEnded( 95 long elapsedTime, HashMap<String, MetricMeasurement.Metric> runMetrics) { 96 super.testRunEnded(elapsedTime, runMetrics); 97 handleTestRunEnded(mListener, elapsedTime, TfMetricProtoUtil.compatibleConvert(runMetrics)); 98 // never be called since handleTestRunEnded will handle it if needed. 99 // mListener.testRunEnded(elapsedTime, runMetrics); 100 } 101 102 @Override testRunStarted(String runName, int testCount)103 public void testRunStarted(String runName, int testCount) { 104 super.testRunStarted(runName, testCount); 105 mListener.testRunStarted(runName, testCount); 106 } 107 108 @Override testRunStopped(long elapsedTime)109 public void testRunStopped(long elapsedTime) { 110 super.testRunStopped(elapsedTime); 111 mListener.testRunStopped(elapsedTime); 112 } 113 114 @Override testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)115 public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) { 116 super.testLog(dataName, dataType, dataStream); 117 mListener.testLog(dataName, dataType, dataStream); 118 } 119 startDumping(TestDescription test)120 protected void startDumping(TestDescription test) { 121 if (mCameraTestBase.shouldDumpMeminfo()) { 122 mCameraTestBase.mMeminfoTimer.start(test); 123 } 124 if (mCameraTestBase.shouldDumpThreadCount()) { 125 mCameraTestBase.mThreadTrackerTimer.start(test); 126 } 127 } 128 stopDumping(TestDescription test)129 protected void stopDumping(TestDescription test) { 130 InputStreamSource outputSource = null; 131 File outputFile = null; 132 if (mCameraTestBase.shouldDumpMeminfo()) { 133 mCameraTestBase.mMeminfoTimer.stop(); 134 // Grab a snapshot of meminfo file and post it to dashboard. 135 try { 136 outputFile = mCameraTestBase.mMeminfoTimer.getOutputFile(); 137 outputSource = new FileInputStreamSource(outputFile, true /* delete */); 138 String logName = String.format("meminfo_%s", test.getTestName()); 139 mListener.testLog(logName, LogDataType.TEXT, outputSource); 140 } finally { 141 StreamUtil.cancel(outputSource); 142 } 143 } 144 if (mCameraTestBase.shouldDumpThreadCount()) { 145 mCameraTestBase.mThreadTrackerTimer.stop(); 146 try { 147 outputFile = mCameraTestBase.mThreadTrackerTimer.getOutputFile(); 148 outputSource = new FileInputStreamSource(outputFile, true /* delete */); 149 String logName = String.format("ps_%s", test.getTestName()); 150 mListener.testLog(logName, LogDataType.TEXT, outputSource); 151 } finally { 152 StreamUtil.cancel(outputSource); 153 } 154 } 155 } 156 getAggregatedMetrics()157 public Map<String, String> getAggregatedMetrics() { 158 return mMetrics; 159 } 160 getListeners()161 public ITestInvocationListener getListeners() { 162 return mListener; 163 } 164 165 /** 166 * Determine that the test run failed with fatal errors. 167 * 168 * @return True if test run has a failure due to fatal error. 169 */ hasTestRunFatalError()170 public boolean hasTestRunFatalError() { 171 return (getNumTotalTests() > 0 && mFatalErrors.size() > 0); 172 } 173 getFatalErrors()174 public Map<String, String> getFatalErrors() { 175 return mFatalErrors; 176 } 177 getErrorMessage()178 public String getErrorMessage() { 179 StringBuilder sb = new StringBuilder(); 180 for (Map.Entry<String, String> error : mFatalErrors.entrySet()) { 181 sb.append(error.getKey()); 182 sb.append(" : "); 183 sb.append(error.getValue()); 184 sb.append("\n"); 185 } 186 return sb.toString(); 187 } 188 handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics)189 abstract void handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics); 190 handleTestRunEnded( ITestInvocationListener listener, long elapsedTime, Map<String, String> runMetrics)191 abstract void handleTestRunEnded( 192 ITestInvocationListener listener, long elapsedTime, Map<String, String> runMetrics); 193 } 194 195 protected static class DefaultCollectingListener extends AbstractCameraTestMetricsCollectionListener { 196 private CameraTestBase mCameraTestBase; 197 DefaultCollectingListener(ITestInvocationListener listener)198 public DefaultCollectingListener(ITestInvocationListener listener) { 199 super(listener); 200 mCameraTestBase = new CameraTestBase(); 201 } 202 203 @Override handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics)204 public void handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics) { 205 if (testMetrics == null) { 206 return; // No-op if there is nothing to post. 207 } 208 getAggregatedMetrics().putAll(testMetrics); 209 } 210 211 @Override handleTestRunEnded( ITestInvocationListener listener, long elapsedTime, Map<String, String> runMetrics)212 public void handleTestRunEnded( 213 ITestInvocationListener listener, long elapsedTime, Map<String, String> runMetrics) { 214 // Post aggregated metrics at the end of test run. 215 listener.testRunEnded( 216 mCameraTestBase.getTestDurationMs(), 217 TfMetricProtoUtil.upgradeConvert(getAggregatedMetrics())); 218 } 219 } 220 } 221