Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 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 Adams | e11063a | 2016-12-15 17:33:32 +0000 | [diff] [blame] | 15 | package com.google.devtools.build.lib.exec; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 16 | |
George Gensure | c53bdaf | 2020-06-04 01:32:39 -0700 | [diff] [blame] | 17 | import com.google.common.annotations.VisibleForTesting; |
Laszlo Csomor | 6b08ff1 | 2019-05-02 01:35:41 -0700 | [diff] [blame] | 18 | import com.google.common.base.Preconditions; |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 19 | import com.google.common.base.Verify; |
Klaus Aehlig | 89512a7 | 2017-02-10 14:36:43 +0000 | [diff] [blame] | 20 | import com.google.common.collect.ImmutableList; |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 21 | import com.google.common.collect.ImmutableMap; |
ulfjack | e4cca14 | 2020-01-08 04:44:40 -0800 | [diff] [blame] | 22 | import com.google.common.collect.ImmutableSet; |
Chi Wang | f5cf8b0 | 2021-11-07 20:05:55 -0800 | [diff] [blame] | 23 | import com.google.common.collect.Maps; |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 24 | import com.google.common.io.ByteStreams; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 25 | import com.google.devtools.build.lib.actions.ActionExecutionContext; |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 26 | import com.google.devtools.build.lib.actions.ActionInput; |
ulfjack | 0858ae1 | 2018-07-27 02:37:53 -0700 | [diff] [blame] | 27 | import com.google.devtools.build.lib.actions.ActionInputHelper; |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 28 | import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; |
buchgr | 580038e | 2019-09-02 02:16:19 -0700 | [diff] [blame] | 29 | import com.google.devtools.build.lib.actions.ArtifactPathResolver; |
ulfjack | 415165a | 2019-09-12 07:56:40 -0700 | [diff] [blame] | 30 | import com.google.devtools.build.lib.actions.EnvironmentalExecException; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 31 | import com.google.devtools.build.lib.actions.ExecException; |
buchgr | 9f7edd7 | 2017-07-14 12:58:50 +0200 | [diff] [blame] | 32 | import com.google.devtools.build.lib.actions.ExecutionRequirements; |
Ulf Adams | d345cf9 | 2017-03-07 10:04:53 +0000 | [diff] [blame] | 33 | import com.google.devtools.build.lib.actions.SimpleSpawn; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 34 | import com.google.devtools.build.lib.actions.Spawn; |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 35 | import com.google.devtools.build.lib.actions.SpawnMetrics; |
ruperts | 4050a89 | 2017-10-07 00:46:20 +0200 | [diff] [blame] | 36 | import com.google.devtools.build.lib.actions.SpawnResult; |
buchgr | 580038e | 2019-09-02 02:16:19 -0700 | [diff] [blame] | 37 | import com.google.devtools.build.lib.actions.TestExecException; |
ulfjack | 0858ae1 | 2018-07-27 02:37:53 -0700 | [diff] [blame] | 38 | import com.google.devtools.build.lib.analysis.actions.SpawnAction; |
jcater | f057da4 | 2020-04-14 05:08:40 -0700 | [diff] [blame] | 39 | import com.google.devtools.build.lib.analysis.test.TestAttempt; |
ulfjack | ab21d18 | 2017-08-10 15:36:14 +0200 | [diff] [blame] | 40 | import com.google.devtools.build.lib.analysis.test.TestResult; |
| 41 | import com.google.devtools.build.lib.analysis.test.TestRunnerAction; |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 42 | import com.google.devtools.build.lib.analysis.test.TestRunnerAction.ResolvedPaths; |
jcater | f057da4 | 2020-04-14 05:08:40 -0700 | [diff] [blame] | 43 | import com.google.devtools.build.lib.analysis.test.TestStrategy; |
Benjamin Peterson | 1bbeadc | 2018-04-26 05:27:10 -0700 | [diff] [blame] | 44 | import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; |
ulfjack | 797325f | 2019-10-17 06:33:40 -0700 | [diff] [blame] | 45 | import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TestResult.ExecutionInfo; |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 46 | import com.google.devtools.build.lib.buildeventstream.TestFileNameConstants; |
ulfjack | e4cca14 | 2020-01-08 04:44:40 -0800 | [diff] [blame] | 47 | import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| 48 | import com.google.devtools.build.lib.collect.nestedset.Order; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 49 | import com.google.devtools.build.lib.events.Reporter; |
mschaller | 4557667 | 2020-06-10 19:15:07 -0700 | [diff] [blame] | 50 | import com.google.devtools.build.lib.server.FailureDetails.Execution.Code; |
mschaller | d322b72 | 2020-12-28 15:16:32 -0800 | [diff] [blame] | 51 | import com.google.devtools.build.lib.server.FailureDetails.FailureDetail; |
mschaller | 0793388 | 2020-06-24 14:38:23 -0700 | [diff] [blame] | 52 | import com.google.devtools.build.lib.server.FailureDetails.TestAction; |
Klaus Aehlig | 89512a7 | 2017-02-10 14:36:43 +0000 | [diff] [blame] | 53 | import com.google.devtools.build.lib.util.Pair; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 54 | import com.google.devtools.build.lib.util.io.FileOutErr; |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 55 | import com.google.devtools.build.lib.vfs.FileStatus; |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 56 | import com.google.devtools.build.lib.vfs.FileSystemUtils; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 57 | import com.google.devtools.build.lib.vfs.Path; |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 58 | import com.google.devtools.build.lib.vfs.PathFragment; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 59 | import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus; |
| 60 | import com.google.devtools.build.lib.view.test.TestStatus.TestCase; |
| 61 | import com.google.devtools.build.lib.view.test.TestStatus.TestResultData; |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 62 | import com.google.protobuf.Duration; |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 63 | import com.google.protobuf.util.Durations; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 64 | import java.io.Closeable; |
| 65 | import java.io.IOException; |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 66 | import java.io.InputStream; |
| 67 | import java.io.OutputStream; |
ruperts | 7967f33 | 2017-11-21 16:37:13 -0800 | [diff] [blame] | 68 | import java.util.List; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 69 | import java.util.Map; |
ulfjack | 404a670 | 2018-02-21 01:37:55 -0800 | [diff] [blame] | 70 | import java.util.TreeMap; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 71 | |
Ulf Adams | e11063a | 2016-12-15 17:33:32 +0000 | [diff] [blame] | 72 | /** Runs TestRunnerAction actions. */ |
lberki | 037c9dc | 2018-02-09 06:06:49 -0800 | [diff] [blame] | 73 | // TODO(bazel-team): add tests for this strategy. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 74 | public class StandaloneTestStrategy extends TestStrategy { |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 75 | private static final ImmutableMap<String, String> ENV_VARS = |
| 76 | ImmutableMap.<String, String>builder() |
| 77 | .put("TZ", "UTC") |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 78 | .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 Peng | 62c3105 | 2017-06-07 15:06:51 -0400 | [diff] [blame] | 84 | .put("RUN_UNDER_RUNFILES", "1") |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 85 | .build(); |
| 86 | |
| 87 | public static final TestPolicy DEFAULT_LOCAL_POLICY = new TestPolicy(ENV_VARS); |
| 88 | |
Ulf Adams | b21cfa1 | 2016-12-16 14:31:42 +0000 | [diff] [blame] | 89 | protected final Path tmpDirRoot; |
Kristina Chodorow | 379f7c0 | 2015-04-21 18:50:50 +0000 | [diff] [blame] | 90 | |
Han-Wen Nienhuys | a51b3f7 | 2015-09-28 10:15:15 +0000 | [diff] [blame] | 91 | public StandaloneTestStrategy( |
ulfjack | acd291a | 2017-06-16 15:25:42 +0200 | [diff] [blame] | 92 | ExecutionOptions executionOptions, BinTools binTools, Path tmpDirRoot) { |
| 93 | super(executionOptions, binTools); |
Ulf Adams | b21cfa1 | 2016-12-16 14:31:42 +0000 | [diff] [blame] | 94 | this.tmpDirRoot = tmpDirRoot; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 95 | } |
| 96 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 97 | @Override |
ulfjack | 645fe0f | 2019-02-28 05:58:36 -0800 | [diff] [blame] | 98 | public TestRunnerSpawn createTestRunnerSpawn( |
michajlo | b091d30 | 2020-11-17 14:29:42 -0800 | [diff] [blame] | 99 | TestRunnerAction action, ActionExecutionContext actionExecutionContext) |
| 100 | throws ExecException, InterruptedException { |
buchgr | 580038e | 2019-09-02 02:16:19 -0700 | [diff] [blame] | 101 | if (action.getExecutionSettings().getInputManifest() == null) { |
Bo Zhang | 49eb31b | 2022-10-06 00:24:23 -0700 | [diff] [blame] | 102 | throw createTestExecException( |
| 103 | TestAction.Code.LOCAL_TEST_PREREQ_UNMET, |
| 104 | "cannot run local tests with --nobuild_runfile_manifests"); |
buchgr | 580038e | 2019-09-02 02:16:19 -0700 | [diff] [blame] | 105 | } |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 106 | Map<String, String> testEnvironment = |
| 107 | createEnvironment( |
| 108 | actionExecutionContext, action, tmpDirRoot, executionOptions.splitXmlGeneration); |
Han-Wen Nienhuys | b6f557b | 2015-06-23 12:39:03 +0000 | [diff] [blame] | 109 | |
ulfjack | 404a670 | 2018-02-21 01:37:55 -0800 | [diff] [blame] | 110 | Map<String, String> executionInfo = |
| 111 | new TreeMap<>(action.getTestProperties().getExecutionInfo()); |
buchgr | 9f7edd7 | 2017-07-14 12:58:50 +0200 | [diff] [blame] | 112 | if (!action.shouldCacheResult()) { |
| 113 | executionInfo.put(ExecutionRequirements.NO_CACHE, ""); |
| 114 | } |
ulfjack | 404a670 | 2018-02-21 01:37:55 -0800 | [diff] [blame] | 115 | executionInfo.put(ExecutionRequirements.TIMEOUT, "" + getTimeout(action).getSeconds()); |
Ulf Adams | 19948b0 | 2017-01-10 16:27:59 +0000 | [diff] [blame] | 116 | |
janakr | 91d93f6 | 2022-01-31 15:31:57 -0800 | [diff] [blame] | 117 | SimpleSpawn.LocalResourcesSupplier localResourcesSupplier = |
| 118 | () -> |
| 119 | action |
| 120 | .getTestProperties() |
| 121 | .getLocalResourceUsage( |
| 122 | action.getOwner().getLabel(), executionOptions.usingLocalTestJobs()); |
philwo | da21ba7 | 2017-05-02 20:25:12 +0200 | [diff] [blame] | 123 | |
Ulf Adams | 19948b0 | 2017-01-10 16:27:59 +0000 | [diff] [blame] | 124 | Spawn spawn = |
Ulf Adams | d345cf9 | 2017-03-07 10:04:53 +0000 | [diff] [blame] | 125 | new SimpleSpawn( |
| 126 | action, |
lberki | 037c9dc | 2018-02-09 06:06:49 -0800 | [diff] [blame] | 127 | getArgs(action), |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 128 | ImmutableMap.copyOf(testEnvironment), |
ulfjack | 404a670 | 2018-02-21 01:37:55 -0800 | [diff] [blame] | 129 | ImmutableMap.copyOf(executionInfo), |
buchgr | a30df55 | 2019-08-29 04:54:41 -0700 | [diff] [blame] | 130 | action.getRunfilesSupplier(), |
kush | 2ce45a2 | 2018-05-02 14:15:37 -0700 | [diff] [blame] | 131 | ImmutableMap.of(), |
ulfjack | e4cca14 | 2020-01-08 04:44:40 -0800 | [diff] [blame] | 132 | /*inputs=*/ action.getInputs(), |
cushon | 1f33504 | 2022-03-21 09:18:42 -0700 | [diff] [blame] | 133 | NestedSetBuilder.emptySet(Order.STABLE_ORDER), |
Chi Wang | 79b5b5d | 2022-08-22 02:34:05 -0700 | [diff] [blame] | 134 | ImmutableSet.copyOf(action.getSpawnOutputs()), |
janakr | 82b3896 | 2022-02-01 07:12:10 -0800 | [diff] [blame] | 135 | /*mandatoryOutputs=*/ ImmutableSet.of(), |
janakr | 91d93f6 | 2022-01-31 15:31:57 -0800 | [diff] [blame] | 136 | localResourcesSupplier); |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 137 | 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()); |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 142 | return new StandaloneTestRunnerSpawn( |
ulfjack | fb04c5b | 2019-04-26 00:15:05 -0700 | [diff] [blame] | 143 | action, actionExecutionContext, spawn, tmpDir, workingDirectory, execRoot); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 144 | } |
| 145 | |
ajurkowski | bd74a87 | 2020-01-23 13:51:33 -0800 | [diff] [blame] | 146 | private static ImmutableList<Pair<String, Path>> renameOutputs( |
ulfjack | 77c9f5e | 2017-06-19 14:17:52 +0200 | [diff] [blame] | 147 | ActionExecutionContext actionExecutionContext, |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 148 | TestRunnerAction action, |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 149 | ImmutableList<Pair<String, Path>> testOutputs, |
| 150 | int attemptId) |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 151 | throws IOException { |
| 152 | // Rename outputs |
| 153 | String namePrefix = |
| 154 | FileSystemUtils.removeExtension(action.getTestLog().getExecPath().getBaseName()); |
shahan | 18726b7 | 2018-03-15 14:18:46 -0700 | [diff] [blame] | 155 | Path testRoot = actionExecutionContext.getInputPath(action.getTestLog()).getParentDirectory(); |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 156 | Path attemptsDir = testRoot.getChild(namePrefix + "_attempts"); |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 157 | attemptsDir.createDirectory(); |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 158 | String attemptPrefix = "attempt_" + attemptId; |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 159 | Path testLog = attemptsDir.getChild(attemptPrefix + ".log"); |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 160 | |
| 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. |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 163 | ImmutableList.Builder<Pair<String, Path>> testOutputsBuilder = new ImmutableList.Builder<>(); |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 164 | 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(); |
ulfjack | 0c61edb | 2018-03-27 11:32:07 -0700 | [diff] [blame] | 167 | 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); |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 174 | |
ulfjack | 0c61edb | 2018-03-27 11:32:07 -0700 | [diff] [blame] | 175 | // e.g. attempt_1.dir/file |
| 176 | String destinationPathFragmentStr = |
| 177 | relativeToTestDirectory.getSafePathString().replaceFirst("test", attemptPrefix); |
| 178 | PathFragment destinationPathFragment = PathFragment.create(destinationPathFragmentStr); |
olaola | c35fe50 | 2017-10-17 04:09:07 +0200 | [diff] [blame] | 179 | |
ulfjack | 0c61edb | 2018-03-27 11:32:07 -0700 | [diff] [blame] | 180 | // e.g. /attemptsDir/attempt_1.dir/file |
| 181 | destinationPath = attemptsDir.getRelative(destinationPathFragment); |
| 182 | destinationPath.getParentDirectory().createDirectory(); |
| 183 | } |
olaola | c35fe50 | 2017-10-17 04:09:07 +0200 | [diff] [blame] | 184 | |
ulfjack | 0c61edb | 2018-03-27 11:32:07 -0700 | [diff] [blame] | 185 | // Move to the destination. |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 186 | testOutputPath.renameTo(destinationPath); |
| 187 | |
| 188 | testOutputsBuilder.add(Pair.of(testOutput.getFirst(), destinationPath)); |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 189 | } |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 190 | 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 | |
arostovtsev | a6bfab0 | 2022-02-14 22:15:58 -0800 | [diff] [blame] | 203 | private void finalizeTest( |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 204 | 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(); |
mschaller | d322b72 | 2020-12-28 15:16:32 -0800 | [diff] [blame] | 228 | TestResult result = |
| 229 | new TestResult(action, data, false, standaloneTestResult.primarySystemFailure()); |
arostovtsev | a6bfab0 | 2022-02-14 22:15:58 -0800 | [diff] [blame] | 230 | postTestResult(actionExecutionContext, result); |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 231 | } |
| 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 | } |
Googler | 2f08a18 | 2017-09-21 18:51:20 +0200 | [diff] [blame] | 246 | |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 247 | // 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 Gensure | c53bdaf | 2020-06-04 01:32:39 -0700 | [diff] [blame] | 251 | Preconditions.checkState(renamedTestLog == null, "multiple test_log matches"); |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 252 | renamedTestLog = pair.getSecond(); |
| 253 | } |
| 254 | } |
ulfjack | 797325f | 2019-10-17 06:33:40 -0700 | [diff] [blame] | 255 | |
| 256 | TestResultData.Builder dataBuilder = result.testResultDataBuilder(); |
George Gensure | c53bdaf | 2020-06-04 01:32:39 -0700 | [diff] [blame] | 257 | // If the test log path does not exist, mark the test as incomplete |
| 258 | if (renamedTestLog == null) { |
| 259 | dataBuilder.setStatus(BlazeTestStatus.INCOMPLETE); |
| 260 | } |
| 261 | |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 262 | if (dataBuilder.getStatus() == BlazeTestStatus.PASSED) { |
| 263 | dataBuilder.setPassedLog(renamedTestLog.toString()); |
ulfjack | 797325f | 2019-10-17 06:33:40 -0700 | [diff] [blame] | 264 | } else if (dataBuilder.getStatus() == BlazeTestStatus.INCOMPLETE) { |
| 265 | // Incomplete (cancelled) test runs don't have a log. |
| 266 | Preconditions.checkState(renamedTestLog == null); |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 267 | } else { |
| 268 | dataBuilder.addFailedLogs(renamedTestLog.toString()); |
| 269 | } |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 270 | |
| 271 | // Add the test log to the output |
| 272 | TestResultData data = dataBuilder.build(); |
ulfjack | 77c9f5e | 2017-06-19 14:17:52 +0200 | [diff] [blame] | 273 | actionExecutionContext |
Benjamin Peterson | 1bbeadc | 2018-04-26 05:27:10 -0700 | [diff] [blame] | 274 | .getEventHandler() |
Klaus Aehlig | 0aaa6dd | 2017-03-08 17:33:08 +0000 | [diff] [blame] | 275 | .post( |
ulfjack | b22c7ee | 2018-05-14 05:32:03 -0700 | [diff] [blame] | 276 | TestAttempt.forExecutedTestResult( |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 277 | action, data, attemptId, testOutputs, result.executionInfo(), isLastAttempt)); |
| 278 | processTestOutput(actionExecutionContext, data, action.getTestName(), renamedTestLog); |
ulfjack | b70f0c1 | 2019-02-27 07:23:38 -0800 | [diff] [blame] | 279 | return new StandaloneFailedAttemptResult(data); |
Damien Martin-Guillerez | cc49b66 | 2016-12-29 15:53:08 +0000 | [diff] [blame] | 280 | } |
| 281 | |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 282 | private static Map<String, String> setupEnvironment( |
| 283 | TestRunnerAction action, |
| 284 | Map<String, String> clientEnv, |
| 285 | Path execRoot, |
| 286 | Path runfilesDir, |
ulfjack | d1c5329 | 2017-06-08 18:09:01 +0200 | [diff] [blame] | 287 | Path tmpDir) { |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 288 | PathFragment relativeTmpDir; |
Kristina Chodorow | 88de40e | 2016-08-18 14:24:58 +0000 | [diff] [blame] | 289 | if (tmpDir.startsWith(execRoot)) { |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 290 | relativeTmpDir = tmpDir.relativeTo(execRoot); |
Kristina Chodorow | 88de40e | 2016-08-18 14:24:58 +0000 | [diff] [blame] | 291 | } else { |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 292 | relativeTmpDir = tmpDir.asFragment(); |
Kristina Chodorow | 88de40e | 2016-08-18 14:24:58 +0000 | [diff] [blame] | 293 | } |
Ulf Adams | b3c833f | 2017-02-01 14:47:02 +0000 | [diff] [blame] | 294 | return DEFAULT_LOCAL_POLICY.computeTestEnvironment( |
Chi Wang | 97fb2cf | 2021-06-21 23:23:19 -0700 | [diff] [blame] | 295 | action, clientEnv, getTimeout(action), runfilesDir.relativeTo(execRoot), relativeTmpDir); |
| 296 | } |
| 297 | |
Googler | c0bbb0f | 2022-12-01 00:30:29 -0800 | [diff] [blame] | 298 | private TestAttemptResult beginTestAttempt( |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 299 | TestRunnerAction testAction, |
| 300 | Spawn spawn, |
| 301 | ActionExecutionContext actionExecutionContext, |
| 302 | Path execRoot) |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 303 | throws ExecException, IOException, InterruptedException { |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 304 | 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; |
jcater | d990df5 | 2020-04-07 04:38:15 -0700 | [diff] [blame] | 309 | if (executionOptions.testOutput.equals(ExecutionOptions.TestOutputFormat.STREAMED)) { |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 310 | streamed = |
michajlo | 9885c2f | 2020-03-09 12:33:06 -0700 | [diff] [blame] | 311 | createStreamedTestOutput( |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 312 | Reporter.outErrForReporter(actionExecutionContext.getEventHandler()), out); |
| 313 | } |
Chi Wang | 97fb2cf | 2021-06-21 23:23:19 -0700 | [diff] [blame] | 314 | |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 315 | long startTimeMillis = actionExecutionContext.getClock().currentTimeMillis(); |
jcater | 56b9b18 | 2020-04-17 13:55:34 -0700 | [diff] [blame] | 316 | SpawnStrategyResolver resolver = actionExecutionContext.getContext(SpawnStrategyResolver.class); |
Googler | a48b0d2 | 2022-11-25 00:12:14 -0800 | [diff] [blame] | 317 | |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 318 | return runTestAttempt( |
ulfjack | 415165a | 2019-09-12 07:56:40 -0700 | [diff] [blame] | 319 | testAction, |
| 320 | actionExecutionContext, |
| 321 | spawn, |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 322 | resolver, |
ulfjack | 415165a | 2019-09-12 07:56:40 -0700 | [diff] [blame] | 323 | resolvedPaths, |
| 324 | testOutErr, |
| 325 | streamed, |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 326 | startTimeMillis); |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 327 | } |
| 328 | |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 329 | 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(); |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 337 | if (stat != null) { |
| 338 | try { |
| 339 | if (stat.getSize() > 0) { |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 340 | if (outFilePath.exists()) { |
| 341 | outFilePath.setWritable(true); |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 342 | } |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 343 | try (OutputStream out = outFilePath.getOutputStream(true); |
| 344 | InputStream in = inFilePath.getInputStream()) { |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 345 | ByteStreams.copy(in, out); |
| 346 | } |
| 347 | } |
| 348 | } finally { |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 349 | inFilePath.delete(); |
felly | 7148054 | 2019-02-06 07:55:51 -0800 | [diff] [blame] | 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 354 | private static BuildEventStreamProtos.TestResult.ExecutionInfo extractExecutionInfo( |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 355 | SpawnResult spawnResult, TestResultData.Builder result) { |
| 356 | BuildEventStreamProtos.TestResult.ExecutionInfo.Builder executionInfo = |
| 357 | BuildEventStreamProtos.TestResult.ExecutionInfo.newBuilder(); |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 358 | |
Benjamin Peterson | 1bbeadc | 2018-04-26 05:27:10 -0700 | [diff] [blame] | 359 | if (spawnResult.isCacheHit()) { |
| 360 | result.setRemotelyCached(true); |
| 361 | executionInfo.setCachedRemotely(true); |
| 362 | } |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 363 | |
Benjamin Peterson | 1bbeadc | 2018-04-26 05:27:10 -0700 | [diff] [blame] | 364 | String strategy = spawnResult.getRunnerName(); |
| 365 | if (strategy != null) { |
| 366 | executionInfo.setStrategy(strategy); |
| 367 | result.setIsRemoteStrategy(strategy.equals("remote")); |
| 368 | } |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 369 | |
Benjamin Peterson | 1bbeadc | 2018-04-26 05:27:10 -0700 | [diff] [blame] | 370 | if (spawnResult.getExecutorHostName() != null) { |
| 371 | executionInfo.setHostname(spawnResult.getExecutorHostName()); |
| 372 | } |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 373 | |
| 374 | SpawnMetrics sm = spawnResult.getMetrics(); |
| 375 | executionInfo.setTimingBreakdown( |
| 376 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 377 | .setName("totalTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 378 | .setTime(toProtoDuration(sm.totalTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 379 | .addChild( |
| 380 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 381 | .setName("parseTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 382 | .setTime(toProtoDuration(sm.parseTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 383 | .build()) |
| 384 | .addChild( |
| 385 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 386 | .setName("fetchTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 387 | .setTime(toProtoDuration(sm.fetchTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 388 | .build()) |
| 389 | .addChild( |
| 390 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 391 | .setName("queueTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 392 | .setTime(toProtoDuration(sm.queueTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 393 | .build()) |
| 394 | .addChild( |
| 395 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 396 | .setName("uploadTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 397 | .setTime(toProtoDuration(sm.uploadTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 398 | .build()) |
| 399 | .addChild( |
| 400 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 401 | .setName("setupTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 402 | .setTime(toProtoDuration(sm.setupTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 403 | .build()) |
| 404 | .addChild( |
| 405 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 406 | .setName("executionWallTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 407 | .setTime(toProtoDuration(sm.executionWallTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 408 | .build()) |
| 409 | .addChild( |
| 410 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 411 | .setName("processOutputsTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 412 | .setTime(toProtoDuration(sm.processOutputsTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 413 | .build()) |
| 414 | .addChild( |
| 415 | BuildEventStreamProtos.TestResult.ExecutionInfo.TimingBreakdown.newBuilder() |
| 416 | .setName("networkTime") |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 417 | .setTime(toProtoDuration(sm.networkTimeInMs())) |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 418 | .build()) |
| 419 | .build()); |
| 420 | |
ulfjack | eb8cfc1 | 2019-04-29 04:43:14 -0700 | [diff] [blame] | 421 | return executionInfo.build(); |
Benjamin Peterson | 1bbeadc | 2018-04-26 05:27:10 -0700 | [diff] [blame] | 422 | } |
| 423 | |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 424 | private static Duration toProtoDuration(int timeInMs) { |
| 425 | return Durations.fromMillis(timeInMs); |
Emil Kattainen | acf736d | 2022-07-14 05:10:52 -0700 | [diff] [blame] | 426 | } |
| 427 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 428 | /** |
ulfjack | 0858ae1 | 2018-07-27 02:37:53 -0700 | [diff] [blame] | 429 | * 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 Peng | 3551898 | 2020-12-15 06:04:28 -0800 | [diff] [blame] | 432 | private static Spawn createXmlGeneratingSpawn( |
| 433 | TestRunnerAction action, ImmutableMap<String, String> testEnv, SpawnResult result) { |
laszlocsomor | ba87ba9 | 2020-01-15 05:33:02 -0800 | [diff] [blame] | 434 | ImmutableList<String> args = |
| 435 | ImmutableList.of( |
| 436 | action.getTestXmlGeneratorScript().getExecPath().getCallablePathString(), |
| 437 | action.getTestLog().getExecPathString(), |
| 438 | action.getXmlOutputPath().getPathString(), |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 439 | Integer.toString(result.getWallTimeInMs() / 1000), |
laszlocsomor | ba87ba9 | 2020-01-15 05:33:02 -0800 | [diff] [blame] | 440 | Integer.toString(result.exitCode())); |
Yun Peng | 3551898 | 2020-12-15 06:04:28 -0800 | [diff] [blame] | 441 | 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 Wang | f5cf8b0 | 2021-11-07 20:05:55 -0800 | [diff] [blame] | 452 | 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 | } |
ulfjack | 0858ae1 | 2018-07-27 02:37:53 -0700 | [diff] [blame] | 461 | return new SimpleSpawn( |
| 462 | action, |
laszlocsomor | ba87ba9 | 2020-01-15 05:33:02 -0800 | [diff] [blame] | 463 | args, |
Googler | 2fc3d3f | 2022-02-01 06:29:56 -0800 | [diff] [blame] | 464 | envBuilder.buildOrThrow(), |
Jakob Buchgraber | 803801d | 2019-03-21 09:22:58 -0700 | [diff] [blame] | 465 | // 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 Wang | f5cf8b0 | 2021-11-07 20:05:55 -0800 | [diff] [blame] | 467 | ImmutableMap.copyOf(executionInfo), |
ulfjack | 0858ae1 | 2018-07-27 02:37:53 -0700 | [diff] [blame] | 468 | null, |
| 469 | ImmutableMap.of(), |
ulfjack | e4cca14 | 2020-01-08 04:44:40 -0800 | [diff] [blame] | 470 | /*inputs=*/ NestedSetBuilder.create( |
| 471 | Order.STABLE_ORDER, action.getTestXmlGeneratorScript(), action.getTestLog()), |
| 472 | /*tools=*/ NestedSetBuilder.emptySet(Order.STABLE_ORDER), |
Chi Wang | 79b5b5d | 2022-08-22 02:34:05 -0700 | [diff] [blame] | 473 | /*outputs=*/ ImmutableSet.of(ActionInputHelper.fromPath(action.getXmlOutputPath())), |
janakr | 82b3896 | 2022-02-01 07:12:10 -0800 | [diff] [blame] | 474 | /*mandatoryOutputs=*/ null, |
ulfjack | 0858ae1 | 2018-07-27 02:37:53 -0700 | [diff] [blame] | 475 | SpawnAction.DEFAULT_RESOURCE_SET); |
| 476 | } |
| 477 | |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 478 | 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), |
jcater | 285b17f | 2021-12-16 07:16:28 -0800 | [diff] [blame] | 499 | action.getExecutionInfo(), |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 500 | action.getLcovMergerRunfilesSupplier(), |
janakr | 82b3896 | 2022-02-01 07:12:10 -0800 | [diff] [blame] | 501 | /*filesetMappings=*/ ImmutableMap.of(), |
| 502 | /*inputs=*/ NestedSetBuilder.<ActionInput>compileOrder() |
Benjamin Peterson | 7e790a0 | 2021-04-15 01:37:10 -0700 | [diff] [blame] | 503 | .addTransitive(action.getInputs()) |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 504 | .addAll(expandedCoverageDir) |
| 505 | .add(action.getCollectCoverageScript()) |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 506 | .add(action.getCoverageManifest()) |
| 507 | .addTransitive(action.getLcovMergerFilesToRun().build()) |
| 508 | .build(), |
janakr | 82b3896 | 2022-02-01 07:12:10 -0800 | [diff] [blame] | 509 | /*tools=*/ NestedSetBuilder.emptySet(Order.STABLE_ORDER), |
| 510 | /*outputs=*/ ImmutableSet.of( |
ajurkowski | 0e71e90 | 2021-06-11 10:58:16 -0700 | [diff] [blame] | 511 | ActionInputHelper.fromPath(action.getCoverageData().getExecPath())), |
janakr | 82b3896 | 2022-02-01 07:12:10 -0800 | [diff] [blame] | 512 | /*mandatoryOutputs=*/ null, |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 513 | 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 Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 534 | @Override |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 535 | public TestResult newCachedTestResult( |
| 536 | Path execRoot, TestRunnerAction action, TestResultData data) { |
mschaller | d322b72 | 2020-12-28 15:16:32 -0800 | [diff] [blame] | 537 | return new TestResult(action, data, /*cached*/ true, execRoot, /*systemFailure=*/ null); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 538 | } |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 539 | |
George Gensure | c53bdaf | 2020-06-04 01:32:39 -0700 | [diff] [blame] | 540 | @VisibleForTesting |
| 541 | static final class StandaloneFailedAttemptResult implements FailedAttemptResult { |
ulfjack | b70f0c1 | 2019-02-27 07:23:38 -0800 | [diff] [blame] | 542 | private final TestResultData testResultData; |
| 543 | |
| 544 | StandaloneFailedAttemptResult(TestResultData testResultData) { |
| 545 | this.testResultData = testResultData; |
| 546 | } |
George Gensure | c53bdaf | 2020-06-04 01:32:39 -0700 | [diff] [blame] | 547 | |
| 548 | TestResultData testResultData() { |
| 549 | return testResultData; |
| 550 | } |
ulfjack | b70f0c1 | 2019-02-27 07:23:38 -0800 | [diff] [blame] | 551 | } |
| 552 | |
arostovtsev | a6bfab0 | 2022-02-14 22:15:58 -0800 | [diff] [blame] | 553 | private final class StandaloneTestRunnerSpawn implements TestRunnerSpawn { |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 554 | private final TestRunnerAction testAction; |
| 555 | private final ActionExecutionContext actionExecutionContext; |
| 556 | private final Spawn spawn; |
| 557 | private final Path tmpDir; |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 558 | private final Path workingDirectory; |
| 559 | private final Path execRoot; |
| 560 | |
| 561 | StandaloneTestRunnerSpawn( |
| 562 | TestRunnerAction testAction, |
| 563 | ActionExecutionContext actionExecutionContext, |
| 564 | Spawn spawn, |
| 565 | Path tmpDir, |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 566 | Path workingDirectory, |
| 567 | Path execRoot) { |
| 568 | this.testAction = testAction; |
| 569 | this.actionExecutionContext = actionExecutionContext; |
| 570 | this.spawn = spawn; |
| 571 | this.tmpDir = tmpDir; |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 572 | this.workingDirectory = workingDirectory; |
| 573 | this.execRoot = execRoot; |
| 574 | } |
| 575 | |
ulfjack | 1188344 | 2019-03-15 12:10:47 -0700 | [diff] [blame] | 576 | @Override |
| 577 | public ActionExecutionContext getActionExecutionContext() { |
| 578 | return actionExecutionContext; |
| 579 | } |
| 580 | |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 581 | @Override |
Googler | c0bbb0f | 2022-12-01 00:30:29 -0800 | [diff] [blame] | 582 | public TestAttemptResult execute() throws InterruptedException, IOException, ExecException { |
ulfjack | 71f9cea | 2019-10-17 00:32:35 -0700 | [diff] [blame] | 583 | prepareFileSystem(testAction, execRoot, tmpDir, workingDirectory); |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 584 | return beginTestAttempt(testAction, spawn, actionExecutionContext, execRoot); |
| 585 | } |
| 586 | |
| 587 | @Override |
ulfjack | b70f0c1 | 2019-02-27 07:23:38 -0800 | [diff] [blame] | 588 | 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) |
ulfjack | d23e577 | 2019-03-15 07:39:42 -0700 | [diff] [blame] | 602 | throws IOException { |
ulfjack | b70f0c1 | 2019-02-27 07:23:38 -0800 | [diff] [blame] | 603 | StandaloneTestStrategy.this.finalizeTest( |
| 604 | testAction, actionExecutionContext, (StandaloneTestResult) finalResult, failedAttempts); |
| 605 | } |
ulfjack | 797325f | 2019-10-17 06:33:40 -0700 | [diff] [blame] | 606 | |
| 607 | @Override |
| 608 | public void finalizeCancelledTest(List<FailedAttemptResult> failedAttempts) throws IOException { |
| 609 | TestResultData.Builder builder = |
mschaller | d322b72 | 2020-12-28 15:16:32 -0800 | [diff] [blame] | 610 | TestResultData.newBuilder() |
| 611 | .setCachable(false) |
| 612 | .setTestPassed(false) |
| 613 | .setStatus(BlazeTestStatus.INCOMPLETE); |
ulfjack | 797325f | 2019-10-17 06:33:40 -0700 | [diff] [blame] | 614 | StandaloneTestResult standaloneTestResult = |
| 615 | StandaloneTestResult.builder() |
| 616 | .setSpawnResults(ImmutableList.of()) |
| 617 | .setTestResultDataBuilder(builder) |
| 618 | .setExecutionInfo(ExecutionInfo.getDefaultInstance()) |
| 619 | .build(); |
| 620 | finalizeTest(standaloneTestResult, failedAttempts); |
| 621 | } |
ulfjack | b2a92ad | 2019-02-26 06:48:27 -0800 | [diff] [blame] | 622 | } |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 623 | |
Bo Zhang | 49eb31b | 2022-10-06 00:24:23 -0700 | [diff] [blame] | 624 | 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 | |
Googler | c0bbb0f | 2022-12-01 00:30:29 -0800 | [diff] [blame] | 634 | private TestAttemptResult runTestAttempt( |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 635 | 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 { |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 644 | |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 645 | 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; |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 684 | } |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 685 | long endTimeMillis = actionExecutionContext.getClock().currentTimeMillis(); |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 686 | |
Fabian Meumertzheim | f7d795a | 2023-05-08 03:43:30 -0700 | [diff] [blame] | 687 | 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 | |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 706 | // 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); |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 709 | |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 710 | // 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 = |
Googler | 9961a75 | 2023-02-23 05:21:53 -0800 | [diff] [blame] | 714 | (primaryResult.getWallTimeInMs() != 0 ? primaryResult.getWallTimeInMs() : durationMillis); |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 715 | |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 716 | 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 | } |
Googler | d8e13c8 | 2023-04-11 00:07:32 -0700 | [diff] [blame] | 736 | var unused = |
| 737 | actionExecutionContext |
Googler | 8ec4c50 | 2023-04-12 10:44:40 -0700 | [diff] [blame] | 738 | .getOutputMetadataStore() |
Googler | d8e13c8 | 2023-04-11 00:07:32 -0700 | [diff] [blame] | 739 | .getOutputMetadata(testAction.getCoverageDirectoryTreeArtifact()); |
| 740 | |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 741 | ImmutableSet<? extends ActionInput> expandedCoverageDir = |
| 742 | actionExecutionContext |
Googler | 8ec4c50 | 2023-04-12 10:44:40 -0700 | [diff] [blame] | 743 | .getOutputMetadataStore() |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 744 | .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); |
Googler | d8e13c8 | 2023-04-11 00:07:32 -0700 | [diff] [blame] | 762 | ActionExecutionContext coverageActionExecutionContext = |
| 763 | actionExecutionContext |
| 764 | .withFileOutErr(coverageOutErr) |
| 765 | .withOutputsAsInputs(expandedCoverageDir); |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 766 | |
| 767 | writeOutFile(coverageOutErr.getErrorPath(), coverageOutErr.getOutputPath()); |
| 768 | appendCoverageLog(coverageOutErr, fileOutErr); |
| 769 | try { |
Googler | d8e13c8 | 2023-04-11 00:07:32 -0700 | [diff] [blame] | 770 | spawnStrategyResolver.exec(coveragePostProcessingSpawn, coverageActionExecutionContext); |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 771 | } catch (SpawnExecException e) { |
| 772 | if (e.isCatastrophic()) { |
plf | a445cda | 2020-10-29 10:39:30 -0700 | [diff] [blame] | 773 | closeSuppressed(e, streamed); |
| 774 | closeSuppressed(e, fileOutErr); |
| 775 | throw e; |
| 776 | } |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 777 | 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. |
ulfjack | 415165a | 2019-09-12 07:56:40 -0700 | [diff] [blame] | 781 | throw e; |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 782 | } |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 783 | 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; |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 791 | } |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 792 | } |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 793 | |
| 794 | Verify.verify( |
| 795 | !(testAction.isCoverageMode() && testAction.getSplitCoveragePostProcessing()) |
Chi Wang | e8ff51f | 2023-01-18 06:00:19 -0800 | [diff] [blame] | 796 | || actionExecutionContext |
| 797 | .getPathResolver() |
| 798 | .convertPath(testAction.getCoverageData().getPath()) |
| 799 | .exists()); |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 800 | 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(); |
Googler | d8e13c8 | 2023-04-11 00:07:32 -0700 | [diff] [blame] | 834 | |
| 835 | ActionExecutionContext xmlActionExecutionContext = |
| 836 | actionExecutionContext |
| 837 | .withFileOutErr(xmlSpawnOutErr) |
| 838 | .withOutputsAsInputs(ImmutableList.of(testAction.getTestLog())); |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 839 | try { |
| 840 | |
| 841 | ImmutableList<SpawnResult> xmlSpawnResults = |
Googler | d8e13c8 | 2023-04-11 00:07:32 -0700 | [diff] [blame] | 842 | spawnStrategyResolver.exec(xmlGeneratingSpawn, xmlActionExecutionContext); |
Googler | f61e2e9 | 2022-11-30 09:19:04 -0800 | [diff] [blame] | 843 | 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); |
Googler | c0bbb0f | 2022-12-01 00:30:29 -0800 | [diff] [blame] | 861 | 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(); |
ulfjack | 4a5e1b7 | 2019-03-18 06:56:57 -0700 | [diff] [blame] | 870 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 871 | } |