blob: 5805a3b05e54ba73dc5592bf00dd629cab20008c [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;
jdesprez2ffb1bd2018-03-19 10:37:23 -070023import com.android.tradefed.result.ILogSaverListener;
Brett Chabotb1052c22011-01-12 19:44:23 -080024import com.android.tradefed.result.ITestInvocationListener;
Omari Stephens41978ee2011-02-23 19:03:05 -080025import com.android.tradefed.result.InputStreamSource;
Brett Chabotb1052c22011-01-12 19:44:23 -080026import com.android.tradefed.result.LogDataType;
jdesprez2ffb1bd2018-03-19 10:37:23 -070027import com.android.tradefed.result.LogFile;
jdesprez109b1de2018-02-07 16:33:40 -080028import com.android.tradefed.result.TestDescription;
jdesprez821a0052018-01-16 03:37:16 -080029import com.android.tradefed.result.TestResult;
30import com.android.tradefed.result.TestRunResult;
jdesprez0b8464a2017-06-26 17:33:58 -070031import com.android.tradefed.util.TimeUtil;
Brett Chabotb1052c22011-01-12 19:44:23 -080032
jdesprez0b8464a2017-06-26 17:33:58 -070033import java.util.Collection;
Julien Desprez74089e12018-05-31 01:47:53 -070034import java.util.HashMap;
Brett Chabotb1052c22011-01-12 19:44:23 -080035import java.util.Map;
Julien Despreze6b756b2018-04-16 10:56:38 -070036import java.util.Map.Entry;
Brett Chabotb1052c22011-01-12 19:44:23 -080037
38/**
39 * A {@link ITestInvocationListener} that collects results from a invocation shard (aka an
40 * invocation split to run on multiple resources in parallel), and forwards them to another
41 * listener.
42 */
Julien Desprez453d8892017-02-06 14:13:34 +000043public class ShardListener extends CollectingTestListener {
Brett Chabotb1052c22011-01-12 19:44:23 -080044
45 private ITestInvocationListener mMasterListener;
Julien Desprezf97eb342019-05-28 11:49:54 -070046 private IInvocationContext mModuleContext = null;
Brett Chabotb1052c22011-01-12 19:44:23 -080047
48 /**
49 * Create a {@link ShardListener}.
50 *
Julien Desprez453d8892017-02-06 14:13:34 +000051 * @param master the {@link ITestInvocationListener} the results should be forwarded. To prevent
52 * collisions with other {@link ShardListener}s, this object will synchronize on
53 * <var>master</var> when forwarding results. And results will only be sent once the
54 * invocation shard completes.
Brett Chabotb1052c22011-01-12 19:44:23 -080055 */
Julien Desprez453d8892017-02-06 14:13:34 +000056 public ShardListener(ITestInvocationListener master) {
Brett Chabotb1052c22011-01-12 19:44:23 -080057 mMasterListener = master;
58 }
59
60 /**
61 * {@inheritDoc}
62 */
Brett Chabotb1052c22011-01-12 19:44:23 -080063 @Override
Julien Desprez53353e62016-08-12 15:24:33 +010064 public void invocationStarted(IInvocationContext context) {
65 super.invocationStarted(context);
66 synchronized (mMasterListener) {
67 mMasterListener.invocationStarted(context);
68 }
69 }
70
71 /**
72 * {@inheritDoc}
73 */
74 @Override
Brett Chabotb1052c22011-01-12 19:44:23 -080075 public void invocationFailed(Throwable cause) {
76 super.invocationFailed(cause);
77 synchronized (mMasterListener) {
78 mMasterListener.invocationFailed(cause);
79 }
80 }
81
82 /**
83 * {@inheritDoc}
84 */
85 @Override
Omari Stephens41978ee2011-02-23 19:03:05 -080086 public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
Julien Despreze6b756b2018-04-16 10:56:38 -070087 // forward testLog results immediately, since result reporters might take action on it.
Brett Chabotb1052c22011-01-12 19:44:23 -080088 synchronized (mMasterListener) {
Julien Despreze6b756b2018-04-16 10:56:38 -070089 if (mMasterListener instanceof ShardMasterResultForwarder) {
90 // If the listener is a log saver, we should simply forward the testLog not save
91 // again.
92 ((ShardMasterResultForwarder) mMasterListener)
93 .testLogForward(dataName, dataType, dataStream);
94 } else {
95 mMasterListener.testLog(dataName, dataType, dataStream);
96 }
Brett Chabotb1052c22011-01-12 19:44:23 -080097 }
98 }
99
jdesprez2ffb1bd2018-03-19 10:37:23 -0700100 /** {@inheritDoc} */
101 @Override
102 public void testLogSaved(
103 String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile) {
104 super.testLogSaved(dataName, dataType, dataStream, logFile);
105 // Forward the testLogSaved callback.
106 synchronized (mMasterListener) {
107 if (mMasterListener instanceof ILogSaverListener) {
108 ((ILogSaverListener) mMasterListener)
109 .testLogSaved(dataName, dataType, dataStream, logFile);
110 }
111 }
112 }
113
114 /** {@inheritDoc} */
Brett Chabotb1052c22011-01-12 19:44:23 -0800115 @Override
Julien Desprezf97eb342019-05-28 11:49:54 -0700116 public void testModuleStarted(IInvocationContext moduleContext) {
117 super.testModuleStarted(moduleContext);
118 mModuleContext = moduleContext;
Jed Estep1db79d12015-12-14 18:18:51 -0800119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 @Override
125 public void testRunFailed(String failureMessage) {
126 super.testRunFailed(failureMessage);
127 CLog.logAndDisplay(LogLevel.ERROR, "FAILED: %s failed with message: %s",
128 getCurrentRunResults().getName(), failureMessage);
129 }
130
Julien Desprezf97eb342019-05-28 11:49:54 -0700131 /** {@inheritDoc} */
Jed Estep1db79d12015-12-14 18:18:51 -0800132 @Override
Julien Desprezf97eb342019-05-28 11:49:54 -0700133 public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
134 super.testRunEnded(elapsedTime, runMetrics);
135 CLog.logAndDisplay(
136 LogLevel.INFO, "Sharded test completed: %s", getCurrentRunResults().getName());
137 if (mModuleContext == null) {
138 // testRunEnded only forwards if it's not part of a module. If it's a module
139 // testModuleEnded is in charge of forwarding all run results.
140 synchronized (mMasterListener) {
141 forwardRunResults(getCurrentRunResults());
142 }
143 }
144 }
145
146 /** {@inheritDoc} */
147 @Override
148 public void testModuleEnded() {
149 super.testModuleEnded();
150
Brett Chabotb1052c22011-01-12 19:44:23 -0800151 synchronized (mMasterListener) {
jdesprez51beee82017-08-23 15:28:42 -0700152 IInvocationContext moduleContext = null;
Julien Desprezf97eb342019-05-28 11:49:54 -0700153 // TODO: Support attempts and retries
Eric Rowea6d559e2018-07-26 12:01:02 -0700154 for (TestRunResult runResult : getMergedTestRunResults()) {
Julien Desprezf97eb342019-05-28 11:49:54 -0700155 // Only consider run results of the module in progress
156 if (getModuleContextForRunResult(runResult.getName()) != mModuleContext) {
157 continue;
158 }
Eric Rowea6d559e2018-07-26 12:01:02 -0700159
jdesprez51beee82017-08-23 15:28:42 -0700160 // Stop or start the module
161 if (moduleContext != null
Eric Rowea6d559e2018-07-26 12:01:02 -0700162 && !getModuleContextForRunResult(runResult.getName())
163 .equals(moduleContext)) {
jdesprez51beee82017-08-23 15:28:42 -0700164 mMasterListener.testModuleEnded();
165 moduleContext = null;
166 }
Eric Rowea6d559e2018-07-26 12:01:02 -0700167 if (moduleContext == null
168 && getModuleContextForRunResult(runResult.getName()) != null) {
169 moduleContext = getModuleContextForRunResult(runResult.getName());
jdesprez51beee82017-08-23 15:28:42 -0700170 mMasterListener.testModuleStarted(moduleContext);
171 }
Julien Desprezf97eb342019-05-28 11:49:54 -0700172 // Forward the run level results
173 forwardRunResults(runResult);
Brett Chabotb1052c22011-01-12 19:44:23 -0800174 }
jdesprez51beee82017-08-23 15:28:42 -0700175 // Close the last module
176 if (moduleContext != null) {
177 mMasterListener.testModuleEnded();
178 moduleContext = null;
179 }
Julien Desprezf97eb342019-05-28 11:49:54 -0700180 }
181 mModuleContext = null;
182 }
183
184 /** {@inheritDoc} */
185 @Override
186 public void invocationEnded(long elapsedTime) {
187 super.invocationEnded(elapsedTime);
188 synchronized (mMasterListener) {
189 logShardContent(getMergedTestRunResults());
Julien Desprez69c26202019-06-04 09:31:24 -0700190 // Report all logs not associated with test runs
191 forwardLogAssociation(getNonAssociatedLogFiles(), mMasterListener);
Brett Chabotb1052c22011-01-12 19:44:23 -0800192 mMasterListener.invocationEnded(elapsedTime);
193 }
194 }
195
Julien Desprezf97eb342019-05-28 11:49:54 -0700196 private void forwardRunResults(TestRunResult runResult) {
197 // TODO: Support attempts and retries
198 mMasterListener.testRunStarted(runResult.getName(), runResult.getExpectedTestCount());
199 forwardTestResults(runResult.getTestResults());
200 if (runResult.isRunFailure()) {
201 mMasterListener.testRunFailed(runResult.getRunFailureMessage());
202 }
203
204 // Provide a strong association of the run to its logs.
205 forwardLogAssociation(runResult.getRunLoggedFiles(), mMasterListener);
206
207 mMasterListener.testRunEnded(runResult.getElapsedTime(), runResult.getRunProtoMetrics());
208 }
209
jdesprez109b1de2018-02-07 16:33:40 -0800210 private void forwardTestResults(Map<TestDescription, TestResult> testResults) {
211 for (Map.Entry<TestDescription, TestResult> testEntry : testResults.entrySet()) {
jdesprez4f7ade02017-04-03 11:46:11 -0700212 mMasterListener.testStarted(testEntry.getKey(), testEntry.getValue().getStartTime());
Brett Chabot2ea6ede2014-08-04 11:15:19 -0700213 switch (testEntry.getValue().getStatus()) {
214 case FAILURE:
215 mMasterListener.testFailed(testEntry.getKey(),
216 testEntry.getValue().getStackTrace());
217 break;
218 case ASSUMPTION_FAILURE:
219 mMasterListener.testAssumptionFailure(testEntry.getKey(),
220 testEntry.getValue().getStackTrace());
221 break;
222 case IGNORED:
223 mMasterListener.testIgnored(testEntry.getKey());
224 break;
Julien Desprez2763ca02016-12-06 14:39:33 +0000225 default:
226 break;
Brett Chabotb1052c22011-01-12 19:44:23 -0800227 }
Julien Despreze6b756b2018-04-16 10:56:38 -0700228 // Provide a strong association of the test to its logs.
229 forwardLogAssociation(testEntry.getValue().getLoggedFiles(), mMasterListener);
230
Brett Chabote3b8b9e2011-10-21 14:17:06 -0700231 if (!testEntry.getValue().getStatus().equals(TestStatus.INCOMPLETE)) {
jdesprez4f7ade02017-04-03 11:46:11 -0700232 mMasterListener.testEnded(
233 testEntry.getKey(),
234 testEntry.getValue().getEndTime(),
Julien Desprez04db83b2018-04-25 06:06:36 -0700235 testEntry.getValue().getProtoMetrics());
Brett Chabote3b8b9e2011-10-21 14:17:06 -0700236 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800237 }
238 }
jdesprez0b8464a2017-06-26 17:33:58 -0700239
Julien Despreze6b756b2018-04-16 10:56:38 -0700240 /** Forward to the listener the logAssociated callback on the files. */
241 private void forwardLogAssociation(
242 Map<String, LogFile> loggedFiles, ITestInvocationListener listener) {
243 for (Entry<String, LogFile> logFile : loggedFiles.entrySet()) {
244 if (listener instanceof ILogSaverListener) {
245 ((ILogSaverListener) listener).logAssociation(logFile.getKey(), logFile.getValue());
246 }
247 }
248 }
249
jdesprez0b8464a2017-06-26 17:33:58 -0700250 /** Log the content of the shard for easier debugging. */
251 private void logShardContent(Collection<TestRunResult> listResults) {
252 CLog.d("=================================================");
253 CLog.d(
254 "========== Shard Primary Device %s ==========",
255 getInvocationContext().getDevices().get(0).getSerialNumber());
256 for (TestRunResult runRes : listResults) {
257 CLog.d(
258 "\tRan '%s' in %s",
259 runRes.getName(), TimeUtil.formatElapsedTime(runRes.getElapsedTime()));
260 }
261 CLog.d("=================================================");
262 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800263}