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