blob: 71ad448350cda90a73569636b16b90a48d193ff8 [file] [log] [blame]
Brett Chabotb1052c22011-01-12 19:44:23 -08001/*
2 * Copyright (C) 2011 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.invoker;
17
Jed Estep1db79d12015-12-14 18:18:51 -080018import com.android.ddmlib.Log.LogLevel;
Brett Chabot2ea6ede2014-08-04 11:15:19 -070019import com.android.ddmlib.testrunner.TestResult.TestStatus;
Jed Estep1db79d12015-12-14 18:18:51 -080020import com.android.tradefed.log.LogUtil.CLog;
Julien Desprez74089e12018-05-31 01:47:53 -070021import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
Brett Chabotb1052c22011-01-12 19:44:23 -080022import com.android.tradefed.result.CollectingTestListener;
Julien Desprez8ee09232020-02-06 10:30:11 -080023import com.android.tradefed.result.FailureDescription;
jdesprez2ffb1bd2018-03-19 10:37:23 -070024import com.android.tradefed.result.ILogSaverListener;
Brett Chabotb1052c22011-01-12 19:44:23 -080025import com.android.tradefed.result.ITestInvocationListener;
Omari Stephens41978ee2011-02-23 19:03:05 -080026import com.android.tradefed.result.InputStreamSource;
Brett Chabotb1052c22011-01-12 19:44:23 -080027import com.android.tradefed.result.LogDataType;
jdesprez2ffb1bd2018-03-19 10:37:23 -070028import com.android.tradefed.result.LogFile;
jdesprez109b1de2018-02-07 16:33:40 -080029import com.android.tradefed.result.TestDescription;
jdesprez821a0052018-01-16 03:37:16 -080030import com.android.tradefed.result.TestResult;
31import com.android.tradefed.result.TestRunResult;
Julien Desprez9ce5fa32019-08-02 14:49:29 -070032import com.android.tradefed.result.retry.ISupportGranularResults;
jdesprez0b8464a2017-06-26 17:33:58 -070033import com.android.tradefed.util.TimeUtil;
Brett Chabotb1052c22011-01-12 19:44:23 -080034
Julien Desprez9ce5fa32019-08-02 14:49:29 -070035import java.util.ArrayList;
jdesprez0b8464a2017-06-26 17:33:58 -070036import java.util.Collection;
Julien Desprez74089e12018-05-31 01:47:53 -070037import java.util.HashMap;
Julien Desprez9ce5fa32019-08-02 14:49:29 -070038import java.util.List;
Brett Chabotb1052c22011-01-12 19:44:23 -080039import java.util.Map;
Julien Despreze6b756b2018-04-16 10:56:38 -070040import java.util.Map.Entry;
Brett Chabotb1052c22011-01-12 19:44:23 -080041
42/**
43 * A {@link ITestInvocationListener} that collects results from a invocation shard (aka an
44 * invocation split to run on multiple resources in parallel), and forwards them to another
45 * listener.
46 */
Julien Desprez9ce5fa32019-08-02 14:49:29 -070047public class ShardListener extends CollectingTestListener implements ISupportGranularResults {
Brett Chabotb1052c22011-01-12 19:44:23 -080048
49 private ITestInvocationListener mMasterListener;
Julien Desprezf97eb342019-05-28 11:49:54 -070050 private IInvocationContext mModuleContext = null;
Julien Desprez9ce5fa32019-08-02 14:49:29 -070051 private int mAttemptInProgress = 0;
52 private boolean mEnableGranularResults = false;
Brett Chabotb1052c22011-01-12 19:44:23 -080053
54 /**
55 * Create a {@link ShardListener}.
56 *
Julien Desprez453d8892017-02-06 14:13:34 +000057 * @param master the {@link ITestInvocationListener} the results should be forwarded. To prevent
58 * collisions with other {@link ShardListener}s, this object will synchronize on
59 * <var>master</var> when forwarding results. And results will only be sent once the
60 * invocation shard completes.
Brett Chabotb1052c22011-01-12 19:44:23 -080061 */
Julien Desprez453d8892017-02-06 14:13:34 +000062 public ShardListener(ITestInvocationListener master) {
Brett Chabotb1052c22011-01-12 19:44:23 -080063 mMasterListener = master;
64 }
65
Julien Desprez9ce5fa32019-08-02 14:49:29 -070066 /** {@inheritDoc} */
67 @Override
68 public boolean supportGranularResults() {
69 return mEnableGranularResults;
70 }
71
72 public void setSupportGranularResults(boolean enableGranularResults) {
73 mEnableGranularResults = enableGranularResults;
74 }
75
Brett Chabotb1052c22011-01-12 19:44:23 -080076 /**
77 * {@inheritDoc}
78 */
Brett Chabotb1052c22011-01-12 19:44:23 -080079 @Override
Julien Desprez53353e62016-08-12 15:24:33 +010080 public void invocationStarted(IInvocationContext context) {
81 super.invocationStarted(context);
82 synchronized (mMasterListener) {
83 mMasterListener.invocationStarted(context);
84 }
85 }
86
87 /**
88 * {@inheritDoc}
89 */
90 @Override
Brett Chabotb1052c22011-01-12 19:44:23 -080091 public void invocationFailed(Throwable cause) {
92 super.invocationFailed(cause);
93 synchronized (mMasterListener) {
94 mMasterListener.invocationFailed(cause);
95 }
96 }
97
Julien Desprez7fc54252020-04-30 15:08:45 -070098 /** {@inheritDoc} */
99 @Override
100 public void invocationFailed(FailureDescription failure) {
101 super.invocationFailed(failure);
102 synchronized (mMasterListener) {
103 mMasterListener.invocationFailed(failure);
104 }
105 }
106
Brett Chabotb1052c22011-01-12 19:44:23 -0800107 /**
108 * {@inheritDoc}
109 */
110 @Override
Omari Stephens41978ee2011-02-23 19:03:05 -0800111 public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
Julien Despreze6b756b2018-04-16 10:56:38 -0700112 // forward testLog results immediately, since result reporters might take action on it.
Brett Chabotb1052c22011-01-12 19:44:23 -0800113 synchronized (mMasterListener) {
Julien Despreze6b756b2018-04-16 10:56:38 -0700114 if (mMasterListener instanceof ShardMasterResultForwarder) {
115 // If the listener is a log saver, we should simply forward the testLog not save
116 // again.
117 ((ShardMasterResultForwarder) mMasterListener)
118 .testLogForward(dataName, dataType, dataStream);
119 } else {
120 mMasterListener.testLog(dataName, dataType, dataStream);
121 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800122 }
123 }
124
jdesprez2ffb1bd2018-03-19 10:37:23 -0700125 /** {@inheritDoc} */
126 @Override
127 public void testLogSaved(
128 String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile) {
129 super.testLogSaved(dataName, dataType, dataStream, logFile);
130 // Forward the testLogSaved callback.
131 synchronized (mMasterListener) {
132 if (mMasterListener instanceof ILogSaverListener) {
133 ((ILogSaverListener) mMasterListener)
134 .testLogSaved(dataName, dataType, dataStream, logFile);
135 }
136 }
137 }
138
139 /** {@inheritDoc} */
Brett Chabotb1052c22011-01-12 19:44:23 -0800140 @Override
Julien Desprezf97eb342019-05-28 11:49:54 -0700141 public void testModuleStarted(IInvocationContext moduleContext) {
142 super.testModuleStarted(moduleContext);
143 mModuleContext = moduleContext;
Jed Estep1db79d12015-12-14 18:18:51 -0800144 }
145
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700146 /** {@inheritDoc} */
147 @Override
148 public void testRunStarted(String name, int numTests, int attemptNumber, long startTime) {
149 super.testRunStarted(name, numTests, attemptNumber, startTime);
150 mAttemptInProgress = attemptNumber;
151 }
152
Jed Estep1db79d12015-12-14 18:18:51 -0800153 /**
154 * {@inheritDoc}
155 */
156 @Override
157 public void testRunFailed(String failureMessage) {
158 super.testRunFailed(failureMessage);
159 CLog.logAndDisplay(LogLevel.ERROR, "FAILED: %s failed with message: %s",
160 getCurrentRunResults().getName(), failureMessage);
161 }
162
Julien Desprezf97eb342019-05-28 11:49:54 -0700163 /** {@inheritDoc} */
Jed Estep1db79d12015-12-14 18:18:51 -0800164 @Override
Julien Desprez8ee09232020-02-06 10:30:11 -0800165 public void testRunFailed(FailureDescription failure) {
166 super.testRunFailed(failure);
167 CLog.logAndDisplay(
168 LogLevel.ERROR,
169 "FAILED: %s failed with message: %s",
170 getCurrentRunResults().getName(),
171 failure.toString());
172 }
173
174 /** {@inheritDoc} */
175 @Override
Julien Desprezf97eb342019-05-28 11:49:54 -0700176 public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
177 super.testRunEnded(elapsedTime, runMetrics);
178 CLog.logAndDisplay(
179 LogLevel.INFO, "Sharded test completed: %s", getCurrentRunResults().getName());
180 if (mModuleContext == null) {
181 // testRunEnded only forwards if it's not part of a module. If it's a module
182 // testModuleEnded is in charge of forwarding all run results.
183 synchronized (mMasterListener) {
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700184 forwardRunResults(getCurrentRunResults(), mAttemptInProgress);
Julien Desprezf97eb342019-05-28 11:49:54 -0700185 }
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700186 mAttemptInProgress = 0;
Julien Desprezf97eb342019-05-28 11:49:54 -0700187 }
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700188
Julien Desprezf97eb342019-05-28 11:49:54 -0700189 }
190
191 /** {@inheritDoc} */
192 @Override
193 public void testModuleEnded() {
194 super.testModuleEnded();
195
Brett Chabotb1052c22011-01-12 19:44:23 -0800196 synchronized (mMasterListener) {
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700197 mMasterListener.testModuleStarted(mModuleContext);
198 List<String> resultNames = new ArrayList<String>();
199 if (mEnableGranularResults) {
200 for (int i = 0; i < mAttemptInProgress + 1; i++) {
201 List<TestRunResult> runResults = getTestRunForAttempts(i);
202 for (TestRunResult runResult : runResults) {
203 forwardRunResults(runResult, i);
204 resultNames.add(runResult.getName());
205 }
Julien Desprezf97eb342019-05-28 11:49:54 -0700206 }
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700207 } else {
208 for (TestRunResult runResult : getMergedTestRunResults()) {
209 // Forward the run level results
210 forwardRunResults(runResult, 0);
211 resultNames.add(runResult.getName());
212 }
213 }
Eric Rowea6d559e2018-07-26 12:01:02 -0700214
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700215 // Ensure we don't carry results from one module to another.
216 for (String name : resultNames) {
217 clearResultsForName(name);
Brett Chabotb1052c22011-01-12 19:44:23 -0800218 }
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700219 mMasterListener.testModuleEnded();
Julien Desprezf97eb342019-05-28 11:49:54 -0700220 }
221 mModuleContext = null;
222 }
223
224 /** {@inheritDoc} */
225 @Override
226 public void invocationEnded(long elapsedTime) {
227 super.invocationEnded(elapsedTime);
228 synchronized (mMasterListener) {
229 logShardContent(getMergedTestRunResults());
Julien Desprez69c26202019-06-04 09:31:24 -0700230 // Report all logs not associated with test runs
231 forwardLogAssociation(getNonAssociatedLogFiles(), mMasterListener);
Brett Chabotb1052c22011-01-12 19:44:23 -0800232 mMasterListener.invocationEnded(elapsedTime);
233 }
234 }
235
Julien Desprez9ce5fa32019-08-02 14:49:29 -0700236 private void forwardRunResults(TestRunResult runResult, int attempt) {
237 mMasterListener.testRunStarted(
238 runResult.getName(),
239 runResult.getExpectedTestCount(),
240 attempt,
241 runResult.getStartTime());
Julien Desprezf97eb342019-05-28 11:49:54 -0700242 forwardTestResults(runResult.getTestResults());
243 if (runResult.isRunFailure()) {
Julien Desprezead2e462020-02-18 14:54:12 -0800244 mMasterListener.testRunFailed(runResult.getRunFailureDescription());
Julien Desprezf97eb342019-05-28 11:49:54 -0700245 }
246
247 // Provide a strong association of the run to its logs.
248 forwardLogAssociation(runResult.getRunLoggedFiles(), mMasterListener);
249
250 mMasterListener.testRunEnded(runResult.getElapsedTime(), runResult.getRunProtoMetrics());
251 }
252
jdesprez109b1de2018-02-07 16:33:40 -0800253 private void forwardTestResults(Map<TestDescription, TestResult> testResults) {
254 for (Map.Entry<TestDescription, TestResult> testEntry : testResults.entrySet()) {
jdesprez4f7ade02017-04-03 11:46:11 -0700255 mMasterListener.testStarted(testEntry.getKey(), testEntry.getValue().getStartTime());
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700256 switch (testEntry.getValue().getStatus()) {
257 case FAILURE:
Julien Desprezead2e462020-02-18 14:54:12 -0800258 mMasterListener.testFailed(
259 testEntry.getKey(), testEntry.getValue().getFailure());
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700260 break;
261 case ASSUMPTION_FAILURE:
262 mMasterListener.testAssumptionFailure(testEntry.getKey(),
263 testEntry.getValue().getStackTrace());
264 break;
265 case IGNORED:
266 mMasterListener.testIgnored(testEntry.getKey());
267 break;
Julien Desprez2763ca02016-12-06 14:39:33 +0000268 default:
269 break;
Brett Chabotb1052c22011-01-12 19:44:23 -0800270 }
Julien Despreze6b756b2018-04-16 10:56:38 -0700271 // Provide a strong association of the test to its logs.
272 forwardLogAssociation(testEntry.getValue().getLoggedFiles(), mMasterListener);
273
Brett Chabote3b8b9e2011-10-21 14:17:06 -0700274 if (!testEntry.getValue().getStatus().equals(TestStatus.INCOMPLETE)) {
jdesprez4f7ade02017-04-03 11:46:11 -0700275 mMasterListener.testEnded(
276 testEntry.getKey(),
277 testEntry.getValue().getEndTime(),
Julien Desprez04db83b2018-04-25 06:06:36 -0700278 testEntry.getValue().getProtoMetrics());
Brett Chabote3b8b9e2011-10-21 14:17:06 -0700279 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800280 }
281 }
jdesprez0b8464a2017-06-26 17:33:58 -0700282
Julien Despreze6b756b2018-04-16 10:56:38 -0700283 /** Forward to the listener the logAssociated callback on the files. */
284 private void forwardLogAssociation(
285 Map<String, LogFile> loggedFiles, ITestInvocationListener listener) {
286 for (Entry<String, LogFile> logFile : loggedFiles.entrySet()) {
287 if (listener instanceof ILogSaverListener) {
288 ((ILogSaverListener) listener).logAssociation(logFile.getKey(), logFile.getValue());
289 }
290 }
291 }
292
jdesprez0b8464a2017-06-26 17:33:58 -0700293 /** Log the content of the shard for easier debugging. */
294 private void logShardContent(Collection<TestRunResult> listResults) {
295 CLog.d("=================================================");
296 CLog.d(
297 "========== Shard Primary Device %s ==========",
298 getInvocationContext().getDevices().get(0).getSerialNumber());
299 for (TestRunResult runRes : listResults) {
300 CLog.d(
301 "\tRan '%s' in %s",
302 runRes.getName(), TimeUtil.formatElapsedTime(runRes.getElapsedTime()));
303 }
304 CLog.d("=================================================");
305 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800306}