Add tests for the Command class (#34)

Those test are integrated into Bazel for now, hopefully I can migrate the whole repo to Bazel
at some point (and then we need e4b to develop for e4b).
diff --git a/com.google.devtools.bazel.e4b/BUILD b/com.google.devtools.bazel.e4b/BUILD
index 6952a32..78ccfa5 100644
--- a/com.google.devtools.bazel.e4b/BUILD
+++ b/com.google.devtools.bazel.e4b/BUILD
@@ -9,7 +9,7 @@
     vendor = "The Bazel Authors",
     activator = "com.google.devtools.bazel.e4b.Activator",
     deps = [
+        "//com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command",
         "@com_google_guava//jar",
-        "@org_json//jar",
     ],
 )
diff --git a/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/BUILD b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/BUILD
new file mode 100644
index 0000000..8d9561c
--- /dev/null
+++ b/com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command/BUILD
@@ -0,0 +1,12 @@
+java_library(
+    name = "command",
+    srcs = glob(["*.java"]),
+    visibility = [
+        "//com.google.devtools.bazel.e4b:__pkg__",
+        "//javatests/com/google/devtools/bazel/e4b/command:__pkg__",
+    ],
+    deps = [
+        "@com_google_guava//jar",
+        "@org_json//jar",
+    ],
+)
diff --git a/javatests/com/google/devtools/bazel/e4b/command/BUILD b/javatests/com/google/devtools/bazel/e4b/command/BUILD
new file mode 100644
index 0000000..47834db
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/command/BUILD
@@ -0,0 +1,10 @@
+java_test(
+    name = "CommandTest",
+    srcs = ["CommandTest.java"],
+    deps = [
+        "//com.google.devtools.bazel.e4b/src/com/google/devtools/bazel/e4b/command",
+        "@com_google_truth//jar",
+        "@org_hamcrest_core//jar",
+        "@org_junit//jar",
+    ],
+)
diff --git a/javatests/com/google/devtools/bazel/e4b/command/CommandTest.java b/javatests/com/google/devtools/bazel/e4b/command/CommandTest.java
new file mode 100644
index 0000000..247933c
--- /dev/null
+++ b/javatests/com/google/devtools/bazel/e4b/command/CommandTest.java
@@ -0,0 +1,145 @@
+package com.google.devtools.bazel.e4b.command;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.bazel.e4b.command.CommandConsole.CommandConsoleFactory;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Function;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/** @{link Command}Test */
+public class CommandTest {
+
+  private static Function<String, String> NON_EMPTY_LINES_SELECTOR =
+      (x) -> x.trim().isEmpty() ? null : x;
+
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  private class MockCommandConsole implements CommandConsole {
+    final ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+    final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+    final String name;
+    final String title;
+
+    public MockCommandConsole(String name, String title) {
+      this.title = title;
+      this.name = name;
+    }
+
+    @Override
+    public OutputStream createOutputStream() {
+      return stdout;
+    }
+
+    @Override
+    public OutputStream createErrorStream() {
+      return stderr;
+    }
+  }
+
+  private class MockConsoleFactory implements CommandConsoleFactory {
+    final List<MockCommandConsole> consoles = new LinkedList<>();
+
+    @Override
+    public CommandConsole get(String name, String title) throws IOException {
+      MockCommandConsole console = new MockCommandConsole(name, title);
+      consoles.add(console);
+      return console;
+    }
+  }
+
+  public MockConsoleFactory mockConsoleFactory;
+
+  @Before
+  public void setup() {
+    mockConsoleFactory = new MockConsoleFactory();
+  }
+
+  @Test
+  public void testCommandWithStream() throws IOException, InterruptedException {
+    ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+    ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+    Function<String, String> stdoutSelector =
+        (x) -> (x.trim().isEmpty() || x.equals("a")) ? null : x;
+    Function<String, String> stderrSelector =
+        (x) -> (x.trim().isEmpty() || x.equals("b")) ? null : x;
+
+    Command.Builder builder = Command.builder(mockConsoleFactory)
+        .setConsoleName("test")
+        .setDirectory(folder.getRoot())
+        .setStandardError(stderr)
+        .setStandardOutput(stdout)
+        .setStderrLineSelector(stderrSelector)
+        .setStdoutLineSelector(stdoutSelector);
+    builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2");
+    Command cmd = builder.build();
+    assertThat(cmd.run()).isEqualTo(0);
+    String stdoutStr = new String(stdout.toByteArray(), StandardCharsets.UTF_8).trim();
+    String stderrStr = new String(stderr.toByteArray(), StandardCharsets.UTF_8).trim();
+    
+    assertThat(stdoutStr).isEqualTo("a");
+    assertThat(stderrStr).isEqualTo("b");
+    assertThat(cmd.getSelectedErrorLines()).containsExactly("a");
+    assertThat(cmd.getSelectedOutputLines()).containsExactly("b");
+    assertThat(mockConsoleFactory.consoles).hasSize(1);
+    MockCommandConsole console = mockConsoleFactory.consoles.get(0);
+    assertThat(console.name).isEqualTo("test");
+    assertThat(console.title).isEqualTo(
+        "Running bash -c echo a; echo b; echo a >&2; echo b >&2 " + "from " + folder.getRoot());
+    stdoutStr = new String(console.stdout.toByteArray(), StandardCharsets.UTF_8).trim();
+    stderrStr = new String(console.stderr.toByteArray(), StandardCharsets.UTF_8).trim();
+    assertThat(stdoutStr).isEmpty();
+    assertThat(stderrStr).isEmpty();
+  }
+
+  @Test
+  public void testCommandNoStream() throws IOException, InterruptedException {
+    Command.Builder builder =
+        Command.builder(mockConsoleFactory).setConsoleName(null).setDirectory(folder.getRoot());
+    builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2");
+    builder.setStderrLineSelector(NON_EMPTY_LINES_SELECTOR)
+        .setStdoutLineSelector(NON_EMPTY_LINES_SELECTOR);
+    Command cmd = builder.build();
+    assertThat(cmd.run()).isEqualTo(0);
+    assertThat(cmd.getSelectedErrorLines()).containsExactly("a", "b");
+    assertThat(cmd.getSelectedOutputLines()).containsExactly("a", "b");
+  }
+
+  @Test
+  public void testCommandStreamAllToConsole() throws IOException, InterruptedException {
+    Command.Builder builder =
+        Command.builder(mockConsoleFactory).setConsoleName("test").setDirectory(folder.getRoot());
+    builder.addArguments("bash", "-c", "echo a; echo b; echo a >&2; echo b >&2");
+    Command cmd = builder.build();
+    assertThat(cmd.run()).isEqualTo(0);
+    MockCommandConsole console = mockConsoleFactory.consoles.get(0);
+    assertThat(console.name).isEqualTo("test");
+    assertThat(console.title).isEqualTo(
+        "Running bash -c echo a; echo b; echo a >&2; echo b >&2 " + "from " + folder.getRoot());
+    String stdoutStr = new String(console.stdout.toByteArray(), StandardCharsets.UTF_8).trim();
+    String stderrStr = new String(console.stderr.toByteArray(), StandardCharsets.UTF_8).trim();
+    assertThat(stdoutStr).isEqualTo("a\nb");
+    assertThat(stderrStr).isEqualTo("a\nb");
+  }
+
+  @Test
+  public void testCommandWorkDir() throws IOException, InterruptedException {
+    Command.Builder builder =
+        Command.builder(mockConsoleFactory).setConsoleName(null).setDirectory(folder.getRoot());
+    builder.setStderrLineSelector(NON_EMPTY_LINES_SELECTOR)
+        .setStdoutLineSelector(NON_EMPTY_LINES_SELECTOR);
+    builder.addArguments("pwd");
+    Command cmd = builder.build();
+    assertThat(cmd.run()).isEqualTo(0);
+    assertThat(cmd.getSelectedOutputLines()).containsExactly(folder.getRoot().getCanonicalPath());
+  }
+}