@Test
  public void nonExistentBinaryThrowsExceptionOnInfiniteWait() throws Exception {
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ListeningProcessExecutor.LaunchedProcess process =
        executor.launchProcess(
            ProcessExecutorParams.ofCommand("this-better-not-be-a-process-on-your-system-for-real"),
            listener);

    exception.expect(IOException.class);
    exception.expectMessage(
        "Failed to start process [this-better-not-be-a-process-on-your-system-for-real]");
    executor.waitForProcess(process);
  }
/** Tests for {@link ListeningProcessExecutor}. */
public class ListeningProcessExecutorTest {

  @Rule public TemporaryPaths tmp = new TemporaryPaths();

  @Rule public ExpectedException exception = ExpectedException.none();

  @Test
  public void echoTextReceivedOnStdout() throws Exception {
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ProcessExecutorParams params;
    if (Platform.detect() == Platform.WINDOWS) {
      params = ProcessExecutorParams.ofCommand("cmd.exe", "/c", "echo", "Hello");
    } else {
      params = ProcessExecutorParams.ofCommand("echo", "Hello");
    }
    ListeningProcessExecutor.LaunchedProcess process = executor.launchProcess(params, listener);
    int returnCode = executor.waitForProcess(process);
    assertThat(returnCode, equalTo(0));
    assertThat(listener.capturedStdout.toString("UTF-8"), equalTo(String.format("Hello%n")));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }

  @Test
  public void processCwdIsRespected() throws Exception {
    ProcessExecutorParams.Builder paramsBuilder = ProcessExecutorParams.builder();
    if (Platform.detect() == Platform.WINDOWS) {
      paramsBuilder.addCommand("cmd.exe", "/c", "type");
    } else {
      paramsBuilder.addCommand("cat");
    }
    paramsBuilder.addCommand("hello-world.txt");
    paramsBuilder.setDirectory(tmp.getRoot());
    Path helloWorldPath = tmp.getRoot().resolve("hello-world.txt");
    String fileContents = "Hello, world!";
    Files.write(helloWorldPath, fileContents.getBytes(StandardCharsets.UTF_8));
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ListeningProcessExecutor.LaunchedProcess process =
        executor.launchProcess(paramsBuilder.build(), listener);
    int returnCode = executor.waitForProcess(process);
    assertThat(returnCode, equalTo(0));
    assertThat(listener.capturedStdout.toString("UTF-8"), equalTo(fileContents));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }

  @Test
  public void catTextSentToStdinReceivedOnStdout() throws Exception {
    ProcessExecutorParams params;
    if (Platform.detect() == Platform.WINDOWS) {
      params =
          ProcessExecutorParams.ofCommand(
              "python", "-c", "import sys, shutil; shutil.copyfileobj(sys.stdin, sys.stdout)");
    } else {
      params = ProcessExecutorParams.ofCommand("cat");
    }
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    StdinWritingListener listener = new StdinWritingListener(String.format("Meow%n"));
    ListeningProcessExecutor.LaunchedProcess process = executor.launchProcess(params, listener);
    process.wantWrite();
    int returnCode = executor.waitForProcess(process);
    assertThat(returnCode, equalTo(0));
    assertThat(listener.capturedStdout.toString("UTF-8"), equalTo(String.format("Meow%n")));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }

  @Test
  public void catMoreTextThanFitsInSingleBufferReceivedOnStdout() throws Exception {
    ProcessExecutorParams params;
    if (Platform.detect() == Platform.WINDOWS) {
      params =
          ProcessExecutorParams.ofCommand(
              "python", "-c", "import sys, shutil; shutil.copyfileobj(sys.stdin, sys.stdout)");
    } else {
      params = ProcessExecutorParams.ofCommand("cat");
    }
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    StringBuilder sb = new StringBuilder();
    // Use a 3 byte Unicode sequence to ensure writes go across byte buffer
    // boundaries, and append it as many times as needed to ensure it doesn't
    // fit in a single I/O buffer.
    String threeByteUTF8 = "\u2764";
    for (int i = 0; i < ListeningProcessExecutor.LaunchedProcess.BUFFER_CAPACITY + 1; i++) {
      sb.append(threeByteUTF8);
    }
    sb.append(String.format("%n"));
    String longString = sb.toString();
    StdinWritingListener listener = new StdinWritingListener(longString);
    ListeningProcessExecutor.LaunchedProcess process = executor.launchProcess(params, listener);
    process.wantWrite();
    int returnCode = executor.waitForProcess(process);
    assertThat(returnCode, equalTo(0));
    assertThat(listener.capturedStdout.toString("UTF-8"), equalTo(longString));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }

  @Test
  public void processFailureExitCodeNotZero() throws Exception {
    ProcessExecutorParams params;
    if (Platform.detect() == Platform.WINDOWS) {
      params = ProcessExecutorParams.ofCommand("cmd.exe", "/c", "exit", "1");
    } else {
      params = ProcessExecutorParams.ofCommand("false");
    }
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ListeningProcessExecutor.LaunchedProcess process = executor.launchProcess(params, listener);
    int returnCode = executor.waitForProcess(process);
    assertThat(returnCode, not(equalTo(0)));
    assertThat(listener.capturedStdout.toString("UTF-8"), is(emptyString()));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }

  @Test
  public void nonExistentBinaryExitCodeNotZeroOnLimitedWait() throws Exception {
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ListeningProcessExecutor.LaunchedProcess process =
        executor.launchProcess(
            ProcessExecutorParams.ofCommand("this-better-not-be-a-process-on-your-system-for-real"),
            listener);
    int returnCode = executor.waitForProcess(process, Long.MAX_VALUE, TimeUnit.SECONDS);
    assertThat(returnCode, not(equalTo(0)));
    assertThat(listener.capturedStdout.toString("UTF-8"), is(emptyString()));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }

  @Test
  public void nonExistentBinaryThrowsExceptionOnInfiniteWait() throws Exception {
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ListeningProcessExecutor.LaunchedProcess process =
        executor.launchProcess(
            ProcessExecutorParams.ofCommand("this-better-not-be-a-process-on-your-system-for-real"),
            listener);

    exception.expect(IOException.class);
    exception.expectMessage(
        "Failed to start process [this-better-not-be-a-process-on-your-system-for-real]");
    executor.waitForProcess(process);
  }

  @Test
  public void waitForProcessReturnsMinIntegerOnTimeout() throws Exception {
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ProcessExecutorParams params;
    if (Platform.detect() == Platform.WINDOWS) {
      params = ProcessExecutorParams.ofCommand("python", "-c", "import time; time.sleep(50)");
    } else {
      params = ProcessExecutorParams.ofCommand("sleep", "50");
    }
    ListeningProcessExecutor.LaunchedProcess process = executor.launchProcess(params, listener);
    int returnCode = executor.waitForProcess(process, 100, TimeUnit.MILLISECONDS);
    assertThat(returnCode, equalTo(Integer.MIN_VALUE));
    assertThat(listener.capturedStdout.toString("UTF-8"), is(emptyString()));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
    executor.destroyProcess(process, /* force */ true);
    executor.waitForProcess(process);
  }

  @Test
  public void clearsEnvWhenExplicitlySet() throws Exception {
    ListeningProcessExecutor executor = new ListeningProcessExecutor();
    CapturingListener listener = new CapturingListener();
    ProcessExecutorParams params;
    if (Platform.detect() == Platform.WINDOWS) {
      params = ProcessExecutorParams.ofCommand("cmd.exe", "/c", "set");
    } else {
      params = ProcessExecutorParams.ofCommand("env");
    }
    params = params.withEnvironment(new HashMap<String, String>());
    ListeningProcessExecutor.LaunchedProcess process = executor.launchProcess(params, listener);
    int returnCode = executor.waitForProcess(process);
    assertThat(returnCode, equalTo(0));
    assertThat(listener.capturedStdout.toString("UTF-8"), is(emptyString()));
    assertThat(listener.capturedStderr.toString("UTF-8"), is(emptyString()));
  }
}