blob: 1e4b985cede1769be7acb4097c2ce4044564bcf4 [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
jdesprez5c272662017-11-28 02:42:27 -080018import com.android.tradefed.build.IBuildInfo;
jdesprezc9f81492017-05-30 15:59:05 -070019import com.android.tradefed.log.LogUtil.CLog;
Julien Desprez7fc54252020-04-30 15:08:45 -070020import com.android.tradefed.result.FailureDescription;
jdesprez2ffb1bd2018-03-19 10:37:23 -070021import com.android.tradefed.result.ILogSaverListener;
Brett Chabotb1052c22011-01-12 19:44:23 -080022import com.android.tradefed.result.ITestInvocationListener;
jdesprez2ffb1bd2018-03-19 10:37:23 -070023import com.android.tradefed.result.InputStreamSource;
24import com.android.tradefed.result.LogDataType;
25import com.android.tradefed.result.LogFile;
Julien Despreze6b756b2018-04-16 10:56:38 -070026import com.android.tradefed.result.LogSaverResultForwarder;
Brett Chabotb1052c22011-01-12 19:44:23 -080027import com.android.tradefed.result.ResultForwarder;
jdesprezc9f81492017-05-30 15:59:05 -070028import com.android.tradefed.util.TimeUtil;
Brett Chabotb1052c22011-01-12 19:44:23 -080029
jdesprez5c272662017-11-28 02:42:27 -080030import java.util.ArrayList;
Brett Chabotb1052c22011-01-12 19:44:23 -080031import java.util.List;
jdesprez5c272662017-11-28 02:42:27 -080032import java.util.Map.Entry;
Brett Chabotb1052c22011-01-12 19:44:23 -080033
34/**
35 * A {@link ResultForwarder} that combines the results of a sharded test invocations. It only
36 * reports completion of the invocation to the listeners once all sharded invocations are complete.
jdesprezbf750e12017-04-03 12:18:47 -070037 *
38 * <p>This class is not thread safe. It is expected that clients will lock on this class when
39 * sending test results, to prevent invocation callbacks from being called out of order.
Brett Chabotb1052c22011-01-12 19:44:23 -080040 */
jdesprez2ffb1bd2018-03-19 10:37:23 -070041public class ShardMasterResultForwarder extends ResultForwarder implements ILogSaverListener {
Brett Chabotb1052c22011-01-12 19:44:23 -080042
jdesprezc9f81492017-05-30 15:59:05 -070043 private final int mInitCount;
Brett Chabotb1052c22011-01-12 19:44:23 -080044 private int mShardsRemaining;
jdesprez671d0d22018-01-29 16:04:15 -080045 private long mTotalElapsed = 0L;
Brett Chabotb1052c22011-01-12 19:44:23 -080046 private boolean mStartReported = false;
47
jdesprez671d0d22018-01-29 16:04:15 -080048 private long mFirstShardEndTime = 0L;
jdesprez57f2df42017-10-31 11:58:23 -070049 private IInvocationContext mOriginalContext;
jdesprez5c272662017-11-28 02:42:27 -080050 private List<IInvocationContext> mShardContextList;
jdesprez57f2df42017-10-31 11:58:23 -070051 private int shardIndex = 0;
jdesprezc9f81492017-05-30 15:59:05 -070052
Brett Chabotb1052c22011-01-12 19:44:23 -080053 /**
54 * Create a {@link ShardMasterResultForwarder}.
55 *
56 * @param listeners the list of {@link ITestInvocationListener} to forward results to when all
jdesprez2ffb1bd2018-03-19 10:37:23 -070057 * shards are completed
Brett Chabotb1052c22011-01-12 19:44:23 -080058 * @param expectedShards the number of shards
59 */
jdesprez2ffb1bd2018-03-19 10:37:23 -070060 public ShardMasterResultForwarder(List<ITestInvocationListener> listeners, int expectedShards) {
61 super(listeners);
Brett Chabotb1052c22011-01-12 19:44:23 -080062 mShardsRemaining = expectedShards;
jdesprezc9f81492017-05-30 15:59:05 -070063 mInitCount = expectedShards;
jdesprez5c272662017-11-28 02:42:27 -080064 mShardContextList = new ArrayList<>();
Brett Chabotb1052c22011-01-12 19:44:23 -080065 }
66
Julien Desprezd0c379a2016-11-04 11:00:54 +000067 /**
68 * {@inheritDoc}
Julien Desprezd0c379a2016-11-04 11:00:54 +000069 */
Julien Desprez53353e62016-08-12 15:24:33 +010070 @Override
71 public void invocationStarted(IInvocationContext context) {
72 if (!mStartReported) {
jdesprez57f2df42017-10-31 11:58:23 -070073 mOriginalContext = context;
Julien Desprez53353e62016-08-12 15:24:33 +010074 super.invocationStarted(context);
75 mStartReported = true;
jdesprez57f2df42017-10-31 11:58:23 -070076 } else {
77 // Track serials used in each shard.
78 mOriginalContext.addSerialsFromShard(shardIndex, context.getSerials());
jdesprez5c272662017-11-28 02:42:27 -080079 mShardContextList.add(context);
jdesprez57f2df42017-10-31 11:58:23 -070080 shardIndex++;
Julien Desprez53353e62016-08-12 15:24:33 +010081 }
82 }
83
Brett Chabotb1052c22011-01-12 19:44:23 -080084 /**
85 * {@inheritDoc}
86 */
87 @Override
88 public void invocationFailed(Throwable cause) {
89 // one of the shards failed. Fail the whole invocation
90 // TODO: does any extra logging need to be done ?
91 super.invocationFailed(cause);
92 }
93
Julien Desprez7fc54252020-04-30 15:08:45 -070094 /** {@inheritDoc} */
95 @Override
96 public void invocationFailed(FailureDescription failure) {
97 // one of the shards failed. Fail the whole invocation
98 super.invocationFailed(failure);
99 }
100
Julien Desprezd0c379a2016-11-04 11:00:54 +0000101 /**
102 * {@inheritDoc}
103 */
Brett Chabotb1052c22011-01-12 19:44:23 -0800104 @Override
105 public void invocationEnded(long elapsedTime) {
106 mTotalElapsed += elapsedTime;
jdesprezc9f81492017-05-30 15:59:05 -0700107 if (mInitCount == mShardsRemaining) {
108 mFirstShardEndTime = System.currentTimeMillis();
109 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800110 mShardsRemaining--;
111 if (mShardsRemaining <= 0) {
jdesprezc9f81492017-05-30 15:59:05 -0700112 // TODO: consider logging all shard final times.
113 CLog.i(
114 "There was %s between the first and last shard ended.",
115 TimeUtil.formatElapsedTime(System.currentTimeMillis() - mFirstShardEndTime));
jdesprez5c272662017-11-28 02:42:27 -0800116 copyShardBuildInfoToMain(mOriginalContext, mShardContextList);
Brett Chabotb1052c22011-01-12 19:44:23 -0800117 super.invocationEnded(mTotalElapsed);
118 }
119 }
jdesprez5c272662017-11-28 02:42:27 -0800120
jdesprez2ffb1bd2018-03-19 10:37:23 -0700121 /** {@inheritDoc} */
122 @Override
123 public void testLogSaved(
124 String dataName, LogDataType dataType, InputStreamSource dataStream, LogFile logFile) {
125 for (ITestInvocationListener listener : getListeners()) {
126 try {
127 // Forward the testLogSaved event to ILogSaverListener
128 if (listener instanceof ILogSaverListener) {
129 ((ILogSaverListener) listener)
130 .testLogSaved(dataName, dataType, dataStream, logFile);
131 }
132 } catch (Exception e) {
133 CLog.e("Exception while invoking %s#testLogSaved", listener.getClass().getName());
134 CLog.e(e);
135 }
136 }
137 }
138
Julien Despreze6b756b2018-04-16 10:56:38 -0700139 /** Only forward the testLog instead of saving the log first. */
140 public void testLogForward(
141 String dataName, LogDataType dataType, InputStreamSource dataStream) {
142 for (ITestInvocationListener listener : getListeners()) {
143 if (listener instanceof LogSaverResultForwarder) {
144 // If the listener is a log saver, we should simply forward the testLog not save
145 // again.
146 ((LogSaverResultForwarder) listener).testLogForward(dataName, dataType, dataStream);
147 } else {
148 try {
149 listener.testLog(dataName, dataType, dataStream);
150 } catch (RuntimeException e) {
151 CLog.e(
152 "RuntimeException while invoking %s#testLog",
153 listener.getClass().getName());
154 CLog.e(e);
155 }
156 }
157 }
158 }
159
160 /** {@inheritDoc} */
161 @Override
162 public void logAssociation(String dataName, LogFile logFile) {
163 for (ITestInvocationListener listener : getListeners()) {
164 try {
165 // Forward the logAssociation call
166 if (listener instanceof ILogSaverListener) {
167 ((ILogSaverListener) listener).logAssociation(dataName, logFile);
168 }
169 } catch (RuntimeException e) {
170 CLog.e("Failed to provide the log association");
171 CLog.e(e);
172 }
173 }
174 }
175
jdesprez5c272662017-11-28 02:42:27 -0800176 /**
177 * Copy the build info from the shard builds to the main build in the original invocation
178 * context.
179 *
180 * @param main the original {@link IInvocationContext} from the main invocation.
181 * @param shardContexts the list of {@link IInvocationContext}s, one for each shard invocation.
182 */
183 private void copyShardBuildInfoToMain(
184 IInvocationContext main, List<IInvocationContext> shardContexts) {
185 for (IInvocationContext shard : shardContexts) {
186 for (String deviceName : shard.getDeviceConfigNames()) {
187 IBuildInfo shardBuild = shard.getBuildInfo(deviceName);
188 IBuildInfo mainBuild = main.getBuildInfo(deviceName);
189 if (mainBuild != null) {
190 for (Entry<String, String> entry : shardBuild.getBuildAttributes().entrySet()) {
191 mainBuild.addBuildAttribute(entry.getKey(), entry.getValue());
192 }
193 } else {
194 // Should not happen
195 CLog.e(
196 "Found a device '%s' in shard configuration but not in parent configuration.",
197 deviceName);
198 }
199 }
200 }
201 }
Brett Chabotb1052c22011-01-12 19:44:23 -0800202}