blob: 34058a42f5f6d0daa46ab13d0c763dad25308615 [file] [log] [blame]
ruperts4050a892017-10-07 00:46:20 +02001// Copyright 2017 The Bazel Authors. All rights reserved.
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
15package com.google.devtools.build.lib.exec;
16
17import static com.google.common.truth.Truth.assertThat;
shahan18726b72018-03-15 14:18:46 -070018import static com.google.devtools.build.lib.testutil.TestConstants.WORKSPACE_NAME;
olaolac35fe502017-10-17 04:09:07 +020019import static java.nio.charset.StandardCharsets.UTF_8;
20import static org.junit.Assert.fail;
ruperts4050a892017-10-07 00:46:20 +020021import static org.mockito.Matchers.any;
22import static org.mockito.Mockito.when;
23
ruperts7967f332017-11-21 16:37:13 -080024import com.google.common.collect.ImmutableList;
ruperts4050a892017-10-07 00:46:20 +020025import com.google.common.collect.ImmutableMap;
ruperts4050a892017-10-07 00:46:20 +020026import com.google.common.collect.Iterables;
27import com.google.devtools.build.lib.actions.ActionExecutionContext;
shahan18726b72018-03-15 14:18:46 -070028import com.google.devtools.build.lib.actions.ActionInput;
ruperts4050a892017-10-07 00:46:20 +020029import com.google.devtools.build.lib.actions.Artifact;
30import com.google.devtools.build.lib.actions.SpawnActionContext;
31import com.google.devtools.build.lib.actions.SpawnResult;
olaolac35fe502017-10-17 04:09:07 +020032import com.google.devtools.build.lib.actions.SpawnResult.Status;
ruperts4050a892017-10-07 00:46:20 +020033import com.google.devtools.build.lib.analysis.ConfiguredTarget;
ruperts4050a892017-10-07 00:46:20 +020034import com.google.devtools.build.lib.analysis.test.TestProvider;
35import com.google.devtools.build.lib.analysis.test.TestResult;
36import com.google.devtools.build.lib.analysis.test.TestRunnerAction;
37import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
38import com.google.devtools.build.lib.clock.BlazeClock;
olaolac35fe502017-10-17 04:09:07 +020039import com.google.devtools.build.lib.exec.TestStrategy.TestOutputFormat;
40import com.google.devtools.build.lib.util.io.FileOutErr;
41import com.google.devtools.build.lib.vfs.FileSystemUtils;
ruperts4050a892017-10-07 00:46:20 +020042import com.google.devtools.build.lib.vfs.Path;
olaolac35fe502017-10-17 04:09:07 +020043import com.google.devtools.common.options.Options;
ruperts4050a892017-10-07 00:46:20 +020044import java.io.IOException;
olaolac35fe502017-10-17 04:09:07 +020045import java.io.OutputStream;
ruperts02fb4bb2017-11-04 06:40:14 +010046import java.time.Duration;
ruperts4050a892017-10-07 00:46:20 +020047import java.util.List;
ruperts4050a892017-10-07 00:46:20 +020048import org.junit.Before;
49import org.junit.Test;
50import org.junit.runner.RunWith;
51import org.junit.runners.JUnit4;
52import org.mockito.Mock;
53import org.mockito.MockitoAnnotations;
olaolac35fe502017-10-17 04:09:07 +020054import org.mockito.invocation.InvocationOnMock;
55import org.mockito.stubbing.Answer;
ruperts4050a892017-10-07 00:46:20 +020056
57/** Unit tests for {@link StandaloneTestStrategy}. */
58@RunWith(JUnit4.class)
59public final class StandaloneTestStrategyTest extends BuildViewTestCase {
60
61 private static class TestedStandaloneTestStrategy extends StandaloneTestStrategy {
62 public TestedStandaloneTestStrategy(
63 ExecutionOptions executionOptions, BinTools binTools, Path tmpDirRoot) {
64 super(executionOptions, binTools, tmpDirRoot);
65 }
66
67 @Override
68 protected void postTestResult(ActionExecutionContext actionExecutionContext, TestResult result)
69 throws IOException {
70 // Make postTestResult a no-op for testing purposes
71 }
72 }
73
74 @Mock private ActionExecutionContext actionExecutionContext;
75
76 @Mock private SpawnActionContext spawnActionContext;
77
78 @Before
79 public final void setUp() throws Exception {
80 MockitoAnnotations.initMocks(this);
81 }
82
83 @Test
84 public void testSpawnResultsAreReturned() throws Exception {
85
86 // setup a StandaloneTestStrategy
87
88 ExecutionOptions executionOptions = ExecutionOptions.DEFAULTS;
89 Path tmpDirRoot = TestStrategy.getTmpRoot(rootDirectory, outputBase, executionOptions);
ulfjack0588ba02018-02-15 10:40:26 -080090 BinTools binTools = BinTools.forUnitTesting(directories, analysisMock.getEmbeddedTools());
ruperts4050a892017-10-07 00:46:20 +020091 TestedStandaloneTestStrategy standaloneTestStrategy =
92 new TestedStandaloneTestStrategy(executionOptions, binTools, tmpDirRoot);
93
94 // setup a test action
95
olaolac35fe502017-10-17 04:09:07 +020096 scratch.file("standalone/simple_test.sh", "this does not get executed, it is mocked out");
ruperts4050a892017-10-07 00:46:20 +020097
98 scratch.file(
99 "standalone/BUILD",
100 "sh_test(",
101 " name = \"simple_test\",",
102 " size = \"small\",",
103 " srcs = [\"simple_test.sh\"],",
104 ")");
105
106 ConfiguredTarget configuredTarget = getConfiguredTarget("//standalone:simple_test");
107 List<Artifact> testStatusArtifacts =
108 configuredTarget.getProvider(TestProvider.class).getTestParams().getTestStatusArtifacts();
109 Artifact testStatusArtifact = Iterables.getOnlyElement(testStatusArtifacts);
110 TestRunnerAction testRunnerAction = (TestRunnerAction) getGeneratingAction(testStatusArtifact);
olaolac35fe502017-10-17 04:09:07 +0200111 FileSystemUtils.createDirectoryAndParents(
112 testRunnerAction.getTestLog().getPath().getParentDirectory());
ruperts4050a892017-10-07 00:46:20 +0200113
114 // setup a mock ActionExecutionContext
115
116 when(actionExecutionContext.getClock()).thenReturn(BlazeClock.instance());
117 when(actionExecutionContext.withFileOutErr(any())).thenReturn(actionExecutionContext);
118 when(actionExecutionContext.getExecRoot()).thenReturn(outputBase.getRelative("execroot"));
119 when(actionExecutionContext.getClientEnv()).thenReturn(ImmutableMap.of());
120 when(actionExecutionContext.getEventHandler()).thenReturn(reporter);
121 when(actionExecutionContext.getEventBus()).thenReturn(eventBus);
shahan18726b72018-03-15 14:18:46 -0700122 when(actionExecutionContext.getInputPath(any())).thenAnswer(this::getInputPathMock);
ruperts4050a892017-10-07 00:46:20 +0200123
ruperts4050a892017-10-07 00:46:20 +0200124 SpawnResult expectedSpawnResult =
ruperts02fb4bb2017-11-04 06:40:14 +0100125 new SpawnResult.Builder()
126 .setStatus(Status.SUCCESS)
127 .setWallTime(Duration.ofMillis(10))
Googler4dd6f002018-03-27 08:15:39 -0700128 .setRunnerName("test")
ruperts02fb4bb2017-11-04 06:40:14 +0100129 .build();
ruperts7967f332017-11-21 16:37:13 -0800130 when(spawnActionContext.exec(any(), any())).thenReturn(ImmutableList.of(expectedSpawnResult));
ruperts4050a892017-10-07 00:46:20 +0200131
Googler2c3990c2018-03-26 16:33:56 -0700132 when(actionExecutionContext.getSpawnActionContext(any())).thenReturn(spawnActionContext);
ruperts4050a892017-10-07 00:46:20 +0200133
134 // actual StandaloneTestStrategy execution
ruperts7967f332017-11-21 16:37:13 -0800135 List<SpawnResult> spawnResults =
ruperts4050a892017-10-07 00:46:20 +0200136 standaloneTestStrategy.exec(testRunnerAction, actionExecutionContext);
137
138 // check that the rigged SpawnResult was returned
ruperts4050a892017-10-07 00:46:20 +0200139 assertThat(spawnResults).containsExactly(expectedSpawnResult);
olaolac35fe502017-10-17 04:09:07 +0200140 }
141
142 @Test
143 public void testThatTestLogAndOutputAreReturned() throws Exception {
144
145 // setup a StandaloneTestStrategy
146
147 ExecutionOptions executionOptions = Options.getDefaults(ExecutionOptions.class);
148 executionOptions.testOutput = TestOutputFormat.ERRORS;
149 Path tmpDirRoot = TestStrategy.getTmpRoot(rootDirectory, outputBase, executionOptions);
ulfjack0588ba02018-02-15 10:40:26 -0800150 BinTools binTools = BinTools.forUnitTesting(directories, analysisMock.getEmbeddedTools());
olaolac35fe502017-10-17 04:09:07 +0200151 TestedStandaloneTestStrategy standaloneTestStrategy =
152 new TestedStandaloneTestStrategy(executionOptions, binTools, tmpDirRoot);
153
154 // setup a test action
155
156 scratch.file("standalone/failing_test.sh", "this does not get executed, it is mocked out");
157
158 scratch.file(
159 "standalone/BUILD",
160 "sh_test(",
161 " name = \"failing_test\",",
162 " size = \"small\",",
163 " srcs = [\"failing_test.sh\"],",
164 ")");
165
166 ConfiguredTarget configuredTarget = getConfiguredTarget("//standalone:failing_test");
167 List<Artifact> testStatusArtifacts =
168 configuredTarget.getProvider(TestProvider.class).getTestParams().getTestStatusArtifacts();
169 Artifact testStatusArtifact = Iterables.getOnlyElement(testStatusArtifacts);
170 TestRunnerAction testRunnerAction = (TestRunnerAction) getGeneratingAction(testStatusArtifact);
171 FileSystemUtils.createDirectoryAndParents(
172 testRunnerAction.getTestLog().getPath().getParentDirectory());
173 // setup a mock ActionExecutionContext
174
175 when(actionExecutionContext.getClock()).thenReturn(BlazeClock.instance());
176 when(actionExecutionContext.withFileOutErr(any()))
177 .thenAnswer(
178 new Answer<ActionExecutionContext>() {
179 @SuppressWarnings("unchecked")
180 @Override
181 public ActionExecutionContext answer(InvocationOnMock invocation) throws Throwable {
182 FileOutErr outErr = (FileOutErr) invocation.getArguments()[0];
183 try (OutputStream stream = outErr.getOutputStream()) {
184 stream.write("This will not appear in the test output: bla\n".getBytes(UTF_8));
185 stream.write((TestLogHelper.HEADER_DELIMITER + "\n").getBytes(UTF_8));
186 stream.write("This will appear in the test output: foo\n".getBytes(UTF_8));
187 }
188 return actionExecutionContext;
189 }
190 });
191 reporter.removeHandler(failFastHandler);
192 when(actionExecutionContext.getExecRoot()).thenReturn(outputBase.getRelative("execroot"));
193 when(actionExecutionContext.getClientEnv()).thenReturn(ImmutableMap.of());
194 when(actionExecutionContext.getEventHandler()).thenReturn(reporter);
195 when(actionExecutionContext.getEventBus()).thenReturn(eventBus);
shahan18726b72018-03-15 14:18:46 -0700196 when(actionExecutionContext.getInputPath(any())).thenAnswer(this::getInputPathMock);
197
olaolac35fe502017-10-17 04:09:07 +0200198 Path outPath = tmpDirRoot.getRelative("test-out.txt");
199 Path errPath = tmpDirRoot.getRelative("test-err.txt");
200 FileOutErr outErr = new FileOutErr(outPath, errPath);
201 when(actionExecutionContext.getFileOutErr()).thenReturn(outErr);
202
203 SpawnResult expectedSpawnResult =
Googler4dd6f002018-03-27 08:15:39 -0700204 new SpawnResult.Builder()
205 .setStatus(Status.NON_ZERO_EXIT)
206 .setExitCode(1)
207 .setRunnerName("test")
208 .build();
olaolac35fe502017-10-17 04:09:07 +0200209 when(spawnActionContext.exec(any(), any()))
210 .thenThrow(
211 new SpawnExecException(
212 "Failure!!",
213 expectedSpawnResult,
214 /*forciblyRunRemotely=*/ false,
215 /*catastrophe=*/ false));
Googler2c3990c2018-03-26 16:33:56 -0700216 when(actionExecutionContext.getSpawnActionContext(any())).thenReturn(spawnActionContext);
olaolac35fe502017-10-17 04:09:07 +0200217
218 // actual StandaloneTestStrategy execution
ruperts7967f332017-11-21 16:37:13 -0800219 List<SpawnResult> spawnResults =
olaolac35fe502017-10-17 04:09:07 +0200220 standaloneTestStrategy.exec(testRunnerAction, actionExecutionContext);
221
222 // check that the rigged SpawnResult was returned
223 assertThat(spawnResults).containsExactly(expectedSpawnResult);
224 // check that the test log contains all the output
225 try {
226 String logData = FileSystemUtils.readContent(testRunnerAction.getTestLog().getPath(), UTF_8);
227 assertThat(logData).contains("bla");
228 assertThat(logData).contains(TestLogHelper.HEADER_DELIMITER);
229 assertThat(logData).contains("foo");
230 } catch (IOException e) {
231 fail("Test log missing: " + testRunnerAction.getTestLog().getPath());
232 }
233 // check that the test stdout contains all the expected output
234 outErr.close(); // Create the output files.
235 try {
236 String outData = FileSystemUtils.readContent(outPath, UTF_8);
237 assertThat(outData)
238 .contains("==================== Test output for //standalone:failing_test:");
239 assertThat(outData).doesNotContain("bla");
240 assertThat(outData).doesNotContain(TestLogHelper.HEADER_DELIMITER);
241 assertThat(outData).contains("foo");
242 assertThat(outData)
243 .contains(
244 "================================================================================");
245 } catch (IOException e) {
246 fail("Test stdout file missing: " + outPath);
247 }
248 assertThat(errPath.exists()).isFalse();
249 }
250
251 @Test
252 public void testEmptyOutputCreatesEmptyLogFile() throws Exception {
253 // setup a StandaloneTestStrategy
254 ExecutionOptions executionOptions = Options.getDefaults(ExecutionOptions.class);
255 executionOptions.testOutput = TestOutputFormat.ALL;
256 Path tmpDirRoot = TestStrategy.getTmpRoot(rootDirectory, outputBase, executionOptions);
ulfjack0588ba02018-02-15 10:40:26 -0800257 BinTools binTools = BinTools.forUnitTesting(directories, analysisMock.getEmbeddedTools());
olaolac35fe502017-10-17 04:09:07 +0200258 TestedStandaloneTestStrategy standaloneTestStrategy =
259 new TestedStandaloneTestStrategy(executionOptions, binTools, tmpDirRoot);
260
261 // setup a test action
262 scratch.file("standalone/empty_test.sh", "this does not get executed, it is mocked out");
263 scratch.file(
264 "standalone/BUILD",
265 "sh_test(",
266 " name = \"empty_test\",",
267 " size = \"small\",",
268 " srcs = [\"empty_test.sh\"],",
269 ")");
270 ConfiguredTarget configuredTarget = getConfiguredTarget("//standalone:empty_test");
271 List<Artifact> testStatusArtifacts =
272 configuredTarget.getProvider(TestProvider.class).getTestParams().getTestStatusArtifacts();
273 Artifact testStatusArtifact = Iterables.getOnlyElement(testStatusArtifacts);
274 TestRunnerAction testRunnerAction = (TestRunnerAction) getGeneratingAction(testStatusArtifact);
275 FileSystemUtils.createDirectoryAndParents(
276 testRunnerAction.getTestLog().getPath().getParentDirectory());
277
278 // setup a mock ActionExecutionContext
279 when(actionExecutionContext.getClock()).thenReturn(BlazeClock.instance());
280 when(actionExecutionContext.withFileOutErr(any())).thenReturn(actionExecutionContext);
281 when(actionExecutionContext.getExecRoot()).thenReturn(outputBase.getRelative("execroot"));
282 when(actionExecutionContext.getClientEnv()).thenReturn(ImmutableMap.of());
283 when(actionExecutionContext.getEventHandler()).thenReturn(reporter);
284 when(actionExecutionContext.getEventBus()).thenReturn(eventBus);
shahan18726b72018-03-15 14:18:46 -0700285 when(actionExecutionContext.getInputPath(any())).thenAnswer(this::getInputPathMock);
olaolac35fe502017-10-17 04:09:07 +0200286 Path outPath = tmpDirRoot.getRelative("test-out.txt");
287 Path errPath = tmpDirRoot.getRelative("test-err.txt");
288 FileOutErr outErr = new FileOutErr(outPath, errPath);
289 when(actionExecutionContext.getFileOutErr()).thenReturn(outErr);
290
Googler4dd6f002018-03-27 08:15:39 -0700291 SpawnResult expectedSpawnResult =
292 new SpawnResult.Builder().setStatus(Status.SUCCESS).setRunnerName("test").build();
ruperts7967f332017-11-21 16:37:13 -0800293 when(spawnActionContext.exec(any(), any())).thenReturn(ImmutableList.of(expectedSpawnResult));
Googler2c3990c2018-03-26 16:33:56 -0700294 when(actionExecutionContext.getSpawnActionContext(any())).thenReturn(spawnActionContext);
olaolac35fe502017-10-17 04:09:07 +0200295
296 // actual StandaloneTestStrategy execution
ruperts7967f332017-11-21 16:37:13 -0800297 List<SpawnResult> spawnResults =
olaolac35fe502017-10-17 04:09:07 +0200298 standaloneTestStrategy.exec(testRunnerAction, actionExecutionContext);
299
300 // check that the rigged SpawnResult was returned
301 assertThat(spawnResults).containsExactly(expectedSpawnResult);
302 // check that the test log contains all the output
303 try {
304 String logData = FileSystemUtils.readContent(testRunnerAction.getTestLog().getPath(), UTF_8);
305 assertThat(logData).isEmpty();
306 } catch (IOException e) {
307 fail("Test log missing: " + testRunnerAction.getTestLog().getPath());
308 }
309 // check that the test stdout contains all the expected output
310 outErr.close(); // Create the output files.
311 try {
312 String outData = FileSystemUtils.readContent(outPath, UTF_8);
313 String emptyOutput =
314 "==================== Test output for //standalone:empty_test:(\\s)*"
315 + "================================================================================(\\s)*";
316 assertThat(outData).matches(emptyOutput);
317 } catch (IOException e) {
318 fail("Test stdout file missing: " + outPath);
319 }
320 assertThat(errPath.exists()).isFalse();
ruperts4050a892017-10-07 00:46:20 +0200321 }
shahan18726b72018-03-15 14:18:46 -0700322
323 private Path getInputPathMock(InvocationOnMock invocation) {
324 return outputBase
325 .getRelative("execroot/" + WORKSPACE_NAME)
326 .getRelative(invocation.getArgumentAt(0, ActionInput.class).getExecPath());
327 }
ruperts4050a892017-10-07 00:46:20 +0200328}