• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.tradefed.postprocessor;
17 
18 import com.android.tradefed.config.Option;
19 import com.android.tradefed.invoker.IInvocationContext;
20 import com.android.tradefed.log.LogUtil.CLog;
21 import com.android.tradefed.metrics.proto.MetricMeasurement.DataType;
22 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
23 import com.android.tradefed.result.ILogSaver;
24 import com.android.tradefed.result.ILogSaverListener;
25 import com.android.tradefed.result.ITestInvocationListener;
26 import com.android.tradefed.result.InputStreamSource;
27 import com.android.tradefed.result.LogDataType;
28 import com.android.tradefed.result.LogFile;
29 import com.android.tradefed.result.TestDescription;
30 import com.android.tradefed.util.proto.TfMetricProtoUtil;
31 
32 import com.google.common.collect.ArrayListMultimap;
33 import com.google.common.collect.ListMultimap;
34 
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 
39 /**
40  * The base {@link IPostProcessor} that every implementation should extend. Ensure that the post
41  * processing methods are called before the final result reporters.
42  *
43  * <p>TODO: expand to file post-processing too if needed.
44  */
45 public abstract class BasePostProcessor implements IPostProcessor {
46 
47     @Option(name = "disable", description = "disables the post processor.")
48     private boolean mDisable = false;
49 
50     private ITestInvocationListener mForwarder;
51     private ArrayListMultimap<String, Metric> storedTestMetrics = ArrayListMultimap.create();
52 
53     /** {@inheritDoc} */
54     @Override
processRunMetrics( HashMap<String, Metric> rawMetrics)55     public abstract Map<String, Metric.Builder> processRunMetrics(
56             HashMap<String, Metric> rawMetrics);
57 
58     /** {@inhericDoc} */
59     @Override
processTestMetrics( TestDescription testDescription, HashMap<String, Metric> testMetrics)60     public Map<String, Metric.Builder> processTestMetrics(
61             TestDescription testDescription, HashMap<String, Metric> testMetrics) {
62         return new HashMap<String, Metric.Builder>();
63     }
64 
65     /** {@inheritDoc} */
66     @Override
processAllTestMetrics( ListMultimap<String, Metric> allTestMetrics)67     public Map<String, Metric.Builder> processAllTestMetrics(
68             ListMultimap<String, Metric> allTestMetrics) {
69         return new HashMap<String, Metric.Builder>();
70     }
71 
72     /** =================================== */
73     /** {@inheritDoc} */
74     @Override
init(ITestInvocationListener listener)75     public final ITestInvocationListener init(ITestInvocationListener listener) {
76         mForwarder = listener;
77         return this;
78     }
79 
80     /** =================================== */
81     /** {@inheritDoc} */
82     @Override
isDisabled()83     public final boolean isDisabled() {
84         return mDisable;
85     }
86 
87     /** =================================== */
88     /** Invocation Listeners for forwarding */
89     @Override
invocationStarted(IInvocationContext context)90     public final void invocationStarted(IInvocationContext context) {
91         mForwarder.invocationStarted(context);
92     }
93 
94     @Override
invocationFailed(Throwable cause)95     public final void invocationFailed(Throwable cause) {
96         mForwarder.invocationFailed(cause);
97     }
98 
99     @Override
invocationEnded(long elapsedTime)100     public final void invocationEnded(long elapsedTime) {
101         mForwarder.invocationEnded(elapsedTime);
102     }
103 
104     @Override
testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)105     public final void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
106         mForwarder.testLog(dataName, dataType, dataStream);
107     }
108 
109     /** Test run callbacks */
110     @Override
testRunStarted(String runName, int testCount)111     public final void testRunStarted(String runName, int testCount) {
112         mForwarder.testRunStarted(runName, testCount);
113     }
114 
115     @Override
testRunStarted(String runName, int testCount, int attemptNumber)116     public final void testRunStarted(String runName, int testCount, int attemptNumber) {
117         mForwarder.testRunStarted(runName, testCount, attemptNumber);
118     }
119 
120     @Override
testRunFailed(String errorMessage)121     public final void testRunFailed(String errorMessage) {
122         mForwarder.testRunFailed(errorMessage);
123     }
124 
125     @Override
testRunStopped(long elapsedTime)126     public final void testRunStopped(long elapsedTime) {
127         mForwarder.testRunStopped(elapsedTime);
128     }
129 
130     @Override
testRunEnded(long elapsedTime, Map<String, String> runMetrics)131     public final void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
132         testRunEnded(elapsedTime, TfMetricProtoUtil.upgradeConvert(runMetrics));
133     }
134 
135     @Override
testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics)136     public final void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
137         try {
138             HashMap<String, Metric> rawValues = getRawMetricsOnly(runMetrics);
139             // Add post-processed run metrics.
140             Map<String, Metric.Builder> postprocessedResults = processRunMetrics(rawValues);
141             addProcessedMetricsToExistingMetrics(postprocessedResults, runMetrics);
142             // Add aggregated test metrics (results from post-processing all test metrics).
143             Map<String, Metric.Builder> aggregateResults = processAllTestMetrics(storedTestMetrics);
144             addProcessedMetricsToExistingMetrics(aggregateResults, runMetrics);
145         } catch (RuntimeException e) {
146             // Prevent exception from messing up the status reporting.
147             CLog.e(e);
148         } finally {
149             // Clear out the stored test metrics.
150             storedTestMetrics.clear();
151         }
152         mForwarder.testRunEnded(elapsedTime, runMetrics);
153     }
154 
155     /** Test cases callbacks */
156     @Override
testStarted(TestDescription test)157     public final void testStarted(TestDescription test) {
158         testStarted(test, System.currentTimeMillis());
159     }
160 
161     @Override
testStarted(TestDescription test, long startTime)162     public final void testStarted(TestDescription test, long startTime) {
163         mForwarder.testStarted(test, startTime);
164     }
165 
166     @Override
testFailed(TestDescription test, String trace)167     public final void testFailed(TestDescription test, String trace) {
168         mForwarder.testFailed(test, trace);
169     }
170 
171     @Override
testEnded(TestDescription test, Map<String, String> testMetrics)172     public final void testEnded(TestDescription test, Map<String, String> testMetrics) {
173         testEnded(test, System.currentTimeMillis(), testMetrics);
174     }
175 
176     @Override
testEnded( TestDescription test, long endTime, Map<String, String> testMetrics)177     public final void testEnded(
178             TestDescription test, long endTime, Map<String, String> testMetrics) {
179         testEnded(test, endTime, TfMetricProtoUtil.upgradeConvert(testMetrics));
180     }
181 
182     @Override
testEnded(TestDescription test, HashMap<String, Metric> testMetrics)183     public final void testEnded(TestDescription test, HashMap<String, Metric> testMetrics) {
184         testEnded(test, System.currentTimeMillis(), testMetrics);
185     }
186 
187     @Override
testEnded( TestDescription test, long endTime, HashMap<String, Metric> testMetrics)188     public final void testEnded(
189             TestDescription test, long endTime, HashMap<String, Metric> testMetrics) {
190         try {
191             HashMap<String, Metric> rawValues = getRawMetricsOnly(testMetrics);
192             // Store the raw metrics from the test in storedTestMetrics for potential aggregation.
193             for (Map.Entry<String, Metric> entry : rawValues.entrySet()) {
194                 storedTestMetrics.put(entry.getKey(), entry.getValue());
195             }
196             Map<String, Metric.Builder> results = processTestMetrics(test, rawValues);
197             for (Entry<String, Metric.Builder> newEntry : results.entrySet()) {
198                 String newKey = newEntry.getKey();
199                 if (testMetrics.containsKey(newKey)) {
200                     CLog.e(
201                             "Key '%s' is already asssociated with a metric and will not be "
202                                     + "replaced.",
203                             newKey);
204                     continue;
205                 }
206                 // Force the metric to 'processed' since generated in a post-processor.
207                 Metric newMetric = newEntry.getValue().setType(DataType.PROCESSED).build();
208                 testMetrics.put(newKey, newMetric);
209             }
210         } catch (RuntimeException e) {
211             // Prevent exception from messing up the status reporting.
212             CLog.e(e);
213         }
214         mForwarder.testEnded(test, endTime, testMetrics);
215     }
216 
217     @Override
testAssumptionFailure(TestDescription test, String trace)218     public final void testAssumptionFailure(TestDescription test, String trace) {
219         mForwarder.testAssumptionFailure(test, trace);
220     }
221 
222     @Override
testIgnored(TestDescription test)223     public final void testIgnored(TestDescription test) {
224         mForwarder.testIgnored(test);
225     }
226 
227     @Override
setLogSaver(ILogSaver logSaver)228     public final void setLogSaver(ILogSaver logSaver) {
229         if (mForwarder instanceof ILogSaverListener) {
230             ((ILogSaverListener) mForwarder).setLogSaver(logSaver);
231         }
232     }
233 
234     @Override
testLogSaved( String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile)235     public final void testLogSaved(
236             String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile) {
237         if (mForwarder instanceof ILogSaverListener) {
238             ((ILogSaverListener) mForwarder).testLogSaved(dataName, dataType, dataStream, logFile);
239         }
240     }
241 
242     @Override
logAssociation(String dataName, LogFile logFile)243     public final void logAssociation(String dataName, LogFile logFile) {
244         if (mForwarder instanceof ILogSaverListener) {
245             ((ILogSaverListener) mForwarder).logAssociation(dataName, logFile);
246         }
247     }
248 
249     // Internal utilities
250 
251     /**
252      * We only allow post-processing of raw values. Already processed values will not be considered.
253      */
getRawMetricsOnly(HashMap<String, Metric> runMetrics)254     private HashMap<String, Metric> getRawMetricsOnly(HashMap<String, Metric> runMetrics) {
255         HashMap<String, Metric> rawMetrics = new HashMap<>();
256         for (Entry<String, Metric> entry : runMetrics.entrySet()) {
257             if (DataType.RAW.equals(entry.getValue().getType())) {
258                 rawMetrics.put(entry.getKey(), entry.getValue());
259             }
260         }
261         return rawMetrics;
262     }
263 
264     /** Add processed metrics to the metrics to be reported. */
addProcessedMetricsToExistingMetrics( Map<String, Metric.Builder> processed, Map<String, Metric> existing)265     private void addProcessedMetricsToExistingMetrics(
266             Map<String, Metric.Builder> processed, Map<String, Metric> existing) {
267         for (Entry<String, Metric.Builder> newEntry : processed.entrySet()) {
268             String newKey = newEntry.getKey();
269             if (existing.containsKey(newKey)) {
270                 CLog.e(
271                         "Key '%s' is already asssociated with a metric and will not be "
272                                 + "replaced.",
273                         newKey);
274                 continue;
275             }
276             // Force the metric to 'processed' since generated in a post-processor.
277             Metric newMetric = newEntry.getValue().setType(DataType.PROCESSED).build();
278             existing.put(newKey, newMetric);
279         }
280     }
281 }
282