geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kl...@apache.org
Subject [16/40] geode git commit: GEODE-3060: Introduce JUnit rule for testing the fully-assembled GFSH
Date Thu, 15 Jun 2017 21:54:49 GMT
GEODE-3060: Introduce JUnit rule for testing the fully-assembled GFSH


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/d9869ffd
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/d9869ffd
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/d9869ffd

Branch: refs/heads/feature/GEODE-2980
Commit: d9869ffd5b317c9e577f3ee2107a8d4bf46a166d
Parents: b616e80
Author: Jared Stewart <jstewart@pivotal.io>
Authored: Wed Jun 7 20:44:56 2017 -0700
Committer: Jared Stewart <jstewart@pivotal.io>
Committed: Wed Jun 14 11:18:36 2017 -0700

----------------------------------------------------------------------
 .../cli/commands/StatusLocatorRealGfshTest.java |  50 ++++++++
 .../geode/test/dunit/rules/gfsh/GfshRule.java   | 116 +++++++++++++++++
 .../geode/test/dunit/rules/gfsh/GfshScript.java | 124 +++++++++++++++++++
 3 files changed, 290 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/d9869ffd/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
new file mode 100644
index 0000000..82ee240
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StatusLocatorRealGfshTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.management.internal.cli.commands;
+
+import org.apache.geode.test.dunit.rules.gfsh.GfshRule;
+import org.apache.geode.test.dunit.rules.gfsh.GfshScript;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+@Category(DistributedTest.class)
+public class StatusLocatorRealGfshTest {
+  @Rule
+  public GfshRule gfshRule = new GfshRule();
+
+  @Test
+  public void statusLocatorSucceedsWhenConnected() throws Exception {
+    gfshRule.execute(GfshScript.of("start locator --name=locator1").awaitAtMost(1, TimeUnit.MINUTES)
+        .expectExitCode(0));
+
+    gfshRule.execute(GfshScript.of("connect", "status locator --name=locator1")
+        .awaitAtMost(1, TimeUnit.MINUTES).expectExitCode(0));
+  }
+
+  @Test
+  public void statusLocatorFailsWhenNotConnected() throws Exception {
+    gfshRule.execute(GfshScript.of("start locator --name=locator1").awaitAtMost(1, TimeUnit.MINUTES)
+        .expectExitCode(0));
+
+    gfshRule.execute(GfshScript.of("status locator --name=locator1")
+        .awaitAtMost(1, TimeUnit.MINUTES).expectExitCode(1));
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9869ffd/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
new file mode 100644
index 0000000..8109377
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshRule.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.test.dunit.rules.gfsh;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+import org.junit.rules.ExternalResource;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.management.internal.cli.commands.StatusLocatorRealGfshTest;
+import org.apache.geode.test.dunit.rules.RequiresGeodeHome;
+
+/**
+ * The {@code GfshRule} allows a test to execute Gfsh commands via the actual (fully-assembled)
gfsh
+ * binaries. For a usage example, see {@link StatusLocatorRealGfshTest}. Each call to
+ * {@link GfshRule#execute(GfshScript)} will invoke the given gfsh script in a forked JVM.
The
+ * {@link GfshRule#after()} method will attempt to clean up all forked JVMs.
+ */
+public class GfshRule extends ExternalResource {
+  private TemporaryFolder temporaryFolder = new TemporaryFolder();
+  private List<Process> processes = new ArrayList<>();
+  private Path gfsh;
+
+  public Process execute(String... commands) {
+    return execute(GfshScript.of(commands));
+  }
+
+  public Process execute(GfshScript gfshScript) {
+    Process process;
+    try {
+      process = gfshScript.toProcessBuilder(gfsh, temporaryFolder.getRoot()).start();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+
+    processes.add(process);
+    gfshScript.awaitIfNecessary(process);
+
+    return process;
+  }
+
+  @Override
+  protected void before() throws IOException {
+    gfsh = new RequiresGeodeHome().getGeodeHome().toPath().resolve("bin/gfsh");
+    assertThat(gfsh).exists();
+
+    temporaryFolder.create();
+  }
+
+  /**
+   * Attempts to stop any started servers/locators via pid file and tears down any remaining
gfsh
+   * JVMs.
+   */
+  @Override
+  protected void after() {
+    stopMembersQuietly();
+    processes.forEach(Process::destroyForcibly);
+    processes.forEach((Process process) -> {
+      try {
+        // Process.destroyForcibly() may not terminate immediately
+        process.waitFor(1, TimeUnit.MINUTES);
+      } catch (InterruptedException ignore) {
+        // We ignore this exception so that we still attempt the rest of the cleanup.
+      }
+    });
+    temporaryFolder.delete();
+  }
+
+  private void stopMembersQuietly() {
+    File[] directories = temporaryFolder.getRoot().listFiles(File::isDirectory);
+
+    Predicate<File> isServerDir = (File directory) -> Arrays.stream(directory.list())
+        .anyMatch(filename -> filename.endsWith("server.pid"));
+
+    Predicate<File> isLocatorDir = (File directory) -> Arrays.stream(directory.list())
+        .anyMatch(filename -> filename.endsWith("locator.pid"));
+
+    Arrays.stream(directories).filter(isServerDir).forEach(this::stopServerInDir);
+    Arrays.stream(directories).filter(isLocatorDir).forEach(this::stopLocatorInDir);
+  }
+
+  private void stopServerInDir(File dir) {
+    GfshScript stopServerScript = new GfshScript("stop server --dir=" + dir.getAbsolutePath())
+        .awaitQuietlyAtMost(1, TimeUnit.MINUTES);
+
+    execute(stopServerScript);
+  }
+
+  private void stopLocatorInDir(File dir) {
+    GfshScript stopServerScript = new GfshScript("stop locator --dir=" + dir.getAbsolutePath())
+        .awaitQuietlyAtMost(1, TimeUnit.MINUTES);
+
+    execute(stopServerScript);
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9869ffd/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
new file mode 100644
index 0000000..3ee1402
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/test/dunit/rules/gfsh/GfshScript.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.test.dunit.rules.gfsh;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
+
+public class GfshScript {
+  private final String[] commands;
+  private Integer timeout;
+  private TimeUnit timeoutTimeUnit;
+  private boolean awaitQuietly = false;
+  private Integer expectedExitValue;
+
+  public GfshScript(String... commands) {
+    this.commands = commands;
+  }
+
+  public static GfshScript of(String... commands) {
+    return new GfshScript(commands);
+  }
+
+
+  public GfshScript expectExitCode(int expectedExitCode) {
+    this.expectedExitValue = expectedExitCode;
+
+    return this;
+  }
+
+  /**
+   * Will cause the thread that executes {@link GfshScript#awaitIfNecessary} to wait, if
necessary,
+   * until the subprocess executing this Gfsh script has terminated, or the specified waiting
time
+   * elapses.
+   * 
+   * @throws RuntimeException if the current thread is interrupted while waiting.
+   * @throws AssertionError if the specified waiting time elapses before the process exits.
+   */
+  public GfshScript awaitAtMost(int timeout, TimeUnit timeUnit) {
+    this.timeout = timeout;
+    this.timeoutTimeUnit = timeUnit;
+
+    return this;
+  }
+
+  /**
+   * Will cause the thread that executes {@link GfshScript#awaitIfNecessary} to wait, if
necessary,
+   * until the subprocess executing this Gfsh script has terminated, or the specified waiting
time
+   * elapses.
+   */
+  public GfshScript awaitQuietlyAtMost(int timeout, TimeUnit timeUnit) {
+    this.awaitQuietly = true;
+
+    return awaitAtMost(timeout, timeUnit);
+  }
+
+
+  protected ProcessBuilder toProcessBuilder(Path gfshPath, File workingDir) {
+    String[] gfshCommands = new String[commands.length + 1];
+    gfshCommands[0] = gfshPath.toAbsolutePath().toString();
+
+    for (int i = 0; i < commands.length; i++) {
+      gfshCommands[i + 1] = "-e " + commands[i];
+    }
+
+    return new ProcessBuilder(gfshCommands).inheritIO().directory(workingDir);
+  }
+
+  protected void awaitIfNecessary(Process process) {
+    if (shouldAwaitQuietly()) {
+      awaitQuietly(process);
+    } else if (shouldAwaitLoudly()) {
+      awaitLoudly(process);
+    }
+
+    if (expectedExitValue != null) {
+      assertThat(process.exitValue()).isEqualTo(expectedExitValue);
+    }
+  }
+
+  private void awaitQuietly(Process process) {
+    try {
+      process.waitFor(timeout, timeoutTimeUnit);
+    } catch (InterruptedException ignore) {
+      // ignore since we are waiting *quietly*
+    }
+  }
+
+  private void awaitLoudly(Process process) {
+    boolean exited;
+    try {
+      exited = process.waitFor(timeout, timeoutTimeUnit);
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+    assertThat(exited).isTrue();
+  }
+
+  private boolean shouldAwait() {
+    return timeoutTimeUnit != null;
+  }
+
+  private boolean shouldAwaitQuietly() {
+    return shouldAwait() && awaitQuietly;
+  }
+
+  private boolean shouldAwaitLoudly() {
+    return shouldAwait() && !awaitQuietly;
+  }
+}


Mime
View raw message