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