blob: 30bd21700889539e436988cb831e7ac355de28de [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Ulf Adamse11063a2016-12-15 17:33:32 +000015package com.google.devtools.build.lib.exec;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010016
George Gensurec53bdaf2020-06-04 01:32:39 -070017import com.google.common.annotations.VisibleForTesting;
Laszlo Csomor6b08ff12019-05-02 01:35:41 -070018import com.google.common.base.Preconditions;
plfa445cda2020-10-29 10:39:30 -070019import com.google.common.base.Verify;
Klaus Aehlig89512a72017-02-10 14:36:43 +000020import com.google.common.collect.ImmutableList;
Ulf Adamsb3c833f2017-02-01 14:47:02 +000021import com.google.common.collect.ImmutableMap;
ulfjacke4cca142020-01-08 04:44:40 -080022import com.google.common.collect.ImmutableSet;
Chi Wangf5cf8b02021-11-07 20:05:55 -080023import com.google.common.collect.Maps;
felly71480542019-02-06 07:55:51 -080024import com.google.common.io.ByteStreams;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.actions.ActionExecutionContext;
plfa445cda2020-10-29 10:39:30 -070026import com.google.devtools.build.lib.actions.ActionInput;
ulfjack0858ae12018-07-27 02:37:53 -070027import com.google.devtools.build.lib.actions.ActionInputHelper;
plfa445cda2020-10-29 10:39:30 -070028import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
buchgr580038e2019-09-02 02:16:19 -070029import com.google.devtools.build.lib.actions.ArtifactPathResolver;
ulfjack415165a2019-09-12 07:56:40 -070030import com.google.devtools.build.lib.actions.EnvironmentalExecException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.actions.ExecException;
buchgr9f7edd72017-07-14 12:58:50 +020032import com.google.devtools.build.lib.actions.ExecutionRequirements;
Ulf Adamsd345cf92017-03-07 10:04:53 +000033import com.google.devtools.build.lib.actions.SimpleSpawn;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import com.google.devtools.build.lib.actions.Spawn;
Emil Kattainenacf736d2022-07-14 05:10:52 -070035import com.google.devtools.build.lib.actions.SpawnMetrics;
ruperts4050a892017-10-07 00:46:20 +020036import com.google.devtools.build.lib.actions.SpawnResult;
buchgr580038e2019-09-02 02:16:19 -070037import com.google.devtools.build.lib.actions.TestExecException;
ulfjack0858ae12018-07-27 02:37:53 -070038import com.google.devtools.build.lib.analysis.actions.SpawnAction;
jcaterf057da42020-04-14 05:08:40 -070039import com.google.devtools.build.lib.analysis.test.TestAttempt;
ulfjackab21d182017-08-10 15:36:14 +020040import com.google.devtools.build.lib.analysis.test.TestResult;
41import com.google.devtools.build.lib.analysis.test.TestRunnerAction;
ulfjack4a5e1b72019-03-18 06:56:57 -070042import com.google.devtools.build.lib.analysis.test.TestRunnerAction.ResolvedPaths;
jcaterf057da42020-04-14 05:08:40 -070043import com.google.devtools.build.lib.analysis.test.TestStrategy;
Benjamin Peterson1bbeadc2018-04-26 05:27:10 -070044import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
ulfjack797325f2019-10-17 06:33:40 -070045import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TestResult.ExecutionInfo;
Googler2f08a182017-09-21 18:51:20 +020046import com.google.devtools.build.lib.buildeventstream.TestFileNameConstants;
ulfjacke4cca142020-01-08 04:44:40 -080047import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
48import com.google.devtools.build.lib.collect.nestedset.Order;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049import com.google.devtools.build.lib.events.Reporter;
mschaller45576672020-06-10 19:15:07 -070050import com.google.devtools.build.lib.server.FailureDetails.Execution.Code;
mschallerd322b722020-12-28 15:16:32 -080051import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
mschaller07933882020-06-24 14:38:23 -070052import com.google.devtools.build.lib.server.FailureDetails.TestAction;
Klaus Aehlig89512a72017-02-10 14:36:43 +000053import com.google.devtools.build.lib.util.Pair;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.util.io.FileOutErr;
felly71480542019-02-06 07:55:51 -080055import com.google.devtools.build.lib.vfs.FileStatus;
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +000056import com.google.devtools.build.lib.vfs.FileSystemUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010057import com.google.devtools.build.lib.vfs.Path;
Ulf Adamsb3c833f2017-02-01 14:47:02 +000058import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010059import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus;
60import com.google.devtools.build.lib.view.test.TestStatus.TestCase;
61import com.google.devtools.build.lib.view.test.TestStatus.TestResultData;
Googler9961a752023-02-23 05:21:53 -080062import com.google.protobuf.Duration;
Emil Kattainenacf736d2022-07-14 05:10:52 -070063import com.google.protobuf.util.Durations;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064import java.io.Closeable;
65import java.io.IOException;
felly71480542019-02-06 07:55:51 -080066import java.io.InputStream;
67import java.io.OutputStream;
ruperts7967f332017-11-21 16:37:13 -080068import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import java.util.Map;
ulfjack404a6702018-02-21 01:37:55 -080070import java.util.TreeMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071
Ulf Adamse11063a2016-12-15 17:33:32 +000072/** Runs TestRunnerAction actions. */
lberki037c9dc2018-02-09 06:06:49 -080073// TODO(bazel-team): add tests for this strategy.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074public class StandaloneTestStrategy extends TestStrategy {
Ulf Adamsb3c833f2017-02-01 14:47:02 +000075 private static final ImmutableMap<String, String> ENV_VARS =
76 ImmutableMap.<String, String>builder()
77 .put("TZ", "UTC")
Ulf Adamsb3c833f2017-02-01 14:47:02 +000078 .put("TEST_SRCDIR", TestPolicy.RUNFILES_DIR)
79 // TODO(lberki): Remove JAVA_RUNFILES and PYTHON_RUNFILES.
80 .put("JAVA_RUNFILES", TestPolicy.RUNFILES_DIR)
81 .put("PYTHON_RUNFILES", TestPolicy.RUNFILES_DIR)
82 .put("RUNFILES_DIR", TestPolicy.RUNFILES_DIR)
83 .put("TEST_TMPDIR", TestPolicy.TEST_TMP_DIR)
Yun Peng62c31052017-06-07 15:06:51 -040084 .put("RUN_UNDER_RUNFILES", "1")
Ulf Adamsb3c833f2017-02-01 14:47:02 +000085 .build();
86
87 public static final TestPolicy DEFAULT_LOCAL_POLICY = new TestPolicy(ENV_VARS);
88
Ulf Adamsb21cfa12016-12-16 14:31:42 +000089 protected final Path tmpDirRoot;
Kristina Chodorow379f7c02015-04-21 18:50:50 +000090
Han-Wen Nienhuysa51b3f72015-09-28 10:15:15 +000091 public StandaloneTestStrategy(
ulfjackacd291a2017-06-16 15:25:42 +020092 ExecutionOptions executionOptions, BinTools binTools, Path tmpDirRoot) {
93 super(executionOptions, binTools);
Ulf Adamsb21cfa12016-12-16 14:31:42 +000094 this.tmpDirRoot = tmpDirRoot;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095 }
96
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097 @Override
ulfjack645fe0f2019-02-28 05:58:36 -080098 public TestRunnerSpawn createTestRunnerSpawn(
michajlob091d302020-11-17 14:29:42 -080099 TestRunnerAction action, ActionExecutionContext actionExecutionContext)
100 throws ExecException, InterruptedException {
buchgr580038e2019-09-02 02:16:19 -0700101 if (action.getExecutionSettings().getInputManifest() == null) {
Bo Zhang49eb31b2022-10-06 00:24:23 -0700102 throw createTestExecException(
103 TestAction.Code.LOCAL_TEST_PREREQ_UNMET,
104 "cannot run local tests with --nobuild_runfile_manifests");
buchgr580038e2019-09-02 02:16:19 -0700105 }
plfa445cda2020-10-29 10:39:30 -0700106 Map<String, String> testEnvironment =
107 createEnvironment(
108 actionExecutionContext, action, tmpDirRoot, executionOptions.splitXmlGeneration);
Han-Wen Nienhuysb6f557b2015-06-23 12:39:03 +0000109
ulfjack404a6702018-02-21 01:37:55 -0800110 Map<String, String> executionInfo =
111 new TreeMap<>(action.getTestProperties().getExecutionInfo());
buchgr9f7edd72017-07-14 12:58:50 +0200112 if (!action.shouldCacheResult()) {
113 executionInfo.put(ExecutionRequirements.NO_CACHE, "");
114 }
ulfjack404a6702018-02-21 01:37:55 -0800115 executionInfo.put(ExecutionRequirements.TIMEOUT, "" + getTimeout(action).getSeconds());
Ulf Adams19948b02017-01-10 16:27:59 +0000116
janakr91d93f62022-01-31 15:31:57 -0800117 SimpleSpawn.LocalResourcesSupplier localResourcesSupplier =
118 () ->
119 action
120 .getTestProperties()
121 .getLocalResourceUsage(
122 action.getOwner().getLabel(), executionOptions.usingLocalTestJobs());
philwoda21ba72017-05-02 20:25:12 +0200123
Ulf Adams19948b02017-01-10 16:27:59 +0000124 Spawn spawn =
Ulf Adamsd345cf92017-03-07 10:04:53 +0000125 new SimpleSpawn(
126 action,
lberki037c9dc2018-02-09 06:06:49 -0800127 getArgs(action),
plfa445cda2020-10-29 10:39:30 -0700128 ImmutableMap.copyOf(testEnvironment),
ulfjack404a6702018-02-21 01:37:55 -0800129 ImmutableMap.copyOf(executionInfo),
buchgra30df552019-08-29 04:54:41 -0700130 action.getRunfilesSupplier(),
kush2ce45a22018-05-02 14:15:37 -0700131 ImmutableMap.of(),
ulfjacke4cca142020-01-08 04:44:40 -0800132 /*inputs=*/ action.getInputs(),
cushon1f335042022-03-21 09:18:42 -0700133 NestedSetBuilder.emptySet(Order.STABLE_ORDER),
Chi Wang79b5b5d2022-08-22 02:34:05 -0700134 ImmutableSet.copyOf(action.getSpawnOutputs()),
janakr82b38962022-02-01 07:12:10 -0800135 /*mandatoryOutputs=*/ ImmutableSet.of(),
janakr91d93f62022-01-31 15:31:57 -0800136 localResourcesSupplier);
plfa445cda2020-10-29 10:39:30 -0700137 Path execRoot = actionExecutionContext.getExecRoot();
138 ArtifactPathResolver pathResolver = actionExecutionContext.getPathResolver();
139 Path runfilesDir = pathResolver.convertPath(action.getExecutionSettings().getRunfilesDir());
140 Path tmpDir = pathResolver.convertPath(tmpDirRoot.getChild(TestStrategy.getTmpDirName(action)));
141 Path workingDirectory = runfilesDir.getRelative(action.getRunfilesPrefix());
ulfjackb2a92ad2019-02-26 06:48:27 -0800142 return new StandaloneTestRunnerSpawn(
ulfjackfb04c5b2019-04-26 00:15:05 -0700143 action, actionExecutionContext, spawn, tmpDir, workingDirectory, execRoot);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144 }
145
ajurkowskibd74a872020-01-23 13:51:33 -0800146 private static ImmutableList<Pair<String, Path>> renameOutputs(
ulfjack77c9f5e2017-06-19 14:17:52 +0200147 ActionExecutionContext actionExecutionContext,
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +0000148 TestRunnerAction action,
ulfjackeb8cfc12019-04-29 04:43:14 -0700149 ImmutableList<Pair<String, Path>> testOutputs,
150 int attemptId)
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +0000151 throws IOException {
152 // Rename outputs
153 String namePrefix =
154 FileSystemUtils.removeExtension(action.getTestLog().getExecPath().getBaseName());
shahan18726b72018-03-15 14:18:46 -0700155 Path testRoot = actionExecutionContext.getInputPath(action.getTestLog()).getParentDirectory();
Googler2f08a182017-09-21 18:51:20 +0200156 Path attemptsDir = testRoot.getChild(namePrefix + "_attempts");
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +0000157 attemptsDir.createDirectory();
ulfjackeb8cfc12019-04-29 04:43:14 -0700158 String attemptPrefix = "attempt_" + attemptId;
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +0000159 Path testLog = attemptsDir.getChild(attemptPrefix + ".log");
Googler2f08a182017-09-21 18:51:20 +0200160
161 // Get the normal test output paths, and then update them to use "attempt_N" names, and
162 // attemptDir, before adding them to the outputs.
ulfjackb2a92ad2019-02-26 06:48:27 -0800163 ImmutableList.Builder<Pair<String, Path>> testOutputsBuilder = new ImmutableList.Builder<>();
Googler2f08a182017-09-21 18:51:20 +0200164 for (Pair<String, Path> testOutput : testOutputs) {
165 // e.g. /testRoot/test.dir/file, an example we follow throughout this loop's comments.
166 Path testOutputPath = testOutput.getSecond();
ulfjack0c61edb2018-03-27 11:32:07 -0700167 Path destinationPath;
168 if (testOutput.getFirst().equals(TestFileNameConstants.TEST_LOG)) {
169 // The rename rules for the test log are different than for all the other files.
170 destinationPath = testLog;
171 } else {
172 // e.g. test.dir/file
173 PathFragment relativeToTestDirectory = testOutputPath.relativeTo(testRoot);
Googler2f08a182017-09-21 18:51:20 +0200174
ulfjack0c61edb2018-03-27 11:32:07 -0700175 // e.g. attempt_1.dir/file
176 String destinationPathFragmentStr =
177 relativeToTestDirectory.getSafePathString().replaceFirst("test", attemptPrefix);
178 PathFragment destinationPathFragment = PathFragment.create(destinationPathFragmentStr);
olaolac35fe502017-10-17 04:09:07 +0200179
ulfjack0c61edb2018-03-27 11:32:07 -0700180 // e.g. /attemptsDir/attempt_1.dir/file
181 destinationPath = attemptsDir.getRelative(destinationPathFragment);
182 destinationPath.getParentDirectory().createDirectory();
183 }
olaolac35fe502017-10-17 04:09:07 +0200184
ulfjack0c61edb2018-03-27 11:32:07 -0700185 // Move to the destination.
Googler2f08a182017-09-21 18:51:20 +0200186 testOutputPath.renameTo(destinationPath);
187
188 testOutputsBuilder.add(Pair.of(testOutput.getFirst(), destinationPath));
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +0000189 }
ulfjackeb8cfc12019-04-29 04:43:14 -0700190 return testOutputsBuilder.build();
191 }
192
193 private StandaloneFailedAttemptResult processFailedTestAttempt(
194 int attemptId,
195 ActionExecutionContext actionExecutionContext,
196 TestRunnerAction action,
197 StandaloneTestResult result)
198 throws IOException {
199 return processTestAttempt(
200 attemptId, /*isLastAttempt=*/ false, actionExecutionContext, action, result);
201 }
202
arostovtseva6bfab02022-02-14 22:15:58 -0800203 private void finalizeTest(
ulfjackeb8cfc12019-04-29 04:43:14 -0700204 TestRunnerAction action,
205 ActionExecutionContext actionExecutionContext,
206 StandaloneTestResult standaloneTestResult,
207 List<FailedAttemptResult> failedAttempts)
208 throws IOException {
209 processTestAttempt(
210 failedAttempts.size() + 1,
211 /*isLastAttempt=*/ true,
212 actionExecutionContext,
213 action,
214 standaloneTestResult);
215
216 TestResultData.Builder dataBuilder = standaloneTestResult.testResultDataBuilder();
217 for (FailedAttemptResult failedAttempt : failedAttempts) {
218 TestResultData failedAttemptData =
219 ((StandaloneFailedAttemptResult) failedAttempt).testResultData;
220 dataBuilder.addAllFailedLogs(failedAttemptData.getFailedLogsList());
221 dataBuilder.addTestTimes(failedAttemptData.getTestTimes(0));
222 dataBuilder.addAllTestProcessTimes(failedAttemptData.getTestProcessTimesList());
223 }
224 if (dataBuilder.getStatus() == BlazeTestStatus.PASSED && !failedAttempts.isEmpty()) {
225 dataBuilder.setStatus(BlazeTestStatus.FLAKY);
226 }
227 TestResultData data = dataBuilder.build();
mschallerd322b722020-12-28 15:16:32 -0800228 TestResult result =
229 new TestResult(action, data, false, standaloneTestResult.primarySystemFailure());
arostovtseva6bfab02022-02-14 22:15:58 -0800230 postTestResult(actionExecutionContext, result);
ulfjackeb8cfc12019-04-29 04:43:14 -0700231 }
232
233 private StandaloneFailedAttemptResult processTestAttempt(
234 int attemptId,
235 boolean isLastAttempt,
236 ActionExecutionContext actionExecutionContext,
237 TestRunnerAction action,
238 StandaloneTestResult result)
239 throws IOException {
240 ImmutableList<Pair<String, Path>> testOutputs =
241 action.getTestOutputsMapping(
242 actionExecutionContext.getPathResolver(), actionExecutionContext.getExecRoot());
243 if (!isLastAttempt) {
244 testOutputs = renameOutputs(actionExecutionContext, action, testOutputs, attemptId);
245 }
Googler2f08a182017-09-21 18:51:20 +0200246
ulfjackeb8cfc12019-04-29 04:43:14 -0700247 // Recover the test log path, which may have been renamed, and add it to the data builder.
248 Path renamedTestLog = null;
249 for (Pair<String, Path> pair : testOutputs) {
250 if (TestFileNameConstants.TEST_LOG.equals(pair.getFirst())) {
George Gensurec53bdaf2020-06-04 01:32:39 -0700251 Preconditions.checkState(renamedTestLog == null, "multiple test_log matches");
ulfjackeb8cfc12019-04-29 04:43:14 -0700252 renamedTestLog = pair.getSecond();
253 }
254 }
ulfjack797325f2019-10-17 06:33:40 -0700255
256 TestResultData.Builder dataBuilder = result.testResultDataBuilder();
George Gensurec53bdaf2020-06-04 01:32:39 -0700257 // If the test log path does not exist, mark the test as incomplete
258 if (renamedTestLog == null) {
259 dataBuilder.setStatus(BlazeTestStatus.INCOMPLETE);
260 }
261
ulfjackeb8cfc12019-04-29 04:43:14 -0700262 if (dataBuilder.getStatus() == BlazeTestStatus.PASSED) {
263 dataBuilder.setPassedLog(renamedTestLog.toString());
ulfjack797325f2019-10-17 06:33:40 -0700264 } else if (dataBuilder.getStatus() == BlazeTestStatus.INCOMPLETE) {
265 // Incomplete (cancelled) test runs don't have a log.
266 Preconditions.checkState(renamedTestLog == null);
ulfjackeb8cfc12019-04-29 04:43:14 -0700267 } else {
268 dataBuilder.addFailedLogs(renamedTestLog.toString());
269 }
ulfjackb2a92ad2019-02-26 06:48:27 -0800270
271 // Add the test log to the output
272 TestResultData data = dataBuilder.build();
ulfjack77c9f5e2017-06-19 14:17:52 +0200273 actionExecutionContext
Benjamin Peterson1bbeadc2018-04-26 05:27:10 -0700274 .getEventHandler()
Klaus Aehlig0aaa6dd2017-03-08 17:33:08 +0000275 .post(
ulfjackb22c7ee2018-05-14 05:32:03 -0700276 TestAttempt.forExecutedTestResult(
ulfjackeb8cfc12019-04-29 04:43:14 -0700277 action, data, attemptId, testOutputs, result.executionInfo(), isLastAttempt));
278 processTestOutput(actionExecutionContext, data, action.getTestName(), renamedTestLog);
ulfjackb70f0c12019-02-27 07:23:38 -0800279 return new StandaloneFailedAttemptResult(data);
Damien Martin-Guillerezcc49b662016-12-29 15:53:08 +0000280 }
281
plfa445cda2020-10-29 10:39:30 -0700282 private static Map<String, String> setupEnvironment(
283 TestRunnerAction action,
284 Map<String, String> clientEnv,
285 Path execRoot,
286 Path runfilesDir,
ulfjackd1c53292017-06-08 18:09:01 +0200287 Path tmpDir) {
Ulf Adamsb3c833f2017-02-01 14:47:02 +0000288 PathFragment relativeTmpDir;
Kristina Chodorow88de40e2016-08-18 14:24:58 +0000289 if (tmpDir.startsWith(execRoot)) {
Ulf Adamsb3c833f2017-02-01 14:47:02 +0000290 relativeTmpDir = tmpDir.relativeTo(execRoot);
Kristina Chodorow88de40e2016-08-18 14:24:58 +0000291 } else {
Ulf Adamsb3c833f2017-02-01 14:47:02 +0000292 relativeTmpDir = tmpDir.asFragment();
Kristina Chodorow88de40e2016-08-18 14:24:58 +0000293 }
Ulf Adamsb3c833f2017-02-01 14:47:02 +0000294 return DEFAULT_LOCAL_POLICY.computeTestEnvironment(
Chi Wang97fb2cf2021-06-21 23:23:19 -0700295 action, clientEnv, getTimeout(action), runfilesDir.relativeTo(execRoot), relativeTmpDir);
296 }
297
Googlerc0bbb0f2022-12-01 00:30:29 -0800298 private TestAttemptResult beginTestAttempt(
ulfjack4a5e1b72019-03-18 06:56:57 -0700299 TestRunnerAction testAction,
300 Spawn spawn,
301 ActionExecutionContext actionExecutionContext,
302 Path execRoot)
Googlerf61e2e92022-11-30 09:19:04 -0800303 throws ExecException, IOException, InterruptedException {
ulfjack4a5e1b72019-03-18 06:56:57 -0700304 ResolvedPaths resolvedPaths = testAction.resolve(execRoot);
305 Path out = actionExecutionContext.getInputPath(testAction.getTestLog());
306 Path err = resolvedPaths.getTestStderr();
307 FileOutErr testOutErr = new FileOutErr(out, err);
308 Closeable streamed = null;
jcaterd990df52020-04-07 04:38:15 -0700309 if (executionOptions.testOutput.equals(ExecutionOptions.TestOutputFormat.STREAMED)) {
ulfjack4a5e1b72019-03-18 06:56:57 -0700310 streamed =
michajlo9885c2f2020-03-09 12:33:06 -0700311 createStreamedTestOutput(
ulfjack4a5e1b72019-03-18 06:56:57 -0700312 Reporter.outErrForReporter(actionExecutionContext.getEventHandler()), out);
313 }
Chi Wang97fb2cf2021-06-21 23:23:19 -0700314
ulfjack4a5e1b72019-03-18 06:56:57 -0700315 long startTimeMillis = actionExecutionContext.getClock().currentTimeMillis();
jcater56b9b182020-04-17 13:55:34 -0700316 SpawnStrategyResolver resolver = actionExecutionContext.getContext(SpawnStrategyResolver.class);
Googlera48b0d22022-11-25 00:12:14 -0800317
Googlerf61e2e92022-11-30 09:19:04 -0800318 return runTestAttempt(
ulfjack415165a2019-09-12 07:56:40 -0700319 testAction,
320 actionExecutionContext,
321 spawn,
Googlerf61e2e92022-11-30 09:19:04 -0800322 resolver,
ulfjack415165a2019-09-12 07:56:40 -0700323 resolvedPaths,
324 testOutErr,
325 streamed,
Googlerf61e2e92022-11-30 09:19:04 -0800326 startTimeMillis);
ulfjack4a5e1b72019-03-18 06:56:57 -0700327 }
328
plfa445cda2020-10-29 10:39:30 -0700329 private static void appendCoverageLog(FileOutErr coverageOutErr, FileOutErr outErr)
330 throws IOException {
331 writeOutFile(coverageOutErr.getErrorPath(), outErr.getOutputPath());
332 writeOutFile(coverageOutErr.getOutputPath(), outErr.getOutputPath());
333 }
334
335 private static void writeOutFile(Path inFilePath, Path outFilePath) throws IOException {
336 FileStatus stat = inFilePath.statNullable();
felly71480542019-02-06 07:55:51 -0800337 if (stat != null) {
338 try {
339 if (stat.getSize() > 0) {
plfa445cda2020-10-29 10:39:30 -0700340 if (outFilePath.exists()) {
341 outFilePath.setWritable(true);
felly71480542019-02-06 07:55:51 -0800342 }
plfa445cda2020-10-29 10:39:30 -0700343 try (OutputStream out = outFilePath.getOutputStream(true);
344 InputStream in = inFilePath.getInputStream()) {
felly71480542019-02-06 07:55:51 -0800345 ByteStreams.copy(in, out);
346 }
347 }
348 } finally {
plfa445cda2020-10-29 10:39:30 -0700349 inFilePath.delete();
felly71480542019-02-06 07:55:51 -0800350 }
351 }
352 }
353
ulfjackeb8cfc12019-04-29 04:43:14 -0700354 private static BuildEventStreamProtos.TestResult.ExecutionInfo extractExecutionInfo(
ulfjack4a5e1b72019-03-18 06:56:57 -0700355 SpawnResult spawnResult, TestResultData.Builder result) {
356 BuildEventStreamProtos.TestResult.ExecutionInfo.Builder executionInfo =
357 BuildEventStreamProtos.TestResult.ExecutionInfo.newBuilder();
Emil Kattainenacf736d2022-07-14 05:10:52 -0700358
Benjamin Peterson1bbeadc2018-04-26 05:27:10 -0700359 if (spawnResult.isCacheHit()) {
360 result.setRemotelyCached(true);
361 executionInfo.setCachedRemotely(true);
362 }
Emil Kattainenacf736d2022-07-14 05:10:52 -0700363
Benjamin Peterson1bbeadc2018-04-26 05:27:10 -0700364 String strategy = spawnResult.getRunnerName();
365 if (strategy != null) {
366 executionInfo.setStrategy(strategy);
367 result.setIsRemoteStrategy(strategy.equals("remote"));
368 }
Emil Kattainenacf736d2022-07-14 05:10:52 -0700369
Benjamin Peterson1bbeadc2018-04-26 05:27:10 -0700370 if (spawnResult.getExecutorHostName() != null) {
371 executionInfo.setHostname(spawnResult.getExecutorHostName());
372 }
Emil Kattainenacf736d2022-07-14 05:10:52 -0700373
374 SpawnMetrics sm = spawnResult.getMetrics();
375 executionInfo.setTimingBreakdown(
376 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
377 .setName("totalTime")
Googler9961a752023-02-23 05:21:53 -0800378 .setTime(toProtoDuration(sm.totalTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700379 .addChild(
380 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
381 .setName("parseTime")
Googler9961a752023-02-23 05:21:53 -0800382 .setTime(toProtoDuration(sm.parseTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700383 .build())
384 .addChild(
385 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
386 .setName("fetchTime")
Googler9961a752023-02-23 05:21:53 -0800387 .setTime(toProtoDuration(sm.fetchTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700388 .build())
389 .addChild(
390 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
391 .setName("queueTime")
Googler9961a752023-02-23 05:21:53 -0800392 .setTime(toProtoDuration(sm.queueTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700393 .build())
394 .addChild(
395 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
396 .setName("uploadTime")
Googler9961a752023-02-23 05:21:53 -0800397 .setTime(toProtoDuration(sm.uploadTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700398 .build())
399 .addChild(
400 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
401 .setName("setupTime")
Googler9961a752023-02-23 05:21:53 -0800402 .setTime(toProtoDuration(sm.setupTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700403 .build())
404 .addChild(
405 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
406 .setName("executionWallTime")
Googler9961a752023-02-23 05:21:53 -0800407 .setTime(toProtoDuration(sm.executionWallTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700408 .build())
409 .addChild(
410 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
411 .setName("processOutputsTime")
Googler9961a752023-02-23 05:21:53 -0800412 .setTime(toProtoDuration(sm.processOutputsTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700413 .build())
414 .addChild(
415 BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder()
416 .setName("networkTime")
Googler9961a752023-02-23 05:21:53 -0800417 .setTime(toProtoDuration(sm.networkTimeInMs()))
Emil Kattainenacf736d2022-07-14 05:10:52 -0700418 .build())
419 .build());
420
ulfjackeb8cfc12019-04-29 04:43:14 -0700421 return executionInfo.build();
Benjamin Peterson1bbeadc2018-04-26 05:27:10 -0700422 }
423
Googler9961a752023-02-23 05:21:53 -0800424 private static Duration toProtoDuration(int timeInMs) {
425 return Durations.fromMillis(timeInMs);
Emil Kattainenacf736d2022-07-14 05:10:52 -0700426 }
427
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100428 /**
ulfjack0858ae12018-07-27 02:37:53 -0700429 * A spawn to generate a test.xml file from the test log. This is only used if the test does not
430 * generate a test.xml file itself.
431 */
Yun Peng35518982020-12-15 06:04:28 -0800432 private static Spawn createXmlGeneratingSpawn(
433 TestRunnerAction action, ImmutableMap<String, String> testEnv, SpawnResult result) {
laszlocsomorba87ba92020-01-15 05:33:02 -0800434 ImmutableList<String> args =
435 ImmutableList.of(
436 action.getTestXmlGeneratorScript().getExecPath().getCallablePathString(),
437 action.getTestLog().getExecPathString(),
438 action.getXmlOutputPath().getPathString(),
Googler9961a752023-02-23 05:21:53 -0800439 Integer.toString(result.getWallTimeInMs() / 1000),
laszlocsomorba87ba92020-01-15 05:33:02 -0800440 Integer.toString(result.exitCode()));
Yun Peng35518982020-12-15 06:04:28 -0800441 ImmutableMap.Builder<String, String> envBuilder = ImmutableMap.builder();
442 // "PATH" and "TEST_BINARY" are also required, they should always be set in testEnv.
443 Preconditions.checkArgument(testEnv.containsKey("PATH"));
444 Preconditions.checkArgument(testEnv.containsKey("TEST_BINARY"));
445 envBuilder.putAll(testEnv).put("TEST_NAME", action.getTestName());
446 // testEnv only contains TEST_SHARD_INDEX and TEST_TOTAL_SHARDS if the test action is sharded,
447 // we need to set the default value when the action isn't sharded.
448 if (!action.isSharded()) {
449 envBuilder.put("TEST_SHARD_INDEX", "0");
450 envBuilder.put("TEST_TOTAL_SHARDS", "0");
451 }
Chi Wangf5cf8b02021-11-07 20:05:55 -0800452 Map<String, String> executionInfo =
453 Maps.newHashMapWithExpectedSize(action.getExecutionInfo().size() + 1);
454 executionInfo.putAll(action.getExecutionInfo());
455 if (result.exitCode() != 0) {
456 // If the test is failed, the spawn shouldn't use remote cache since the test.xml file is
457 // renamed immediately after the spawn execution. If there is another test attempt, the async
458 // upload will fail because it cannot read the file at original position.
459 executionInfo.put(ExecutionRequirements.NO_REMOTE_CACHE, "");
460 }
ulfjack0858ae12018-07-27 02:37:53 -0700461 return new SimpleSpawn(
462 action,
laszlocsomorba87ba92020-01-15 05:33:02 -0800463 args,
Googler2fc3d3f2022-02-01 06:29:56 -0800464 envBuilder.buildOrThrow(),
Jakob Buchgraber803801d2019-03-21 09:22:58 -0700465 // Pass the execution info of the action which is identical to the supported tags set on the
466 // test target. In particular, this does not set the test timeout on the spawn.
Chi Wangf5cf8b02021-11-07 20:05:55 -0800467 ImmutableMap.copyOf(executionInfo),
ulfjack0858ae12018-07-27 02:37:53 -0700468 null,
469 ImmutableMap.of(),
ulfjacke4cca142020-01-08 04:44:40 -0800470 /*inputs=*/ NestedSetBuilder.create(
471 Order.STABLE_ORDER, action.getTestXmlGeneratorScript(), action.getTestLog()),
472 /*tools=*/ NestedSetBuilder.emptySet(Order.STABLE_ORDER),
Chi Wang79b5b5d2022-08-22 02:34:05 -0700473 /*outputs=*/ ImmutableSet.of(ActionInputHelper.fromPath(action.getXmlOutputPath())),
janakr82b38962022-02-01 07:12:10 -0800474 /*mandatoryOutputs=*/ null,
ulfjack0858ae12018-07-27 02:37:53 -0700475 SpawnAction.DEFAULT_RESOURCE_SET);
476 }
477
plfa445cda2020-10-29 10:39:30 -0700478 private static Spawn createCoveragePostProcessingSpawn(
479 ActionExecutionContext actionExecutionContext,
480 TestRunnerAction action,
481 List<ActionInput> expandedCoverageDir,
482 Path tmpDirRoot,
483 boolean splitXmlGeneration) {
484 ImmutableList<String> args =
485 ImmutableList.of(action.getCollectCoverageScript().getExecPathString());
486
487 Map<String, String> testEnvironment =
488 createEnvironment(actionExecutionContext, action, tmpDirRoot, splitXmlGeneration);
489
490 testEnvironment.put("TEST_SHARD_INDEX", Integer.toString(action.getShardNum()));
491 testEnvironment.put(
492 "TEST_TOTAL_SHARDS", Integer.toString(action.getExecutionSettings().getTotalShards()));
493 testEnvironment.put("TEST_NAME", action.getTestName());
494 testEnvironment.put("IS_COVERAGE_SPAWN", "1");
495 return new SimpleSpawn(
496 action,
497 args,
498 ImmutableMap.copyOf(testEnvironment),
jcater285b17f2021-12-16 07:16:28 -0800499 action.getExecutionInfo(),
plfa445cda2020-10-29 10:39:30 -0700500 action.getLcovMergerRunfilesSupplier(),
janakr82b38962022-02-01 07:12:10 -0800501 /*filesetMappings=*/ ImmutableMap.of(),
502 /*inputs=*/ NestedSetBuilder.<ActionInput>compileOrder()
Benjamin Peterson7e790a02021-04-15 01:37:10 -0700503 .addTransitive(action.getInputs())
plfa445cda2020-10-29 10:39:30 -0700504 .addAll(expandedCoverageDir)
505 .add(action.getCollectCoverageScript())
plfa445cda2020-10-29 10:39:30 -0700506 .add(action.getCoverageManifest())
507 .addTransitive(action.getLcovMergerFilesToRun().build())
508 .build(),
janakr82b38962022-02-01 07:12:10 -0800509 /*tools=*/ NestedSetBuilder.emptySet(Order.STABLE_ORDER),
510 /*outputs=*/ ImmutableSet.of(
ajurkowski0e71e902021-06-11 10:58:16 -0700511 ActionInputHelper.fromPath(action.getCoverageData().getExecPath())),
janakr82b38962022-02-01 07:12:10 -0800512 /*mandatoryOutputs=*/ null,
plfa445cda2020-10-29 10:39:30 -0700513 SpawnAction.DEFAULT_RESOURCE_SET);
514 }
515
516 private static Map<String, String> createEnvironment(
517 ActionExecutionContext actionExecutionContext,
518 TestRunnerAction action,
519 Path tmpDirRoot,
520 boolean splitXmlGeneration) {
521 Path execRoot = actionExecutionContext.getExecRoot();
522 ArtifactPathResolver pathResolver = actionExecutionContext.getPathResolver();
523 Path runfilesDir = pathResolver.convertPath(action.getExecutionSettings().getRunfilesDir());
524 Path tmpDir = pathResolver.convertPath(tmpDirRoot.getChild(TestStrategy.getTmpDirName(action)));
525 Map<String, String> testEnvironment =
526 setupEnvironment(
527 action, actionExecutionContext.getClientEnv(), execRoot, runfilesDir, tmpDir);
528 if (splitXmlGeneration) {
529 testEnvironment.put("EXPERIMENTAL_SPLIT_XML_GENERATION", "1");
530 }
531 return testEnvironment;
532 }
533
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100534 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100535 public TestResult newCachedTestResult(
536 Path execRoot, TestRunnerAction action, TestResultData data) {
mschallerd322b722020-12-28 15:16:32 -0800537 return new TestResult(action, data, /*cached*/ true, execRoot, /*systemFailure=*/ null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100538 }
ulfjackb2a92ad2019-02-26 06:48:27 -0800539
George Gensurec53bdaf2020-06-04 01:32:39 -0700540 @VisibleForTesting
541 static final class StandaloneFailedAttemptResult implements FailedAttemptResult {
ulfjackb70f0c12019-02-27 07:23:38 -0800542 private final TestResultData testResultData;
543
544 StandaloneFailedAttemptResult(TestResultData testResultData) {
545 this.testResultData = testResultData;
546 }
George Gensurec53bdaf2020-06-04 01:32:39 -0700547
548 TestResultData testResultData() {
549 return testResultData;
550 }
ulfjackb70f0c12019-02-27 07:23:38 -0800551 }
552
arostovtseva6bfab02022-02-14 22:15:58 -0800553 private final class StandaloneTestRunnerSpawn implements TestRunnerSpawn {
ulfjackb2a92ad2019-02-26 06:48:27 -0800554 private final TestRunnerAction testAction;
555 private final ActionExecutionContext actionExecutionContext;
556 private final Spawn spawn;
557 private final Path tmpDir;
ulfjackb2a92ad2019-02-26 06:48:27 -0800558 private final Path workingDirectory;
559 private final Path execRoot;
560
561 StandaloneTestRunnerSpawn(
562 TestRunnerAction testAction,
563 ActionExecutionContext actionExecutionContext,
564 Spawn spawn,
565 Path tmpDir,
ulfjackb2a92ad2019-02-26 06:48:27 -0800566 Path workingDirectory,
567 Path execRoot) {
568 this.testAction = testAction;
569 this.actionExecutionContext = actionExecutionContext;
570 this.spawn = spawn;
571 this.tmpDir = tmpDir;
ulfjackb2a92ad2019-02-26 06:48:27 -0800572 this.workingDirectory = workingDirectory;
573 this.execRoot = execRoot;
574 }
575
ulfjack11883442019-03-15 12:10:47 -0700576 @Override
577 public ActionExecutionContext getActionExecutionContext() {
578 return actionExecutionContext;
579 }
580
ulfjack4a5e1b72019-03-18 06:56:57 -0700581 @Override
Googlerc0bbb0f2022-12-01 00:30:29 -0800582 public TestAttemptResult execute() throws InterruptedException, IOException, ExecException {
ulfjack71f9cea2019-10-17 00:32:35 -0700583 prepareFileSystem(testAction, execRoot, tmpDir, workingDirectory);
ulfjack4a5e1b72019-03-18 06:56:57 -0700584 return beginTestAttempt(testAction, spawn, actionExecutionContext, execRoot);
585 }
586
587 @Override
ulfjackb70f0c12019-02-27 07:23:38 -0800588 public int getMaxAttempts(TestAttemptResult firstTestAttemptResult) {
589 return getTestAttempts(testAction);
590 }
591
592 @Override
593 public FailedAttemptResult finalizeFailedTestAttempt(
594 TestAttemptResult testAttemptResult, int attempt) throws IOException {
595 return processFailedTestAttempt(
596 attempt, actionExecutionContext, testAction, (StandaloneTestResult) testAttemptResult);
597 }
598
599 @Override
600 public void finalizeTest(
601 TestAttemptResult finalResult, List<FailedAttemptResult> failedAttempts)
ulfjackd23e5772019-03-15 07:39:42 -0700602 throws IOException {
ulfjackb70f0c12019-02-27 07:23:38 -0800603 StandaloneTestStrategy.this.finalizeTest(
604 testAction, actionExecutionContext, (StandaloneTestResult) finalResult, failedAttempts);
605 }
ulfjack797325f2019-10-17 06:33:40 -0700606
607 @Override
608 public void finalizeCancelledTest(List<FailedAttemptResult> failedAttempts) throws IOException {
609 TestResultData.Builder builder =
mschallerd322b722020-12-28 15:16:32 -0800610 TestResultData.newBuilder()
611 .setCachable(false)
612 .setTestPassed(false)
613 .setStatus(BlazeTestStatus.INCOMPLETE);
ulfjack797325f2019-10-17 06:33:40 -0700614 StandaloneTestResult standaloneTestResult =
615 StandaloneTestResult.builder()
616 .setSpawnResults(ImmutableList.of())
617 .setTestResultDataBuilder(builder)
618 .setExecutionInfo(ExecutionInfo.getDefaultInstance())
619 .build();
620 finalizeTest(standaloneTestResult, failedAttempts);
621 }
ulfjackb2a92ad2019-02-26 06:48:27 -0800622 }
ulfjack4a5e1b72019-03-18 06:56:57 -0700623
Bo Zhang49eb31b2022-10-06 00:24:23 -0700624 private static TestExecException createTestExecException(
625 TestAction.Code errorCode, String errorMessage) {
626 return new TestExecException(
627 errorMessage,
628 FailureDetail.newBuilder()
629 .setTestAction(TestAction.newBuilder().setCode(errorCode))
630 .setMessage(errorMessage)
631 .build());
632 }
633
Googlerc0bbb0f2022-12-01 00:30:29 -0800634 private TestAttemptResult runTestAttempt(
Googlerf61e2e92022-11-30 09:19:04 -0800635 TestRunnerAction testAction,
636 ActionExecutionContext actionExecutionContext,
637 Spawn spawn,
638 SpawnStrategyResolver resolver,
639 ResolvedPaths resolvedPaths,
640 FileOutErr fileOutErr,
641 Closeable streamed,
642 long startTimeMillis)
643 throws InterruptedException, ExecException, IOException {
ulfjack4a5e1b72019-03-18 06:56:57 -0700644
Googlerf61e2e92022-11-30 09:19:04 -0800645 ImmutableList<SpawnResult> spawnResults;
646
647 // We have two protos to represent test attempts:
648 // 1. com.google.devtools.build.lib.view.test.TestStatus.TestResultData represents both
649 // failed attempts and finished tests. Bazel stores this to disk to persist cached test
650 // result information across server restarts.
651 // 2. com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TestResult
652 // represents only individual attempts (failed or not). Bazel reports this as an event to
653 // the Build Event Protocol, but never saves it to disk.
654 //
655 // The TestResult proto is always constructed from a TestResultData instance, either one
656 // that is created right here, or one that is read back from disk.
657 TestResultData.Builder testResultDataBuilder;
658 try {
659 spawnResults = resolver.exec(spawn, actionExecutionContext.withFileOutErr(fileOutErr));
660 testResultDataBuilder = TestResultData.newBuilder();
661 testResultDataBuilder.setCachable(true).setTestPassed(true).setStatus(BlazeTestStatus.PASSED);
662 } catch (SpawnExecException e) {
663 if (e.isCatastrophic()) {
664 closeSuppressed(e, streamed);
665 closeSuppressed(e, fileOutErr);
666 throw e;
667 }
668 if (!e.getSpawnResult().setupSuccess()) {
669 closeSuppressed(e, streamed);
670 closeSuppressed(e, fileOutErr);
671 // Rethrow as the test could not be run and thus there's no point in retrying.
672 throw e;
673 }
674 spawnResults = ImmutableList.of(e.getSpawnResult());
675 testResultDataBuilder = TestResultData.newBuilder();
676 testResultDataBuilder
677 .setCachable(e.getSpawnResult().status().isConsideredUserError())
678 .setTestPassed(false)
679 .setStatus(e.hasTimedOut() ? BlazeTestStatus.TIMEOUT : BlazeTestStatus.FAILED);
680 } catch (InterruptedException e) {
681 closeSuppressed(e, streamed);
682 closeSuppressed(e, fileOutErr);
683 throw e;
ulfjack4a5e1b72019-03-18 06:56:57 -0700684 }
Googlerf61e2e92022-11-30 09:19:04 -0800685 long endTimeMillis = actionExecutionContext.getClock().currentTimeMillis();
ulfjack4a5e1b72019-03-18 06:56:57 -0700686
Fabian Meumertzheimf7d795a2023-05-08 03:43:30 -0700687 if (testAction.isSharded()) {
688 if (testAction.checkShardingSupport()
689 && !actionExecutionContext
690 .getPathResolver()
691 .convertPath(resolvedPaths.getTestShard())
692 .exists()) {
693 TestExecException e =
694 createTestExecException(
695 TestAction.Code.LOCAL_TEST_PREREQ_UNMET,
696 "Sharding requested, but the test runner did not advertise support for it by "
697 + "touching TEST_SHARD_STATUS_FILE. Either remove the 'shard_count' attribute, "
698 + "use a test runner that supports sharding or temporarily disable this check "
699 + "via --noincompatible_check_sharding_support.");
700 closeSuppressed(e, streamed);
701 closeSuppressed(e, fileOutErr);
702 throw e;
703 }
704 }
705
Googlerf61e2e92022-11-30 09:19:04 -0800706 // SpawnActionContext guarantees the first entry to correspond to the spawn passed in (there
707 // may be additional entries due to tree artifact handling).
708 SpawnResult primaryResult = spawnResults.get(0);
ulfjack4a5e1b72019-03-18 06:56:57 -0700709
Googlerf61e2e92022-11-30 09:19:04 -0800710 // The SpawnResult of a remotely cached or remotely executed action may not have walltime
711 // set. We fall back to the time measured here for backwards compatibility.
712 long durationMillis = endTimeMillis - startTimeMillis;
713 durationMillis =
Googler9961a752023-02-23 05:21:53 -0800714 (primaryResult.getWallTimeInMs() != 0 ? primaryResult.getWallTimeInMs() : durationMillis);
plfa445cda2020-10-29 10:39:30 -0700715
Googlerf61e2e92022-11-30 09:19:04 -0800716 testResultDataBuilder
717 .setStartTimeMillisEpoch(startTimeMillis)
718 .addTestTimes(durationMillis)
719 .addTestProcessTimes(durationMillis)
720 .setRunDurationMillis(durationMillis)
721 .setHasCoverage(testAction.isCoverageMode());
722
723 if (testAction.isCoverageMode() && testAction.getSplitCoveragePostProcessing()) {
724 if (testAction.getCoverageDirectoryTreeArtifact() == null) {
725 // Otherwise we'll get a NPE https://github.com/bazelbuild/bazel/issues/13185
726 TestExecException e =
727 createTestExecException(
728 TestAction.Code.LOCAL_TEST_PREREQ_UNMET,
729 "coverageDirectoryTreeArtifact is null:"
730 + " --experimental_split_coverage_postprocessing depends on"
731 + " --experimental_fetch_all_coverage_outputs being enabled");
732 closeSuppressed(e, streamed);
733 closeSuppressed(e, fileOutErr);
734 throw e;
735 }
Googlerd8e13c82023-04-11 00:07:32 -0700736 var unused =
737 actionExecutionContext
Googler8ec4c502023-04-12 10:44:40 -0700738 .getOutputMetadataStore()
Googlerd8e13c82023-04-11 00:07:32 -0700739 .getOutputMetadata(testAction.getCoverageDirectoryTreeArtifact());
740
Googlerf61e2e92022-11-30 09:19:04 -0800741 ImmutableSet<? extends ActionInput> expandedCoverageDir =
742 actionExecutionContext
Googler8ec4c502023-04-12 10:44:40 -0700743 .getOutputMetadataStore()
Googlerf61e2e92022-11-30 09:19:04 -0800744 .getTreeArtifactChildren(
745 (SpecialArtifact) testAction.getCoverageDirectoryTreeArtifact());
746 Spawn coveragePostProcessingSpawn =
747 createCoveragePostProcessingSpawn(
748 actionExecutionContext,
749 testAction,
750 ImmutableList.copyOf(expandedCoverageDir),
751 tmpDirRoot,
752 executionOptions.splitXmlGeneration);
753 SpawnStrategyResolver spawnStrategyResolver =
754 actionExecutionContext.getContext(SpawnStrategyResolver.class);
755
756 Path testRoot =
757 actionExecutionContext.getInputPath(testAction.getTestLog()).getParentDirectory();
758
759 Path out = testRoot.getChild("coverage.log");
760 Path err = testRoot.getChild("coverage.err");
761 FileOutErr coverageOutErr = new FileOutErr(out, err);
Googlerd8e13c82023-04-11 00:07:32 -0700762 ActionExecutionContext coverageActionExecutionContext =
763 actionExecutionContext
764 .withFileOutErr(coverageOutErr)
765 .withOutputsAsInputs(expandedCoverageDir);
Googlerf61e2e92022-11-30 09:19:04 -0800766
767 writeOutFile(coverageOutErr.getErrorPath(), coverageOutErr.getOutputPath());
768 appendCoverageLog(coverageOutErr, fileOutErr);
769 try {
Googlerd8e13c82023-04-11 00:07:32 -0700770 spawnStrategyResolver.exec(coveragePostProcessingSpawn, coverageActionExecutionContext);
Googlerf61e2e92022-11-30 09:19:04 -0800771 } catch (SpawnExecException e) {
772 if (e.isCatastrophic()) {
plfa445cda2020-10-29 10:39:30 -0700773 closeSuppressed(e, streamed);
774 closeSuppressed(e, fileOutErr);
775 throw e;
776 }
Googlerf61e2e92022-11-30 09:19:04 -0800777 if (!e.getSpawnResult().setupSuccess()) {
778 closeSuppressed(e, streamed);
779 closeSuppressed(e, fileOutErr);
780 // Rethrow as the test could not be run and thus there's no point in retrying.
ulfjack415165a2019-09-12 07:56:40 -0700781 throw e;
ulfjack4a5e1b72019-03-18 06:56:57 -0700782 }
Googlerf61e2e92022-11-30 09:19:04 -0800783 testResultDataBuilder
784 .setCachable(e.getSpawnResult().status().isConsideredUserError())
785 .setTestPassed(false)
786 .setStatus(e.hasTimedOut() ? BlazeTestStatus.TIMEOUT : BlazeTestStatus.FAILED);
787 } catch (ExecException | InterruptedException e) {
788 closeSuppressed(e, streamed);
789 closeSuppressed(e, fileOutErr);
790 throw e;
ulfjack4a5e1b72019-03-18 06:56:57 -0700791 }
ulfjack4a5e1b72019-03-18 06:56:57 -0700792 }
Googlerf61e2e92022-11-30 09:19:04 -0800793
794 Verify.verify(
795 !(testAction.isCoverageMode() && testAction.getSplitCoveragePostProcessing())
Chi Wange8ff51f2023-01-18 06:00:19 -0800796 || actionExecutionContext
797 .getPathResolver()
798 .convertPath(testAction.getCoverageData().getPath())
799 .exists());
Googlerf61e2e92022-11-30 09:19:04 -0800800 Verify.verifyNotNull(spawnResults);
801 Verify.verifyNotNull(testResultDataBuilder);
802
803 try {
804 if (!fileOutErr.hasRecordedOutput()) {
805 // Make sure that the test.log exists.Spaw
806 FileSystemUtils.touchFile(fileOutErr.getOutputPath());
807 }
808 // Append any error output to the test.log. This is very rare.
809 writeOutFile(fileOutErr.getErrorPath(), fileOutErr.getOutputPath());
810 fileOutErr.close();
811 if (streamed != null) {
812 streamed.close();
813 }
814 } catch (IOException e) {
815 throw new EnvironmentalExecException(e, Code.TEST_OUT_ERR_IO_EXCEPTION);
816 }
817
818 Path xmlOutputPath = resolvedPaths.getXmlOutputPath();
819
820 // If the test did not create a test.xml, and --experimental_split_xml_generation is enabled,
821 // then we run a separate action to create a test.xml from test.log. We do this as a spawn
822 // rather than doing it locally in-process, as the test.log file may only exist remotely (when
823 // remote execution is enabled), and we do not want to have to download it.
824 if (executionOptions.splitXmlGeneration
825 && fileOutErr.getOutputPath().exists()
826 && !xmlOutputPath.exists()) {
827 Spawn xmlGeneratingSpawn =
828 createXmlGeneratingSpawn(testAction, spawn.getEnvironment(), spawnResults.get(0));
829 SpawnStrategyResolver spawnStrategyResolver =
830 actionExecutionContext.getContext(SpawnStrategyResolver.class);
831 // We treat all failures to generate the test.xml here as catastrophic, and won't rerun
832 // the test if this fails. We redirect the output to a temporary file.
833 FileOutErr xmlSpawnOutErr = actionExecutionContext.getFileOutErr().childOutErr();
Googlerd8e13c82023-04-11 00:07:32 -0700834
835 ActionExecutionContext xmlActionExecutionContext =
836 actionExecutionContext
837 .withFileOutErr(xmlSpawnOutErr)
838 .withOutputsAsInputs(ImmutableList.of(testAction.getTestLog()));
Googlerf61e2e92022-11-30 09:19:04 -0800839 try {
840
841 ImmutableList<SpawnResult> xmlSpawnResults =
Googlerd8e13c82023-04-11 00:07:32 -0700842 spawnStrategyResolver.exec(xmlGeneratingSpawn, xmlActionExecutionContext);
Googlerf61e2e92022-11-30 09:19:04 -0800843 spawnResults =
844 ImmutableList.<SpawnResult>builder()
845 .addAll(spawnResults)
846 .addAll(xmlSpawnResults)
847 .build();
848 } catch (InterruptedException | ExecException e) {
849 closeSuppressed(e, xmlSpawnOutErr);
850 throw e;
851 }
852 }
853
854 TestCase details = parseTestResult(xmlOutputPath);
855 if (details != null) {
856 testResultDataBuilder.setTestCase(details);
857 }
858
859 BuildEventStreamProtos.TestResult.ExecutionInfo executionInfo =
860 extractExecutionInfo(spawnResults.get(0), testResultDataBuilder);
Googlerc0bbb0f2022-12-01 00:30:29 -0800861 return StandaloneTestResult.builder()
862 .setSpawnResults(spawnResults)
863 // We return the TestResultData.Builder rather than the finished TestResultData
864 // instance, as we may have to rename the output files in case the test needs to be
865 // rerun (if it failed here _and_ is marked flaky _and_ the number of flaky attempts
866 // is larger than 1).
867 .setTestResultDataBuilder(testResultDataBuilder)
868 .setExecutionInfo(executionInfo)
869 .build();
ulfjack4a5e1b72019-03-18 06:56:57 -0700870 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100871}