blob: 230805e90b087142af1db30d83193c40b7ff6162 [file] [log] [blame]
Brett Chabot51a4e3d2010-03-09 11:08:44 -08001/*
2 * Copyright (C) 2010 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 */
16package com.android.tradefed.result;
17
Brett Chabot2ea6ede2014-08-04 11:15:19 -070018import com.android.ddmlib.testrunner.TestResult.TestStatus;
Brett Chabotbeaebb02011-02-07 16:16:06 -080019import com.android.tradefed.build.IBuildInfo;
Brett Chabotd110d212011-01-21 20:10:17 -080020import com.android.tradefed.config.Option;
Julien Desprez53353e62016-08-12 15:24:33 +010021import com.android.tradefed.invoker.IInvocationContext;
Julien Desprez93a041b2018-08-29 08:55:27 -070022import com.android.tradefed.log.LogUtil.CLog;
jdesprez9c6eb222018-04-02 10:47:08 -070023import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
Julien Despreze5bdb4c2019-08-01 14:09:30 -070024import com.android.tradefed.retry.MergeStrategy;
Brett Chabot5ec52922010-09-07 18:08:30 -070025
Julien Desprez1047f362017-02-20 09:24:27 +000026import com.google.common.annotations.VisibleForTesting;
Julien Desprez93a041b2018-08-29 08:55:27 -070027
Eric Rowea6d559e2018-07-26 12:01:02 -070028import java.util.ArrayList;
Brett Chabot873741a2010-09-15 12:24:08 -070029import java.util.Collection;
Julien Desprez0dd832d2018-12-03 10:35:53 -080030import java.util.Collections;
jdesprez9c6eb222018-04-02 10:47:08 -070031import java.util.HashMap;
Brett Chabotfccfc612010-03-29 16:08:49 -070032import java.util.LinkedHashMap;
Eric Rowea6d559e2018-07-26 12:01:02 -070033import java.util.LinkedList;
Eric Rowe6a19da72018-07-24 19:13:55 -070034import java.util.List;
Brett Chabot51a4e3d2010-03-09 11:08:44 -080035import java.util.Map;
Julien Desprez7ea0bd42018-12-05 09:59:28 -080036import java.util.Map.Entry;
Julien Desprez42aa6dd2018-12-05 15:56:52 -080037import java.util.concurrent.atomic.AtomicBoolean;
Brett Chabot51a4e3d2010-03-09 11:08:44 -080038
39/**
Brett Chabot873741a2010-09-15 12:24:08 -070040 * A {@link ITestInvocationListener} that will collect all test results.
jdesprez5390ce82018-01-25 16:54:14 -080041 *
42 * <p>Although the data structures used in this object are thread-safe, the {@link
43 * ITestInvocationListener} callbacks must be called in the correct order.
Brett Chabot51a4e3d2010-03-09 11:08:44 -080044 */
jdesprez5390ce82018-01-25 16:54:14 -080045public class CollectingTestListener implements ITestInvocationListener, ILogSaverListener {
Brett Chabot51a4e3d2010-03-09 11:08:44 -080046
Eric Rowe7d8e0c62018-07-25 14:27:41 -070047 @Option(
48 name = "aggregate-metrics",
49 description = "attempt to add test metrics values for test runs with the same name.")
Brett Chabotd110d212011-01-21 20:10:17 -080050 private boolean mIsAggregateMetrics = false;
51
jdesprezbc580f92017-06-02 11:41:40 -070052 /** Toggle the 'aggregate metrics' option */
53 protected void setIsAggregrateMetrics(boolean aggregate) {
Brett Chabotd110d212011-01-21 20:10:17 -080054 mIsAggregateMetrics = aggregate;
55 }
56
Eric Rowea6d559e2018-07-26 12:01:02 -070057 private IInvocationContext mContext;
58 private IBuildInfo mBuildInfo;
59 private Map<String, IInvocationContext> mModuleContextMap = new HashMap<>();
60 // Use LinkedHashMap to provide consistent iterations over the keys.
Julien Desprez0dd832d2018-12-03 10:35:53 -080061 private Map<String, List<TestRunResult>> mTestRunResultMap =
62 Collections.synchronizedMap(new LinkedHashMap<>());
Eric Rowea6d559e2018-07-26 12:01:02 -070063
64 private IInvocationContext mCurrentModuleContext = null;
65 private TestRunResult mCurrentTestRunResult = new TestRunResult();
Julien Desprezf94d3082019-04-23 10:45:01 -070066 /** True if the default initialized mCurrentTestRunResult has its original value. */
67 private boolean mDefaultRun = true;
Julien Desprez69c26202019-06-04 09:31:24 -070068 /** Track whether or not a test run is currently in progress */
69 private boolean mRunInProgress = false;
70
71 private Map<String, LogFile> mNonAssociatedLogFiles = new LinkedHashMap<>();
Eric Rowea6d559e2018-07-26 12:01:02 -070072
73 // Tracks if mStatusCounts are accurate, or if they need to be recalculated
Julien Desprez42aa6dd2018-12-05 15:56:52 -080074 private AtomicBoolean mIsCountDirty = new AtomicBoolean(true);
Julien Desprez0c490df2019-01-07 11:34:46 -080075 // Tracks if the expected count is accurate, or if it needs to be recalculated.
76 private AtomicBoolean mIsExpectedCountDirty = new AtomicBoolean(true);
77 private int mExpectedCount = 0;
78
Eric Rowea6d559e2018-07-26 12:01:02 -070079 // Represents the merged test results. This should not be accessed directly since it's only
80 // calculated when needed.
Julien Desprezbcd2ff32018-11-20 18:23:36 -080081 private final List<TestRunResult> mMergedTestRunResults = new ArrayList<>();
Eric Rowea6d559e2018-07-26 12:01:02 -070082 // Represents the number of tests in each TestStatus state of the merged test results. Indexed
83 // by TestStatus.ordinal()
84 private int[] mStatusCounts = new int[TestStatus.values().length];
85
Julien Desprezdc186652018-08-29 11:56:43 -070086 private MergeStrategy mStrategy = MergeStrategy.ONE_TESTCASE_PASS_IS_PASS;
87
88 /** Sets the {@link MergeStrategy} to use when merging results. */
89 public void setMergeStrategy(MergeStrategy strategy) {
90 mStrategy = strategy;
91 }
92
Brett Chabotd110d212011-01-21 20:10:17 -080093 /**
Julien Desprez9adeb512016-11-15 12:08:51 +000094 * Return the primary build info that was reported via {@link
95 * #invocationStarted(IInvocationContext)}. Primary build is the build returned by the first
Julien Desprez1047f362017-02-20 09:24:27 +000096 * build provider of the running configuration. Returns null if there is no context (no build to
97 * test case).
Brett Chabot0829eea2011-06-17 10:48:08 -070098 */
Julien Desprez9adeb512016-11-15 12:08:51 +000099 public IBuildInfo getPrimaryBuildInfo() {
Julien Desprez1047f362017-02-20 09:24:27 +0000100 if (mContext == null) {
101 return null;
102 } else {
103 return mContext.getBuildInfos().get(0);
104 }
Brett Chabot873741a2010-09-15 12:24:08 -0700105 }
106
107 /**
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700108 * Return the invocation context that was reported via {@link
109 * #invocationStarted(IInvocationContext)}
Tsu Chiang Chuangf39b58f2012-08-29 18:30:55 -0700110 */
Julien Desprezdc53cde2016-10-06 15:49:08 +0100111 public IInvocationContext getInvocationContext() {
112 return mContext;
113 }
114
115 /**
Julien Desprez9adeb512016-11-15 12:08:51 +0000116 * Returns the build info.
117 *
118 * @deprecated rely on the {@link IBuildInfo} from {@link #getInvocationContext()}.
119 */
120 @Deprecated
121 public IBuildInfo getBuildInfo() {
122 return mBuildInfo;
123 }
124
125 /**
Julien Desprezdc53cde2016-10-06 15:49:08 +0100126 * Set the build info. Should only be used for testing.
Julien Desprez9adeb512016-11-15 12:08:51 +0000127 *
128 * @deprecated Not necessary for testing anymore.
Julien Desprezdc53cde2016-10-06 15:49:08 +0100129 */
130 @VisibleForTesting
Julien Desprez9adeb512016-11-15 12:08:51 +0000131 @Deprecated
Tsu Chiang Chuangf39b58f2012-08-29 18:30:55 -0700132 public void setBuildInfo(IBuildInfo buildInfo) {
133 mBuildInfo = buildInfo;
134 }
135
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700136 /** {@inheritDoc} */
137 @Override
138 public void invocationStarted(IInvocationContext context) {
139 mContext = context;
Eric Rowea6d559e2018-07-26 12:01:02 -0700140 mBuildInfo = getPrimaryBuildInfo();
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700141 }
142
143 /** {@inheritDoc} */
144 @Override
145 public void invocationEnded(long elapsedTime) {
146 // ignore
147 }
148
149 /** {@inheritDoc} */
150 @Override
151 public void invocationFailed(Throwable cause) {
152 // ignore
153 }
154
Julien Desprez7fc54252020-04-30 15:08:45 -0700155 /** {@inheritDoc} */
156 @Override
157 public void invocationFailed(FailureDescription failure) {
158 // ignore
159 }
160
jdesprez51beee82017-08-23 15:28:42 -0700161 @Override
162 public void testModuleStarted(IInvocationContext moduleContext) {
163 mCurrentModuleContext = moduleContext;
164 }
165
166 @Override
167 public void testModuleEnded() {
168 mCurrentModuleContext = null;
169 }
170
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700171 /** {@inheritDoc} */
Brett Chabot683ee432011-12-08 11:43:50 -0800172 @Override
Brett Chabot873741a2010-09-15 12:24:08 -0700173 public void testRunStarted(String name, int numTests) {
Eric Rowea6d559e2018-07-26 12:01:02 -0700174 testRunStarted(name, numTests, 0);
175 }
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700176
Julien Desprezf94d3082019-04-23 10:45:01 -0700177 private TestRunResult getNewRunResult() {
178 TestRunResult result = new TestRunResult();
179 if (mDefaultRun) {
180 result = mCurrentTestRunResult;
181 mDefaultRun = false;
182 }
183 result.setAggregateMetrics(mIsAggregateMetrics);
184 return result;
185 }
186
Eric Rowea6d559e2018-07-26 12:01:02 -0700187 /** {@inheritDoc} */
188 @Override
189 public void testRunStarted(String name, int numTests, int attemptNumber) {
Julien Desprez5cdec9f2019-06-13 13:32:43 -0700190 testRunStarted(name, numTests, attemptNumber, System.currentTimeMillis());
191 }
192
193 /** {@inheritDoc} */
194 @Override
195 public void testRunStarted(String name, int numTests, int attemptNumber, long startTime) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800196 setCountDirty();
Julien Desprez0c490df2019-01-07 11:34:46 -0800197 // Only testRunStarted can affect the expected count.
198 mIsExpectedCountDirty.set(true);
Eric Rowea6d559e2018-07-26 12:01:02 -0700199
200 // Associate the run name with the current module context
201 if (mCurrentModuleContext != null) {
202 mModuleContextMap.put(name, mCurrentModuleContext);
203 }
204
205 // Add the list of maps if the run doesn't exist
206 if (!mTestRunResultMap.containsKey(name)) {
207 mTestRunResultMap.put(name, new LinkedList<>());
208 }
209 List<TestRunResult> results = mTestRunResultMap.get(name);
210
211 // Set the current test run result based on the attempt
212 if (attemptNumber < results.size()) {
213 if (results.get(attemptNumber) == null) {
214 throw new RuntimeException(
215 "Test run results should never be null in internal structure.");
216 }
217 } else if (attemptNumber == results.size()) {
218 // new run
Julien Desprezf94d3082019-04-23 10:45:01 -0700219 TestRunResult result = getNewRunResult();
Eric Rowea6d559e2018-07-26 12:01:02 -0700220 results.add(result);
221 } else {
Julien Desprez7ea0bd42018-12-05 09:59:28 -0800222 int size = results.size();
223 for (int i = size; i < attemptNumber; i++) {
Julien Desprezf94d3082019-04-23 10:45:01 -0700224 TestRunResult result = getNewRunResult();
Julien Desprez5cdec9f2019-06-13 13:32:43 -0700225 result.testRunStarted(name, numTests, startTime);
Julien Desprez7ea0bd42018-12-05 09:59:28 -0800226 String errorMessage =
227 String.format(
Julien Desprezd9977892020-04-14 09:01:15 -0700228 "Run attempt %s of %s did not exists, but got attempt %s."
229 + " This is a placeholder for the missing attempt.",
Julien Desprez7ea0bd42018-12-05 09:59:28 -0800230 i, name, attemptNumber);
231 result.testRunFailed(errorMessage);
232 result.testRunEnded(0L, new HashMap<String, Metric>());
233 results.add(result);
234 }
235 // New current run
Julien Desprezf94d3082019-04-23 10:45:01 -0700236 TestRunResult newResult = getNewRunResult();
Julien Desprez7ea0bd42018-12-05 09:59:28 -0800237 results.add(newResult);
Eric Rowea6d559e2018-07-26 12:01:02 -0700238 }
239 mCurrentTestRunResult = results.get(attemptNumber);
240
Julien Desprez5cdec9f2019-06-13 13:32:43 -0700241 mCurrentTestRunResult.testRunStarted(name, numTests, startTime);
Julien Desprez69c26202019-06-04 09:31:24 -0700242 mRunInProgress = true;
Brett Chabot873741a2010-09-15 12:24:08 -0700243 }
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800244
jdesprez109b1de2018-02-07 16:33:40 -0800245 /** {@inheritDoc} */
Brett Chabot45bdfd72011-09-07 18:26:56 -0700246 @Override
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700247 public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800248 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700249 mCurrentTestRunResult.testRunEnded(elapsedTime, runMetrics);
Julien Desprez69c26202019-06-04 09:31:24 -0700250 mRunInProgress = false;
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700251 }
252
253 /** {@inheritDoc} */
254 @Override
255 public void testRunFailed(String errorMessage) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800256 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700257 mCurrentTestRunResult.testRunFailed(errorMessage);
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700258 }
259
260 /** {@inheritDoc} */
261 @Override
Julien Desprezedb2a0c2020-03-18 11:10:34 -0700262 public void testRunFailed(FailureDescription failure) {
263 setCountDirty();
264 mCurrentTestRunResult.testRunFailed(failure);
265 }
266
267 /** {@inheritDoc} */
268 @Override
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700269 public void testRunStopped(long elapsedTime) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800270 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700271 mCurrentTestRunResult.testRunStopped(elapsedTime);
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700272 }
273
274 /** {@inheritDoc} */
275 @Override
jdesprez109b1de2018-02-07 16:33:40 -0800276 public void testStarted(TestDescription test) {
jdesprez4f7ade02017-04-03 11:46:11 -0700277 testStarted(test, System.currentTimeMillis());
278 }
279
280 /** {@inheritDoc} */
281 @Override
jdesprez109b1de2018-02-07 16:33:40 -0800282 public void testStarted(TestDescription test, long startTime) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800283 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700284 mCurrentTestRunResult.testStarted(test, startTime);
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800285 }
286
jdesprez109b1de2018-02-07 16:33:40 -0800287 /** {@inheritDoc} */
Brett Chabot45bdfd72011-09-07 18:26:56 -0700288 @Override
Julien Desprez6b9a9472018-05-25 14:26:46 -0700289 public void testEnded(TestDescription test, HashMap<String, Metric> testMetrics) {
290 testEnded(test, System.currentTimeMillis(), testMetrics);
291 }
292
293 /** {@inheritDoc} */
294 @Override
Julien Desprez6b9a9472018-05-25 14:26:46 -0700295 public void testEnded(TestDescription test, long endTime, HashMap<String, Metric> testMetrics) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800296 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700297 mCurrentTestRunResult.testEnded(test, endTime, testMetrics);
jdesprez4f7ade02017-04-03 11:46:11 -0700298 }
299
300 /** {@inheritDoc} */
Brett Chabot683ee432011-12-08 11:43:50 -0800301 @Override
jdesprez109b1de2018-02-07 16:33:40 -0800302 public void testFailed(TestDescription test, String trace) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800303 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700304 mCurrentTestRunResult.testFailed(test, trace);
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700305 }
306
307 @Override
Julien Despreze393c1c2020-04-07 10:34:42 -0700308 public void testFailed(TestDescription test, FailureDescription failure) {
309 setCountDirty();
310 mCurrentTestRunResult.testFailed(test, failure);
311 }
312
313 @Override
jdesprez109b1de2018-02-07 16:33:40 -0800314 public void testAssumptionFailure(TestDescription test, String trace) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800315 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700316 mCurrentTestRunResult.testAssumptionFailure(test, trace);
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700317 }
318
319 @Override
jdesprez109b1de2018-02-07 16:33:40 -0800320 public void testIgnored(TestDescription test) {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800321 setCountDirty();
Eric Rowea6d559e2018-07-26 12:01:02 -0700322 mCurrentTestRunResult.testIgnored(test);
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800323 }
324
jdesprez9c6eb222018-04-02 10:47:08 -0700325 /** {@inheritDoc} */
326 @Override
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700327 public void logAssociation(String dataName, LogFile logFile) {
Julien Desprez69c26202019-06-04 09:31:24 -0700328 if (mRunInProgress) {
329 mCurrentTestRunResult.testLogSaved(dataName, logFile);
330 } else {
331 mNonAssociatedLogFiles.put(dataName, logFile);
332 }
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800333 }
334
335 /**
Brett Chabot873741a2010-09-15 12:24:08 -0700336 * Gets the results for the current test run.
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700337 *
338 * <p>Note the results may not be complete. It is recommended to test the value of {@link
Brett Chabot53714912011-01-09 16:38:50 -0800339 * TestRunResult#isRunComplete()} and/or (@link TestRunResult#isRunFailure()} as appropriate
340 * before processing the results.
Brett Chabot4d081a92010-09-29 16:44:19 -0700341 *
342 * @return the {@link TestRunResult} representing data collected during last test run
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800343 */
Brett Chabot873741a2010-09-15 12:24:08 -0700344 public TestRunResult getCurrentRunResults() {
Eric Rowea6d559e2018-07-26 12:01:02 -0700345 return mCurrentTestRunResult;
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800346 }
347
Eric Rowea6d559e2018-07-26 12:01:02 -0700348 /** Returns the total number of complete tests for all runs. */
Brett Chabot873741a2010-09-15 12:24:08 -0700349 public int getNumTotalTests() {
Eric Rowea6d559e2018-07-26 12:01:02 -0700350 computeMergedResults();
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700351 int total = 0;
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700352 for (TestStatus s : TestStatus.values()) {
353 total += mStatusCounts[s.ordinal()];
Brett Chabotd5769e32011-09-26 19:54:40 -0700354 }
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700355 return total;
Brett Chabotfccfc612010-03-29 16:08:49 -0700356 }
357
Julien Desprez96a6a992018-08-31 10:42:29 -0700358 /**
359 * Returns the number of expected tests count. Could differ from {@link #getNumTotalTests()} if
360 * some tests did not run.
361 */
Julien Desprez0c490df2019-01-07 11:34:46 -0800362 public synchronized int getExpectedTests() {
363 // If expected count is not dirty, no need to do anything
364 if (!mIsExpectedCountDirty.compareAndSet(true, false)) {
365 return mExpectedCount;
Julien Desprez96a6a992018-08-31 10:42:29 -0700366 }
Julien Desprez0c490df2019-01-07 11:34:46 -0800367
368 computeMergedResults();
369 mExpectedCount = 0;
370 for (TestRunResult result : getMergedTestRunResults()) {
371 mExpectedCount += result.getExpectedTestCount();
372 }
373 return mExpectedCount;
Julien Desprez96a6a992018-08-31 10:42:29 -0700374 }
375
Eric Rowea6d559e2018-07-26 12:01:02 -0700376 /** Returns the number of tests in given state for this run. */
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700377 public int getNumTestsInState(TestStatus status) {
Eric Rowea6d559e2018-07-26 12:01:02 -0700378 computeMergedResults();
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700379 return mStatusCounts[status.ordinal()];
Brett Chabotfccfc612010-03-29 16:08:49 -0700380 }
381
Eric Rowea6d559e2018-07-26 12:01:02 -0700382 /** Returns if the invocation had any failed or assumption failed tests. */
Brett Chabot057e16e2010-06-27 21:53:48 -0700383 public boolean hasFailedTests() {
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700384 return getNumAllFailedTests() > 0;
Brett Chabot057e16e2010-06-27 21:53:48 -0700385 }
386
Eric Rowea6d559e2018-07-26 12:01:02 -0700387 /** Returns the total number of test runs in a failure state */
Guang Zhua4383322017-11-08 13:55:00 -0800388 public int getNumAllFailedTestRuns() {
389 int count = 0;
Eric Rowea6d559e2018-07-26 12:01:02 -0700390 for (TestRunResult result : getMergedTestRunResults()) {
391 if (result.isRunFailure()) {
Guang Zhua4383322017-11-08 13:55:00 -0800392 count++;
393 }
394 }
395 return count;
396 }
Eric Rowe6a19da72018-07-24 19:13:55 -0700397
398 /**
Eric Rowea6d559e2018-07-26 12:01:02 -0700399 * Returns the total number of tests in a failure state (only failed, assumption failures do not
400 * count toward it).
401 */
402 public int getNumAllFailedTests() {
403 return getNumTestsInState(TestStatus.FAILURE);
404 }
405
406 /**
Eric Rowe6a19da72018-07-24 19:13:55 -0700407 * Return the merged collection of results for all runs across different attempts.
408 *
409 * <p>If there are multiple results, each test run is merged, with the latest test result
410 * overwriting test results of previous runs. Test runs are ordered by attempt number.
411 *
412 * <p>Metrics for the same attempt will be merged based on the preference set by {@code
413 * aggregate-metrics}. The final metrics will be the metrics of the last attempt.
414 */
Eric Rowea6d559e2018-07-26 12:01:02 -0700415 public List<TestRunResult> getMergedTestRunResults() {
416 computeMergedResults();
Julien Desprezbcd2ff32018-11-20 18:23:36 -0800417 return new ArrayList<>(mMergedTestRunResults);
Eric Rowea6d559e2018-07-26 12:01:02 -0700418 }
419
420 /**
421 * Returns the results for all test runs.
422 *
423 * @deprecated Use {@link #getMergedTestRunResults()}
424 */
425 @Deprecated
426 public Collection<TestRunResult> getRunResults() {
427 return getMergedTestRunResults();
428 }
429
430 /**
431 * Computes and stores the merged results and the total status counts since both operations are
432 * expensive.
433 */
Julien Desprezbcd2ff32018-11-20 18:23:36 -0800434 private synchronized void computeMergedResults() {
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800435 // If not dirty, nothing to be done
436 if (!mIsCountDirty.compareAndSet(true, false)) {
Eric Rowea6d559e2018-07-26 12:01:02 -0700437 return;
438 }
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800439
Julien Desprezbcd2ff32018-11-20 18:23:36 -0800440 mMergedTestRunResults.clear();
Julien Desprez93a041b2018-08-29 08:55:27 -0700441 // Merge results
442 if (mTestRunResultMap.isEmpty() && mCurrentTestRunResult.isRunFailure()) {
Julien Desprezbcd2ff32018-11-20 18:23:36 -0800443 // In case of early failure that is a bit untracked, still add it to the list to
444 // not loose it.
Julien Desprezd9977892020-04-14 09:01:15 -0700445 CLog.e(
446 "Early failure resulting in no testRunStart. Results might be inconsistent:"
447 + "\n%s",
448 mCurrentTestRunResult.getRunFailureMessage());
Julien Desprez93a041b2018-08-29 08:55:27 -0700449 mMergedTestRunResults.add(mCurrentTestRunResult);
450 } else {
Julien Desprez7ea0bd42018-12-05 09:59:28 -0800451 for (Entry<String, List<TestRunResult>> results : mTestRunResultMap.entrySet()) {
452 TestRunResult res = TestRunResult.merge(results.getValue(), mStrategy);
453 if (res == null) {
454 // Merge can return null in case of results being empty.
455 CLog.w("No results for %s", results.getKey());
456 } else {
457 mMergedTestRunResults.add(res);
458 }
Julien Desprez93a041b2018-08-29 08:55:27 -0700459 }
Eric Rowea6d559e2018-07-26 12:01:02 -0700460 }
Eric Rowea6d559e2018-07-26 12:01:02 -0700461 // Reset counts
462 for (TestStatus s : TestStatus.values()) {
463 mStatusCounts[s.ordinal()] = 0;
464 }
465
466 // Calculate results
467 for (TestRunResult result : mMergedTestRunResults) {
468 for (TestStatus s : TestStatus.values()) {
469 mStatusCounts[s.ordinal()] += result.getNumTestsInState(s);
470 }
471 }
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800472 }
Eric Rowea6d559e2018-07-26 12:01:02 -0700473
Julien Desprez42aa6dd2018-12-05 15:56:52 -0800474 /**
475 * Keep dirty count as AtomicBoolean to ensure when accessed from another thread the state is
476 * consistent.
477 */
478 private void setCountDirty() {
479 mIsCountDirty.set(true);
Eric Rowe6a19da72018-07-24 19:13:55 -0700480 }
481
482 /**
483 * Return all the names for all the test runs.
484 *
485 * <p>These test runs may have run multiple times with different attempts.
486 */
487 public Collection<String> getTestRunNames() {
Eric Rowea6d559e2018-07-26 12:01:02 -0700488 return new ArrayList<String>(mTestRunResultMap.keySet());
Eric Rowe6a19da72018-07-24 19:13:55 -0700489 }
490
491 /**
Eric Rowea6d559e2018-07-26 12:01:02 -0700492 * Gets all the attempts for a {@link TestRunResult} of a given test run.
493 *
494 * @param testRunName The name given by {{@link #testRunStarted(String, int)}.
495 * @return All {@link TestRunResult} for a given test run, ordered by attempts.
496 */
497 public List<TestRunResult> getTestRunAttempts(String testRunName) {
498 return mTestRunResultMap.get(testRunName);
499 }
500
501 /**
Julien Desprez2e19eaa2019-07-01 15:29:17 -0700502 * Gets all the results for a given attempt.
503 *
504 * @param attempt The attempt we want results for.
505 * @return All {@link TestRunResult} for a given attempt.
506 */
507 public List<TestRunResult> getTestRunForAttempts(int attempt) {
508 List<TestRunResult> allResultForAttempts = new ArrayList<>();
509 for (Entry<String, List<TestRunResult>> runInfo : mTestRunResultMap.entrySet()) {
510 if (attempt < runInfo.getValue().size()) {
511 TestRunResult attemptRes = runInfo.getValue().get(attempt);
512 allResultForAttempts.add(attemptRes);
513 }
514 }
515 return allResultForAttempts;
516 }
517
518 /**
Eric Rowea6d559e2018-07-26 12:01:02 -0700519 * Returns whether a given test run name has any results.
Eric Rowe6a19da72018-07-24 19:13:55 -0700520 *
521 * @param testRunName The name given by {{@link #testRunStarted(String, int)}.
522 */
Eric Rowea6d559e2018-07-26 12:01:02 -0700523 public boolean hasTestRunResultsForName(String testRunName) {
524 return mTestRunResultMap.containsKey(testRunName);
525 }
526
527 /**
528 * Returns the number of attempts for a given test run name.
529 *
530 * @param testRunName The name given by {{@link #testRunStarted(String, int)}.
531 */
532 public int getTestRunAttemptCount(String testRunName) {
533 List<TestRunResult> results = mTestRunResultMap.get(testRunName);
534 if (results == null) {
535 return 0;
536 }
537 return results.size();
Eric Rowe6a19da72018-07-24 19:13:55 -0700538 }
539
540 /**
541 * Return the {@link TestRunResult} for a single attempt.
542 *
543 * @param testRunName The name given by {{@link #testRunStarted(String, int)}.
544 * @param attempt The attempt id.
Eric Rowea6d559e2018-07-26 12:01:02 -0700545 * @return The {@link TestRunResult} for the given name and attempt id or {@code null} if it
546 * does not exist.
Eric Rowe6a19da72018-07-24 19:13:55 -0700547 */
548 public TestRunResult getTestRunAtAttempt(String testRunName, int attempt) {
Eric Rowea6d559e2018-07-26 12:01:02 -0700549 List<TestRunResult> results = mTestRunResultMap.get(testRunName);
550 if (results == null || attempt < 0 || attempt >= results.size()) {
551 return null;
552 }
553
554 return results.get(attempt);
Eric Rowe6a19da72018-07-24 19:13:55 -0700555 }
556
557 /**
Eric Rowea6d559e2018-07-26 12:01:02 -0700558 * Returns the {@link IInvocationContext} of the module associated with the results.
Eric Rowe6a19da72018-07-24 19:13:55 -0700559 *
560 * @param testRunName The name given by {{@link #testRunStarted(String, int)}.
Eric Rowea6d559e2018-07-26 12:01:02 -0700561 * @return The {@link IInvocationContext} of the module for a given test run name {@code null}
562 * if there are no results for that name.
Eric Rowe6a19da72018-07-24 19:13:55 -0700563 */
Eric Rowea6d559e2018-07-26 12:01:02 -0700564 public IInvocationContext getModuleContextForRunResult(String testRunName) {
565 return mModuleContextMap.get(testRunName);
Eric Rowe7d8e0c62018-07-25 14:27:41 -0700566 }
Julien Desprez69c26202019-06-04 09:31:24 -0700567
568 /** Returns a copy of the map containing all the logged file not associated with a test run. */
569 public Map<String, LogFile> getNonAssociatedLogFiles() {
570 return new LinkedHashMap<>(mNonAssociatedLogFiles);
571 }
Julien Despreze4abca32019-07-08 14:24:02 -0700572
573 /**
574 * Allows to clear the results for a given run name. Should only be used in some cases like the
575 * aggregator of results.
576 */
577 protected final synchronized void clearResultsForName(String testRunName) {
578 setCountDirty();
579 mTestRunResultMap.remove(testRunName);
580 }
Brett Chabot51a4e3d2010-03-09 11:08:44 -0800581}