blob: 72af7d934b496d9afaf30a0ea4653161ac7f6c5f [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +00002//
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.
14package com.google.devtools.build.lib.shell;
15
Ulf Adams795895a2015-03-06 15:58:35 +000016import static com.google.common.truth.Truth.assertThat;
lberkiaea56b32017-05-30 12:35:33 +020017import static com.google.common.truth.Truth.assertWithMessage;
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000018import static com.google.devtools.build.lib.shell.TestUtil.assertArrayEquals;
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000019import static org.junit.Assert.fail;
20
Dmitry Lomov8efc3ef2016-02-17 09:50:03 +000021import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000022import com.google.devtools.build.lib.testutil.BlazeTestUtils;
23import com.google.devtools.build.lib.testutil.TestConstants;
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000024import java.io.ByteArrayInputStream;
25import java.io.ByteArrayOutputStream;
26import java.io.File;
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
ulfjackf2d45952017-08-09 15:27:49 +020030import java.time.Duration;
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000031import java.util.Collections;
32import java.util.Map;
33import java.util.logging.Level;
34import java.util.logging.Logger;
lberkiaea56b32017-05-30 12:35:33 +020035import org.junit.Before;
36import org.junit.Test;
37import org.junit.runner.RunWith;
38import org.junit.runners.JUnit4;
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000039
40/**
ulfjackf2d45952017-08-09 15:27:49 +020041 * Unit tests for {@link Command}. This test will only succeed on Linux, currently, because of its
42 * non-portable nature.
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000043 */
44@RunWith(JUnit4.class)
45public class CommandTest {
46
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000047 // Platform-independent tests ----------------------------------------------
48
49 @Before
Florian Weikert2c71a822015-12-02 17:22:38 +000050 public final void configureLogger() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +020051 // Enable all log statements to ensure there are no problems with logging code.
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000052 Logger.getLogger("com.google.devtools.build.lib.shell.Command").setLevel(Level.FINEST);
53 }
54
55 @Test
56 public void testIllegalArgs() throws Exception {
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000057 try {
58 new Command((String[]) null);
ulfjackf2d45952017-08-09 15:27:49 +020059 fail("Should have thrown NullPointerException");
60 } catch (NullPointerException iae) {
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000061 // good
62 }
63
64 try {
65 new Command(new String[] {"/bin/true", null}).execute();
66 fail("Should have thrown NullPointerException");
67 } catch (NullPointerException npe) {
68 // good
69 }
70
71 try {
ulfjackf2d45952017-08-09 15:27:49 +020072 Command r = new Command(new String[] {"foo"});
73 r.executeAsync((InputStream) null, Command.KILL_SUBPROCESS_ON_INTERRUPT).get();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000074 fail("Should have thrown NullPointerException");
75 } catch (NullPointerException npe) {
76 // good
77 }
78
79 }
80
81 @Test
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000082 public void testGetters() {
ulfjackf2d45952017-08-09 15:27:49 +020083 File workingDir = new File(".");
84 Map<String, String> env = Collections.singletonMap("foo", "bar");
85 String[] commandArgs = new String[] { "command" };
86 Command command = new Command(commandArgs, env, workingDir);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000087 assertArrayEquals(commandArgs, command.getCommandLineElements());
ulfjackf2d45952017-08-09 15:27:49 +020088 for (String key : env.keySet()) {
Ulf Adams795895a2015-03-06 15:58:35 +000089 assertThat(command.getEnvironmentVariables()).containsEntry(key, env.get(key));
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000090 }
lberkiaea56b32017-05-30 12:35:33 +020091 assertThat(command.getWorkingDirectory()).isEqualTo(workingDir);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +000092 }
93
94 // Platform-dependent tests ------------------------------------------------
95
96 @Test
97 public void testSimpleCommand() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +020098 Command command = new Command(new String[] {"ls"});
99 CommandResult result = command.execute();
lberkiaea56b32017-05-30 12:35:33 +0200100 assertThat(result.getTerminationStatus().success()).isTrue();
101 assertThat(result.getStderr()).isEmpty();
Ulf Adams795895a2015-03-06 15:58:35 +0000102 assertThat(result.getStdout().length).isGreaterThan(0);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000103 }
104
105 @Test
106 public void testArguments() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200107 Command command = new Command(new String[] {"echo", "foo"});
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000108 checkSuccess(command.execute(), "foo\n");
109 }
110
111 @Test
112 public void testEnvironment() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200113 Map<String, String> env = Collections.singletonMap("FOO", "BAR");
114 Command command = new Command(new String[] {"/bin/sh", "-c", "echo $FOO"}, env, null);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000115 checkSuccess(command.execute(), "BAR\n");
116 }
117
118 @Test
119 public void testWorkingDir() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200120 Command command = new Command(new String[] {"pwd"}, null, new File("/"));
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000121 checkSuccess(command.execute(), "/\n");
122 }
123
124 @Test
125 public void testStdin() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200126 Command command = new Command(new String[] {"grep", "bar"});
127 InputStream in = new ByteArrayInputStream("foobarbaz".getBytes());
128 checkSuccess(
129 command.executeAsync(in, Command.KILL_SUBPROCESS_ON_INTERRUPT).get(),
130 "foobarbaz\n");
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000131 }
132
133 @Test
134 public void testRawCommand() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200135 Command command = new Command(new String[] { "perl", "-e", "print 'a'x100000" });
136 CommandResult result = command.execute();
lberkiaea56b32017-05-30 12:35:33 +0200137 assertThat(result.getTerminationStatus().success()).isTrue();
138 assertThat(result.getStderr()).isEmpty();
Ulf Adams795895a2015-03-06 15:58:35 +0000139 assertThat(result.getStdout().length).isGreaterThan(0);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000140 }
141
142 @Test
143 public void testRawCommandWithDir() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200144 Command command = new Command(new String[] { "pwd" }, null, new File("/"));
145 CommandResult result = command.execute();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000146 checkSuccess(result, "/\n");
147 }
148
149 @Test
150 public void testHugeOutput() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200151 Command command = new Command(new String[] {"perl", "-e", "print 'a'x100000"});
152 CommandResult result = command.execute();
lberkiaea56b32017-05-30 12:35:33 +0200153 assertThat(result.getTerminationStatus().success()).isTrue();
154 assertThat(result.getStderr()).isEmpty();
155 assertThat(result.getStdout()).hasLength(100000);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000156 }
157
158 @Test
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000159 public void testNoStreamingInputForCat() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200160 Command command = new Command(new String[]{"/bin/cat"});
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000161 ByteArrayInputStream emptyInput = new ByteArrayInputStream(new byte[0]);
162 ByteArrayOutputStream out = new ByteArrayOutputStream();
163 ByteArrayOutputStream err = new ByteArrayOutputStream();
ulfjackf2d45952017-08-09 15:27:49 +0200164 CommandResult result = command
165 .executeAsync(emptyInput, out, err, Command.KILL_SUBPROCESS_ON_INTERRUPT)
166 .get();
lberkiaea56b32017-05-30 12:35:33 +0200167 assertThat(result.getTerminationStatus().success()).isTrue();
Ulf Adams795895a2015-03-06 15:58:35 +0000168 assertThat(out.toString("UTF-8")).isEmpty();
169 assertThat(err.toString("UTF-8")).isEmpty();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000170 }
171
172 @Test
173 public void testNoInputForCat() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200174 Command command = new Command(new String[]{"/bin/cat"});
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000175 CommandResult result = command.execute();
lberkiaea56b32017-05-30 12:35:33 +0200176 assertThat(result.getTerminationStatus().success()).isTrue();
Ulf Adams795895a2015-03-06 15:58:35 +0000177 assertThat(new String(result.getStdout(), "UTF-8")).isEmpty();
178 assertThat(new String(result.getStderr(), "UTF-8")).isEmpty();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000179 }
180
181 @Test
182 public void testProvidedOutputStreamCapturesHelloWorld() throws Exception {
183 String helloWorld = "Hello, world.";
ulfjackf2d45952017-08-09 15:27:49 +0200184 Command command = new Command(new String[]{"/bin/echo", helloWorld});
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000185 ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
186 ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
ulfjackf2d45952017-08-09 15:27:49 +0200187 command.execute(stdOut, stdErr);
lberkiaea56b32017-05-30 12:35:33 +0200188 assertThat(stdOut.toString("UTF-8")).isEqualTo(helloWorld + "\n");
189 assertThat(stdErr.toByteArray()).isEmpty();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000190 }
191
192 @Test
193 public void testAsynchronous() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200194 File tempFile = File.createTempFile("googlecron-test", "tmp");
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000195 tempFile.delete();
ulfjackf2d45952017-08-09 15:27:49 +0200196 Command command = new Command(new String[] {"touch", tempFile.getAbsolutePath()});
197 FutureCommandResult result = command.executeAsync();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000198 result.get();
lberkiaea56b32017-05-30 12:35:33 +0200199 assertThat(tempFile.exists()).isTrue();
200 assertThat(result.isDone()).isTrue();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000201 tempFile.delete();
202 }
203
204 @Test
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000205 public void testAsynchronousWithOutputStreams() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200206 String helloWorld = "Hello, world.";
207 Command command = new Command(new String[]{"/bin/echo", helloWorld});
208 ByteArrayInputStream emptyInput = new ByteArrayInputStream(new byte[0]);
209 ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
210 ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
cushon03e70182017-09-15 09:33:27 +0200211 FutureCommandResult result =
212 command.executeAsync(emptyInput, stdOut, stdErr, /* killSubprocessOnInterrupt= */ false);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000213 result.get(); // Make sure the process actually finished
lberkiaea56b32017-05-30 12:35:33 +0200214 assertThat(stdOut.toString("UTF-8")).isEqualTo(helloWorld + "\n");
215 assertThat(stdErr.toByteArray()).isEmpty();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000216 }
217
218 @Test
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000219 public void testTimeout() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200220 // Sleep for 3 seconds, but timeout after 1 second.
221 Command command = new Command(new String[] {"sleep", "3"}, null, null, Duration.ofSeconds(1));
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000222 try {
ulfjackf2d45952017-08-09 15:27:49 +0200223 command.execute();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000224 fail("Should have thrown AbnormalTerminationException");
225 } catch (AbnormalTerminationException ate) {
226 // good
227 checkCommandElements(ate, "sleep", "3");
228 checkATE(ate);
229 }
230 }
231
232 @Test
233 public void testTimeoutDoesntFire() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200234 Command command = new Command(new String[] {"cat"}, null, null, Duration.ofSeconds(2));
235 InputStream in = new ByteArrayInputStream(new byte[]{'H', 'i', '!'});
236 command.executeAsync(in, Command.KILL_SUBPROCESS_ON_INTERRUPT).get();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000237 }
238
239 @Test
240 public void testCommandDoesNotExist() throws Exception {
ulfjackf2d45952017-08-09 15:27:49 +0200241 Command command = new Command(new String[]{"thisisnotreal"});
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000242 try {
243 command.execute();
244 fail();
245 } catch (ExecFailedException e){
246 // Good.
247 checkCommandElements(e, "thisisnotreal");
248 }
249 }
250
251 @Test
252 public void testNoSuchCommand() throws Exception {
253 final Command command = new Command(new String[] {"thisisnotreal"});
254 try {
255 command.execute();
256 fail("Should have thrown ExecFailedException");
257 } catch (ExecFailedException expected) {
258 // good
259 }
260 }
261
262 @Test
263 public void testExitCodes() throws Exception {
264 // 0 => success
265 {
ulfjackf2d45952017-08-09 15:27:49 +0200266 String[] args = { "/bin/sh", "-c", "exit 0" };
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000267 CommandResult result = new Command(args).execute();
268 TerminationStatus status = result.getTerminationStatus();
lberkiaea56b32017-05-30 12:35:33 +0200269 assertThat(status.success()).isTrue();
270 assertThat(status.exited()).isTrue();
271 assertThat(status.getExitCode()).isEqualTo(0);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000272 }
273
274 // Every exit value in range [1-255] is reported as such (except [129-191],
275 // which map to signals).
276 for (int exit : new int[] { 1, 2, 3, 127, 128, 192, 255 }) {
277 try {
ulfjackf2d45952017-08-09 15:27:49 +0200278 String[] args = { "/bin/sh", "-c", "exit " + exit };
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000279 new Command(args).execute();
280 fail("Should have exited with status " + exit);
281 } catch (BadExitStatusException e) {
ulfjack0ec71802017-08-10 10:28:50 +0200282 assertThat(e).hasMessageThat().isEqualTo("Process exited with status " + exit);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000283 checkCommandElements(e, "/bin/sh", "-c", "exit " + exit);
284 TerminationStatus status = e.getResult().getTerminationStatus();
lberkiaea56b32017-05-30 12:35:33 +0200285 assertThat(status.success()).isFalse();
286 assertThat(status.exited()).isTrue();
287 assertThat(status.getExitCode()).isEqualTo(exit);
288 assertThat(status.toShortString()).isEqualTo("Exit " + exit);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000289 }
290 }
291
292 // negative exit values are modulo 256:
293 for (int exit : new int[] { -1, -2, -3 }) {
294 int expected = 256 + exit;
295 try {
ulfjackf2d45952017-08-09 15:27:49 +0200296 String[] args = { "/bin/bash", "-c", "exit " + exit };
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000297 new Command(args).execute();
298 fail("Should have exited with status " + expected);
299 } catch (BadExitStatusException e) {
ulfjack0ec71802017-08-10 10:28:50 +0200300 assertThat(e).hasMessageThat().isEqualTo("Process exited with status " + expected);
Kristina Chodorow31ccc992015-04-10 19:10:59 +0000301 checkCommandElements(e, "/bin/bash", "-c", "exit " + exit);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000302 TerminationStatus status = e.getResult().getTerminationStatus();
lberkiaea56b32017-05-30 12:35:33 +0200303 assertThat(status.success()).isFalse();
304 assertThat(status.exited()).isTrue();
305 assertThat(status.getExitCode()).isEqualTo(expected);
306 assertThat(status.toShortString()).isEqualTo("Exit " + expected);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000307 }
308 }
309 }
310
311 @Test
312 public void testFailedWithSignal() throws Exception {
313 // SIGHUP, SIGINT, SIGKILL, SIGTERM
314 for (int signal : new int[] { 1, 2, 9, 15 }) {
315 // Invoke a C++ program (killmyself.cc) that will die
316 // with the specified signal.
317 String killmyself = BlazeTestUtils.runfilesDir() + "/"
318 + TestConstants.JAVATESTS_ROOT
319 + "/com/google/devtools/build/lib/shell/killmyself";
320 try {
ulfjackf2d45952017-08-09 15:27:49 +0200321 String[] args = { killmyself, "" + signal };
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000322 new Command(args).execute();
323 fail("Expected signal " + signal);
324 } catch (AbnormalTerminationException e) {
ulfjack0ec71802017-08-10 10:28:50 +0200325 assertThat(e).hasMessageThat().isEqualTo("Process terminated by signal " + signal);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000326 checkCommandElements(e, killmyself, "" + signal);
327 TerminationStatus status = e.getResult().getTerminationStatus();
lberkiaea56b32017-05-30 12:35:33 +0200328 assertThat(status.success()).isFalse();
329 assertThat(status.exited()).isFalse();
330 assertThat(status.getTerminatingSignal()).isEqualTo(signal);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000331
332 switch (signal) {
lberkie355e772017-05-31 14:34:53 +0200333 case 1: assertThat(status.toShortString()).isEqualTo("Hangup"); break;
334 case 2: assertThat(status.toShortString()).isEqualTo("Interrupt"); break;
335 case 9: assertThat(status.toShortString()).isEqualTo("Killed"); break;
336 case 15: assertThat(status.toShortString()).isEqualTo("Terminated"); break;
Googlerfeb9c872018-07-30 05:06:31 -0700337 default: // fall out
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000338 }
339 }
340 }
341 }
342
343 @Test
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000344 public void testOnlyReadsPartialInput() throws Exception {
Han-Wen Nienhuys924daad2015-02-16 15:01:06 +0000345 // -c == --bytes, but -c also works on Darwin.
346 Command command = new Command(new String[] {"head", "-c", "500"});
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000347 OutputStream out = new ByteArrayOutputStream();
348 InputStream in = new InputStream() {
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000349 @Override
350 public int read() {
351 return 0; // write an unbounded amount
352 }
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000353 };
354
ulfjackf2d45952017-08-09 15:27:49 +0200355 CommandResult result =
356 command.executeAsync(in, out, out, Command.KILL_SUBPROCESS_ON_INTERRUPT).get();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000357 TerminationStatus status = result.getTerminationStatus();
lberkiaea56b32017-05-30 12:35:33 +0200358 assertThat(status.success()).isTrue();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000359 }
360
361 @Test
362 public void testFlushing() throws Exception {
363 final Command command = new Command(
Han-Wen Nienhuys8dff4292015-02-16 14:38:06 +0000364 // On darwin, /bin/sh does not support -n for the echo builtin.
365 new String[] {"/bin/bash", "-c", "echo -n Foo; sleep 0.1; echo Bar"});
ulfjackf2d45952017-08-09 15:27:49 +0200366 // We run this command, passing in a special output stream that records when each flush()
367 // occurs. We test that a flush occurs after writing "Foo" and that another flush occurs after
368 // writing "Bar\n".
369 boolean[] flushed = new boolean[8];
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000370 OutputStream out = new OutputStream() {
371 private int count = 0;
372 @Override
373 public void write(int b) throws IOException {
374 count++;
375 }
376 @Override
377 public void flush() throws IOException {
378 flushed[count] = true;
379 }
380 };
ulfjackf2d45952017-08-09 15:27:49 +0200381 command.execute(out, System.err);
lberkiaea56b32017-05-30 12:35:33 +0200382 assertThat(flushed[0]).isFalse();
383 assertThat(flushed[1]).isFalse(); // 'F'
384 assertThat(flushed[2]).isFalse(); // 'o'
385 assertThat(flushed[3]).isTrue(); // 'o' <- expect flush here.
386 assertThat(flushed[4]).isFalse(); // 'B'
387 assertThat(flushed[5]).isFalse(); // 'a'
388 assertThat(flushed[6]).isFalse(); // 'r'
389 assertThat(flushed[7]).isTrue(); // '\n'
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000390 }
391
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000392 @Test
393 public void testOutputStreamThrowsException() throws Exception {
394 OutputStream out = new OutputStream () {
395 @Override
396 public void write(int b) throws IOException {
397 throw new IOException();
398 }
399 };
400 Command command = new Command(new String[] {"/bin/echo", "foo"});
401 try {
ulfjackf2d45952017-08-09 15:27:49 +0200402 command.execute(out, out);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000403 fail();
404 } catch (AbnormalTerminationException e) {
405 // Good.
406 checkCommandElements(e, "/bin/echo", "foo");
ulfjackf2d45952017-08-09 15:27:49 +0200407 assertThat(e).hasMessageThat().isEqualTo("java.io.IOException");
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000408 }
409 }
410
411 @Test
ulfjackf2d45952017-08-09 15:27:49 +0200412 public void testOutputStreamThrowsExceptionAndCommandFails() throws Exception {
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000413 OutputStream out = new OutputStream () {
414 @Override
415 public void write(int b) throws IOException {
416 throw new IOException();
417 }
418 };
419 Command command = new Command(new String[] {"cat", "/dev/thisisnotreal"});
420 try {
ulfjackf2d45952017-08-09 15:27:49 +0200421 command.execute(out, out);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000422 fail();
423 } catch (AbnormalTerminationException e) {
424 checkCommandElements(e, "cat", "/dev/thisisnotreal");
425 TerminationStatus status = e.getResult().getTerminationStatus();
426 // Subprocess either gets a SIGPIPE trying to write to our output stream,
427 // or it exits with failure. Both are observed, nondetermistically.
lberkiaea56b32017-05-30 12:35:33 +0200428 assertThat(status.exited() ? status.getExitCode() == 1 : status.getTerminatingSignal() == 13)
429 .isTrue();
430 assertWithMessage(e.getMessage())
431 .that(
432 e.getMessage()
433 .endsWith("also encountered an error while attempting " + "to retrieve output"))
434 .isTrue();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000435 }
436 }
437
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000438 private static void checkCommandElements(CommandException e,
439 String... expected) {
440 assertArrayEquals(expected, e.getCommand().getCommandLineElements());
441 }
442
443 private static void checkATE(final AbnormalTerminationException ate) {
444 final CommandResult result = ate.getResult();
lberkiaea56b32017-05-30 12:35:33 +0200445 assertThat(result.getTerminationStatus().success()).isFalse();
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000446 }
447
448 private static void checkSuccess(final CommandResult result,
449 final String expectedOutput) {
lberkiaea56b32017-05-30 12:35:33 +0200450 assertThat(result.getTerminationStatus().success()).isTrue();
451 assertThat(result.getStderr()).isEmpty();
452 assertThat(new String(result.getStdout())).isEqualTo(expectedOutput);
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000453 }
Dmitry Lomov8efc3ef2016-02-17 09:50:03 +0000454
455 @Test
456 public void testRelativePath() throws Exception {
457 Command command = new Command(new String[]{"relative/path/to/binary"},
458 ImmutableMap.<String, String>of(),
459 new File("/working/directory"));
460 assertThat(command.getCommandLineElements()[0])
461 .isEqualTo("/working/directory/relative/path/to/binary");
462 }
Han-Wen Nienhuysb0e387b2015-02-12 13:27:06 +0000463}