| // Copyright 2015 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.devtools.build.lib.shell; | |
| import static com.google.common.truth.Truth.assertThat; | |
| import static com.google.devtools.build.lib.shell.TestUtil.assertArrayEquals; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertFalse; | |
| import static org.junit.Assert.assertTrue; | |
| import static org.junit.Assert.fail; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.devtools.build.lib.testutil.BlazeTestUtils; | |
| import com.google.devtools.build.lib.testutil.TestConstants; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.File; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.OutputStream; | |
| import java.util.Collections; | |
| import java.util.Map; | |
| import java.util.logging.Level; | |
| import java.util.logging.Logger; | |
| /** | |
| * Unit tests for {@link Command}. This test will only succeed on Linux, | |
| * currently, because of its non-portable nature. | |
| */ | |
| @RunWith(JUnit4.class) | |
| public class CommandTest { | |
| private static final long LONG_TIME = 10000; | |
| private static final long SHORT_TIME = 250; | |
| // Platform-independent tests ---------------------------------------------- | |
| @Before | |
| public final void configureLogger() throws Exception { | |
| // enable all log statements to ensure there are no problems with | |
| // logging code | |
| Logger.getLogger("com.google.devtools.build.lib.shell.Command").setLevel(Level.FINEST); | |
| } | |
| @Test | |
| public void testIllegalArgs() throws Exception { | |
| try { | |
| new Command((String[]) null); | |
| fail("Should have thrown IllegalArgumentException"); | |
| } catch (IllegalArgumentException iae) { | |
| // good | |
| } | |
| try { | |
| new Command(new String[] {"/bin/true", null}).execute(); | |
| fail("Should have thrown NullPointerException"); | |
| } catch (NullPointerException npe) { | |
| // good | |
| } | |
| try { | |
| new Command(new String[] {"foo"}).execute(null); | |
| fail("Should have thrown NullPointerException"); | |
| } catch (NullPointerException npe) { | |
| // good | |
| } | |
| } | |
| @Test | |
| public void testGetters() { | |
| final File workingDir = new File("."); | |
| final Map<String,String> env = Collections.singletonMap("foo", "bar"); | |
| final String[] commandArgs = new String[] { "command" }; | |
| final Command command = new Command(commandArgs, env, workingDir); | |
| assertArrayEquals(commandArgs, command.getCommandLineElements()); | |
| for (final String key : env.keySet()) { | |
| assertThat(command.getEnvironmentVariables()).containsEntry(key, env.get(key)); | |
| } | |
| assertEquals(workingDir, command.getWorkingDirectory()); | |
| } | |
| // Platform-dependent tests ------------------------------------------------ | |
| @Test | |
| public void testSimpleCommand() throws Exception { | |
| final Command command = new Command(new String[] {"ls"}); | |
| final CommandResult result = command.execute(); | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertEquals(0, result.getStderr().length); | |
| assertThat(result.getStdout().length).isGreaterThan(0); | |
| } | |
| @Test | |
| public void testArguments() throws Exception { | |
| final Command command = new Command(new String[] {"echo", "foo"}); | |
| checkSuccess(command.execute(), "foo\n"); | |
| } | |
| @Test | |
| public void testEnvironment() throws Exception { | |
| final Map<String,String> env = Collections.singletonMap("FOO", "BAR"); | |
| final Command command = new Command(new String[] {"/bin/sh", "-c", "echo $FOO"}, env, | |
| null); | |
| checkSuccess(command.execute(), "BAR\n"); | |
| } | |
| @Test | |
| public void testWorkingDir() throws Exception { | |
| final Command command = new Command(new String[] {"pwd"}, null, new File("/")); | |
| checkSuccess(command.execute(), "/\n"); | |
| } | |
| @Test | |
| public void testStdin() throws Exception { | |
| final Command command = new Command(new String[] {"grep", "bar"}); | |
| checkSuccess(command.execute("foobarbaz".getBytes()), "foobarbaz\n"); | |
| } | |
| @Test | |
| public void testRawCommand() throws Exception { | |
| final Command command = new Command(new String[] { "perl", | |
| "-e", | |
| "print 'a'x100000" }); | |
| final CommandResult result = command.execute(); | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertEquals(0, result.getStderr().length); | |
| assertThat(result.getStdout().length).isGreaterThan(0); | |
| } | |
| @Test | |
| public void testRawCommandWithDir() throws Exception { | |
| final Command command = new Command(new String[] { "pwd" }, | |
| null, | |
| new File("/")); | |
| final CommandResult result = command.execute(); | |
| checkSuccess(result, "/\n"); | |
| } | |
| @Test | |
| public void testHugeOutput() throws Exception { | |
| final Command command = new Command(new String[] {"perl", "-e", "print 'a'x100000"}); | |
| final CommandResult result = command.execute(); | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertEquals(0, result.getStderr().length); | |
| assertEquals(100000, result.getStdout().length); | |
| } | |
| @Test | |
| public void testIgnoreOutput() throws Exception { | |
| final Command command = new Command(new String[] {"perl", "-e", "print 'a'x100000"}); | |
| final CommandResult result = command.execute(Command.NO_INPUT, null, true); | |
| assertTrue(result.getTerminationStatus().success()); | |
| try { | |
| result.getStdout(); | |
| fail("Should have thrown IllegalStateException"); | |
| } catch (IllegalStateException ise) { | |
| // good | |
| } | |
| try { | |
| result.getStderr(); | |
| fail("Should have thrown IllegalStateException"); | |
| } catch (IllegalStateException ise) { | |
| // good | |
| } | |
| } | |
| @Test | |
| public void testNoStreamingInputForCat() throws Exception { | |
| final Command command = new Command(new String[]{"/bin/cat"}); | |
| ByteArrayInputStream emptyInput = new ByteArrayInputStream(new byte[0]); | |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
| ByteArrayOutputStream err = new ByteArrayOutputStream(); | |
| CommandResult result = command.execute(emptyInput, | |
| Command.NO_OBSERVER, out, err); | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertThat(out.toString("UTF-8")).isEmpty(); | |
| assertThat(err.toString("UTF-8")).isEmpty(); | |
| } | |
| @Test | |
| public void testNoInputForCat() throws Exception { | |
| final Command command = new Command(new String[]{"/bin/cat"}); | |
| CommandResult result = command.execute(); | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertThat(new String(result.getStdout(), "UTF-8")).isEmpty(); | |
| assertThat(new String(result.getStderr(), "UTF-8")).isEmpty(); | |
| } | |
| @Test | |
| public void testProvidedOutputStreamCapturesHelloWorld() throws Exception { | |
| String helloWorld = "Hello, world."; | |
| final Command command = new Command(new String[]{"/bin/echo", helloWorld}); | |
| ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); | |
| ByteArrayOutputStream stdErr = new ByteArrayOutputStream(); | |
| command.execute(Command.NO_INPUT, Command.NO_OBSERVER, stdOut, stdErr); | |
| assertEquals(helloWorld + "\n", stdOut.toString("UTF-8")); | |
| assertEquals(0, stdErr.toByteArray().length); | |
| } | |
| @Test | |
| public void testAsynchronous() throws Exception { | |
| final File tempFile = File.createTempFile("googlecron-test", "tmp"); | |
| tempFile.delete(); | |
| final Command command = new Command(new String[] {"touch", tempFile.getAbsolutePath()}); | |
| // Shouldn't throw any exceptions: | |
| FutureCommandResult result = | |
| command.executeAsynchronously(Command.NO_INPUT); | |
| result.get(); | |
| assertTrue(tempFile.exists()); | |
| assertTrue(result.isDone()); | |
| tempFile.delete(); | |
| } | |
| @Test | |
| public void testAsynchronousWithKillable() throws Exception { | |
| final Command command = new Command(new String[] {"sleep", "5"}); | |
| final SimpleKillableObserver observer = new SimpleKillableObserver(); | |
| FutureCommandResult result = | |
| command.executeAsynchronously(Command.NO_INPUT, observer); | |
| assertFalse(result.isDone()); | |
| observer.kill(); | |
| try { | |
| result.get(); | |
| } catch (AbnormalTerminationException e) { | |
| // Expects, but does not insist on termination with a signal. | |
| } | |
| assertTrue(result.isDone()); | |
| } | |
| @Test | |
| public void testAsynchronousWithOutputStreams() throws Exception { | |
| final String helloWorld = "Hello, world."; | |
| final Command command = new Command(new String[]{"/bin/echo", helloWorld}); | |
| final ByteArrayInputStream emptyInput = | |
| new ByteArrayInputStream(new byte[0]); | |
| final ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); | |
| final ByteArrayOutputStream stdErr = new ByteArrayOutputStream(); | |
| FutureCommandResult result = command.executeAsynchronously(emptyInput, | |
| Command.NO_OBSERVER, | |
| stdOut, | |
| stdErr); | |
| result.get(); // Make sure the process actually finished | |
| assertEquals(helloWorld + "\n", stdOut.toString("UTF-8")); | |
| assertEquals(0, stdErr.toByteArray().length); | |
| } | |
| @Test | |
| public void testSimpleKillableObserver() throws Exception { | |
| final Command command = new Command(new String[] {"sleep", "5"}); | |
| final SimpleKillableObserver observer = new SimpleKillableObserver(); | |
| new Thread() { | |
| @Override | |
| public void run() { | |
| try { | |
| command.execute(Command.NO_INPUT, observer, true); | |
| fail(); | |
| } catch (CommandException e) { | |
| // Good. | |
| checkCommandElements(e, "sleep", "5"); | |
| } | |
| } | |
| }.start(); | |
| // We're racing against the actual startup of the other command. Wait for 10ms so it can start. | |
| Thread.sleep(10); | |
| observer.kill(); | |
| } | |
| @Test | |
| public void testTimeout() throws Exception { | |
| // Sleep for 3 seconds, | |
| final Command command = new Command(new String[] {"sleep", "3"}); | |
| try { | |
| // but timeout after 1 second | |
| command.execute(Command.NO_INPUT, 1000L, false); | |
| fail("Should have thrown AbnormalTerminationException"); | |
| } catch (AbnormalTerminationException ate) { | |
| // good | |
| checkCommandElements(ate, "sleep", "3"); | |
| checkATE(ate); | |
| } | |
| } | |
| @Test | |
| public void testTimeoutDoesntFire() throws Exception { | |
| final Command command = new Command(new String[] {"cat"}); | |
| command.execute(new byte[]{'H', 'i', '!'}, 2000L, false); | |
| } | |
| @Test | |
| public void testCommandDoesNotExist() throws Exception { | |
| final Command command = new Command(new String[]{"thisisnotreal"}); | |
| try { | |
| command.execute(); | |
| fail(); | |
| } catch (ExecFailedException e){ | |
| // Good. | |
| checkCommandElements(e, "thisisnotreal"); | |
| } | |
| } | |
| @Test | |
| public void testNoSuchCommand() throws Exception { | |
| final Command command = new Command(new String[] {"thisisnotreal"}); | |
| try { | |
| command.execute(); | |
| fail("Should have thrown ExecFailedException"); | |
| } catch (ExecFailedException expected) { | |
| // good | |
| } | |
| } | |
| @Test | |
| public void testExitCodes() throws Exception { | |
| // 0 => success | |
| { | |
| String args[] = { "/bin/sh", "-c", "exit 0" }; | |
| CommandResult result = new Command(args).execute(); | |
| TerminationStatus status = result.getTerminationStatus(); | |
| assertTrue(status.success()); | |
| assertTrue(status.exited()); | |
| assertEquals(0, status.getExitCode()); | |
| } | |
| // Every exit value in range [1-255] is reported as such (except [129-191], | |
| // which map to signals). | |
| for (int exit : new int[] { 1, 2, 3, 127, 128, 192, 255 }) { | |
| try { | |
| String args[] = { "/bin/sh", "-c", "exit " + exit }; | |
| new Command(args).execute(); | |
| fail("Should have exited with status " + exit); | |
| } catch (BadExitStatusException e) { | |
| assertThat(e).hasMessage("Process exited with status " + exit); | |
| checkCommandElements(e, "/bin/sh", "-c", "exit " + exit); | |
| TerminationStatus status = e.getResult().getTerminationStatus(); | |
| assertFalse(status.success()); | |
| assertTrue(status.exited()); | |
| assertEquals(exit, status.getExitCode()); | |
| assertEquals("Exit " + exit , status.toShortString()); | |
| } | |
| } | |
| // negative exit values are modulo 256: | |
| for (int exit : new int[] { -1, -2, -3 }) { | |
| int expected = 256 + exit; | |
| try { | |
| String args[] = { "/bin/bash", "-c", "exit " + exit }; | |
| new Command(args).execute(); | |
| fail("Should have exited with status " + expected); | |
| } catch (BadExitStatusException e) { | |
| assertThat(e).hasMessage("Process exited with status " + expected); | |
| checkCommandElements(e, "/bin/bash", "-c", "exit " + exit); | |
| TerminationStatus status = e.getResult().getTerminationStatus(); | |
| assertFalse(status.success()); | |
| assertTrue(status.exited()); | |
| assertEquals(expected, status.getExitCode()); | |
| assertEquals("Exit " + expected, status.toShortString()); | |
| } | |
| } | |
| } | |
| @Test | |
| public void testFailedWithSignal() throws Exception { | |
| // SIGHUP, SIGINT, SIGKILL, SIGTERM | |
| for (int signal : new int[] { 1, 2, 9, 15 }) { | |
| // Invoke a C++ program (killmyself.cc) that will die | |
| // with the specified signal. | |
| String killmyself = BlazeTestUtils.runfilesDir() + "/" | |
| + TestConstants.JAVATESTS_ROOT | |
| + "/com/google/devtools/build/lib/shell/killmyself"; | |
| try { | |
| String args[] = { killmyself, "" + signal }; | |
| new Command(args).execute(); | |
| fail("Expected signal " + signal); | |
| } catch (AbnormalTerminationException e) { | |
| assertThat(e).hasMessage("Process terminated by signal " + signal); | |
| checkCommandElements(e, killmyself, "" + signal); | |
| TerminationStatus status = e.getResult().getTerminationStatus(); | |
| assertFalse(status.success()); | |
| assertFalse(status.exited()); | |
| assertEquals(signal, status.getTerminatingSignal()); | |
| switch (signal) { | |
| case 1: assertEquals("Hangup", status.toShortString()); break; | |
| case 2: assertEquals("Interrupt", status.toShortString()); break; | |
| case 9: assertEquals("Killed", status.toShortString()); break; | |
| case 15: assertEquals("Terminated", status.toShortString()); break; | |
| } | |
| } | |
| } | |
| } | |
| @Test | |
| public void testDestroy() throws Exception { | |
| // Sleep for 10 seconds, | |
| final Command command = new Command(new String[] {"sleep", "10"}); | |
| // but kill it after 1 | |
| final KillableObserver killer = new KillableObserver() { | |
| @Override | |
| public void startObserving(final Killable killable) { | |
| final Thread t = new Thread() { | |
| @Override | |
| public void run() { | |
| try { | |
| Thread.sleep(1000L); | |
| } catch (InterruptedException ie) { | |
| // continue | |
| } | |
| killable.kill(); | |
| } | |
| }; | |
| t.start(); | |
| } | |
| @Override | |
| public void stopObserving(final Killable killable) { | |
| // do nothing | |
| } | |
| }; | |
| try { | |
| command.execute(Command.NO_INPUT, killer, false); | |
| fail("Should have thrown AbnormalTerminationException"); | |
| } catch (AbnormalTerminationException ate) { | |
| // Good. | |
| checkCommandElements(ate, "sleep", "10"); | |
| checkATE(ate); | |
| } | |
| } | |
| @Test | |
| public void testOnlyReadsPartialInput() throws Exception { | |
| // -c == --bytes, but -c also works on Darwin. | |
| Command command = new Command(new String[] {"head", "-c", "500"}); | |
| OutputStream out = new ByteArrayOutputStream(); | |
| InputStream in = new InputStream() { | |
| @Override | |
| public int read() { | |
| return 0; // write an unbounded amount | |
| } | |
| }; | |
| CommandResult result = command.execute(in, Command.NO_OBSERVER, out, out); | |
| TerminationStatus status = result.getTerminationStatus(); | |
| assertTrue(status.success()); | |
| } | |
| @Test | |
| public void testFlushing() throws Exception { | |
| final Command command = new Command( | |
| // On darwin, /bin/sh does not support -n for the echo builtin. | |
| new String[] {"/bin/bash", "-c", "echo -n Foo; sleep 0.1; echo Bar"}); | |
| // We run this command, passing in a special output stream | |
| // that records when each flush() occurs. | |
| // We test that a flush occurs after writing "Foo" | |
| // and that another flush occurs after writing "Bar\n". | |
| final boolean[] flushed = new boolean[8]; | |
| OutputStream out = new OutputStream() { | |
| private int count = 0; | |
| @Override | |
| public void write(int b) throws IOException { | |
| count++; | |
| } | |
| @Override | |
| public void flush() throws IOException { | |
| flushed[count] = true; | |
| } | |
| }; | |
| command.execute(Command.NO_INPUT, Command.NO_OBSERVER, out, System.err); | |
| assertFalse(flushed[0]); | |
| assertFalse(flushed[1]); // 'F' | |
| assertFalse(flushed[2]); // 'o' | |
| assertTrue(flushed[3]); // 'o' <- expect flush here. | |
| assertFalse(flushed[4]); // 'B' | |
| assertFalse(flushed[5]); // 'a' | |
| assertFalse(flushed[6]); // 'r' | |
| assertTrue(flushed[7]); // '\n' | |
| } | |
| // See also InterruptibleTest. | |
| @Test | |
| public void testInterrupt() throws Exception { | |
| // Sleep for 10 seconds, | |
| final Command command = new Command(new String[] {"sleep", "10"}); | |
| // Easy but hacky way to let this thread "return" a result to this method | |
| final CommandResult[] resultContainer = new CommandResult[1]; | |
| final Thread commandThread = new Thread() { | |
| @Override | |
| public void run() { | |
| try { | |
| resultContainer[0] = command.execute(); | |
| } catch (CommandException ce) { | |
| fail(ce.toString()); | |
| } | |
| } | |
| }; | |
| commandThread.start(); | |
| Thread.sleep(1000L); | |
| // but interrupt it after 1 | |
| commandThread.interrupt(); | |
| // should continue to wait and exit normally | |
| commandThread.join(); | |
| final CommandResult result = resultContainer[0]; | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertEquals(0, result.getStderr().length); | |
| assertEquals(0, result.getStdout().length); | |
| } | |
| @Test | |
| public void testOutputStreamThrowsException() throws Exception { | |
| OutputStream out = new OutputStream () { | |
| @Override | |
| public void write(int b) throws IOException { | |
| throw new IOException(); | |
| } | |
| }; | |
| Command command = new Command(new String[] {"/bin/echo", "foo"}); | |
| try { | |
| command.execute(Command.NO_INPUT, Command.NO_OBSERVER, out, out); | |
| fail(); | |
| } catch (AbnormalTerminationException e) { | |
| // Good. | |
| checkCommandElements(e, "/bin/echo", "foo"); | |
| assertThat(e).hasMessage("java.io.IOException"); | |
| } | |
| } | |
| @Test | |
| public void testOutputStreamThrowsExceptionAndCommandFails() | |
| throws Exception { | |
| OutputStream out = new OutputStream () { | |
| @Override | |
| public void write(int b) throws IOException { | |
| throw new IOException(); | |
| } | |
| }; | |
| Command command = new Command(new String[] {"cat", "/dev/thisisnotreal"}); | |
| try { | |
| command.execute(Command.NO_INPUT, Command.NO_OBSERVER, out, out); | |
| fail(); | |
| } catch (AbnormalTerminationException e) { | |
| checkCommandElements(e, "cat", "/dev/thisisnotreal"); | |
| TerminationStatus status = e.getResult().getTerminationStatus(); | |
| // Subprocess either gets a SIGPIPE trying to write to our output stream, | |
| // or it exits with failure. Both are observed, nondetermistically. | |
| assertTrue(status.exited() | |
| ? status.getExitCode() == 1 | |
| : status.getTerminatingSignal() == 13); | |
| assertTrue(e.getMessage(), | |
| e.getMessage().endsWith("also encountered an error while attempting " | |
| + "to retrieve output")); | |
| } | |
| } | |
| /** | |
| * Helper to test KillableObserver classes. | |
| */ | |
| private static class KillableTester implements Killable { | |
| private boolean isKilled = false; | |
| private boolean timedOut = false; | |
| @Override | |
| public synchronized void kill() { | |
| isKilled = true; | |
| notifyAll(); | |
| } | |
| public synchronized boolean getIsKilled() { | |
| return isKilled; | |
| } | |
| /** | |
| * Wait for a specified time or until the {@link #kill()} is called. | |
| */ | |
| public synchronized void sleepUntilKilled(final long timeoutMS) { | |
| long nowTime = System.currentTimeMillis(); | |
| long endTime = nowTime + timeoutMS; | |
| while (!isKilled && !timedOut) { | |
| long waitTime = endTime - nowTime; | |
| if (waitTime <= 0) { | |
| // Process has timed out, needs killing. | |
| timedOut = true; | |
| break; | |
| } | |
| try { | |
| wait(waitTime); // Suffers "spurious wakeup", hence the while() loop. | |
| nowTime = System.currentTimeMillis(); | |
| } catch (InterruptedException exception) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| @Test | |
| public void testTimeOutKillableObserverNoKill() throws Exception { | |
| KillableTester killable = new KillableTester(); | |
| TimeoutKillableObserver observer = new TimeoutKillableObserver(LONG_TIME); | |
| observer.startObserving(killable); | |
| observer.stopObserving(killable); | |
| assertFalse(observer.hasTimedOut()); | |
| assertFalse(killable.getIsKilled()); | |
| } | |
| @Test | |
| public void testTimeOutKillableObserverNoKillWithDelay() throws Exception { | |
| KillableTester killable = new KillableTester(); | |
| TimeoutKillableObserver observer = new TimeoutKillableObserver(LONG_TIME); | |
| observer.startObserving(killable); | |
| killable.sleepUntilKilled(SHORT_TIME); | |
| observer.stopObserving(killable); | |
| assertFalse(observer.hasTimedOut()); | |
| assertFalse(killable.getIsKilled()); | |
| } | |
| @Test | |
| public void testTimeOutKillableObserverWithKill() throws Exception { | |
| KillableTester killable = new KillableTester(); | |
| TimeoutKillableObserver observer = new TimeoutKillableObserver(SHORT_TIME); | |
| observer.startObserving(killable); | |
| killable.sleepUntilKilled(LONG_TIME); | |
| observer.stopObserving(killable); | |
| assertTrue(observer.hasTimedOut()); | |
| assertTrue(killable.getIsKilled()); | |
| } | |
| @Test | |
| public void testTimeOutKillableObserverWithKillZeroMillis() throws Exception { | |
| KillableTester killable = new KillableTester(); | |
| TimeoutKillableObserver observer = new TimeoutKillableObserver(0); | |
| observer.startObserving(killable); | |
| killable.sleepUntilKilled(LONG_TIME); | |
| observer.stopObserving(killable); | |
| assertTrue(observer.hasTimedOut()); | |
| assertTrue(killable.getIsKilled()); | |
| } | |
| private static void checkCommandElements(CommandException e, | |
| String... expected) { | |
| assertArrayEquals(expected, e.getCommand().getCommandLineElements()); | |
| } | |
| private static void checkATE(final AbnormalTerminationException ate) { | |
| final CommandResult result = ate.getResult(); | |
| assertFalse(result.getTerminationStatus().success()); | |
| } | |
| private static void checkSuccess(final CommandResult result, | |
| final String expectedOutput) { | |
| assertTrue(result.getTerminationStatus().success()); | |
| assertEquals(0, result.getStderr().length); | |
| assertEquals(expectedOutput, new String(result.getStdout())); | |
| } | |
| @Test | |
| public void testRelativePath() throws Exception { | |
| Command command = new Command(new String[]{"relative/path/to/binary"}, | |
| ImmutableMap.<String, String>of(), | |
| new File("/working/directory")); | |
| assertThat(command.getCommandLineElements()[0]) | |
| .isEqualTo("/working/directory/relative/path/to/binary"); | |
| } | |
| } |