You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by up...@apache.org on 2018/08/01 20:20:51 UTC

[geode] branch develop updated: GEODE-3530: Refactor LauncherLifecycleCommandsDUnitTest

This is an automated email from the ASF dual-hosted git repository.

upthewaterspout pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new eafd116  GEODE-3530: Refactor LauncherLifecycleCommandsDUnitTest
eafd116 is described below

commit eafd116838324ac16bd81375bb06105b30b6c89b
Author: Helena Bales <hb...@pivotal.io>
AuthorDate: Wed Aug 1 13:20:47 2018 -0700

    GEODE-3530: Refactor LauncherLifecycleCommandsDUnitTest
    
    * extract start locator command tests
    * extract status locator tests
    * use the GfshCommandRule so that the test is not an acceptance test
    * use the ClusterStarterRule to start a locator to help with clean-up
    * connect all started locators to the locator
    * shutdown the cluster using the locator to clean up all JVMs
    * wrap resource creations in try-finally block
    * add test for creating dir and cleanup temp files
    * ignore the test that requires fewer permissions
---
 .../LauncherLifecycleCommandsDUnitTest.java        | 275 ----------------
 .../cli/commands/StartLocatorCommandDUnitTest.java | 352 +++++++++++++++++++++
 .../commands/StatusLocatorCommandDunitTest.java    | 243 ++++++++++++++
 .../geode/test/junit/rules/GfshCommandRule.java    |   2 +-
 4 files changed, 596 insertions(+), 276 deletions(-)

diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java
index f605291..b5a8ea9 100644
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/LauncherLifecycleCommandsDUnitTest.java
@@ -27,10 +27,8 @@ import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
-import java.lang.management.ManagementFactory;
 import java.net.InetAddress;
 import java.nio.charset.Charset;
 import java.text.DateFormat;
@@ -77,7 +75,6 @@ import org.apache.geode.internal.AvailablePortHelper;
 import org.apache.geode.internal.lang.ObjectUtils;
 import org.apache.geode.internal.lang.StringUtils;
 import org.apache.geode.internal.lang.SystemUtils;
-import org.apache.geode.internal.process.ProcessType;
 import org.apache.geode.internal.process.ProcessUtils;
 import org.apache.geode.internal.util.IOUtils;
 import org.apache.geode.management.cli.Result;
@@ -282,120 +279,6 @@ public class LauncherLifecycleCommandsDUnitTest extends CliCommandTestBase {
     return serviceState.getStatus();
   }
 
-  protected void writePid(final File pidFile, final int pid) throws IOException {
-    assertTrue("The PID file must actually exist!", pidFile != null && pidFile.isFile());
-
-    FileWriter writer = null;
-
-    try {
-      writer = new FileWriter(pidFile, false);
-      writer.write(String.valueOf(pid));
-      writer.write(System.getProperty("line.separator"));
-      writer.flush();
-    } finally {
-      IOUtils.close(writer);
-    }
-  }
-
-  @Test
-  public void test000StartLocatorCapturesOutputOnError() throws IOException {
-    final int locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    File workingDirectory = temporaryFolder.newFolder(pathname);
-
-    assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-    File pidFile = new File(workingDirectory, ProcessType.LOCATOR.getPidFileName());
-
-    assertTrue(pidFile.createNewFile());
-
-    writePid(pidFile, getPidOrOne());
-    pidFile.deleteOnExit();
-
-    assertTrue(pidFile.isFile());
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-    command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathname);
-    command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-    command.addOption(CliStrings.START_LOCATOR__ENABLE__SHARED__CONFIGURATION,
-        Boolean.FALSE.toString());
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-    command.addOption(CliStrings.START_LOCATOR__J, "-D" + DistributionConfig.GEMFIRE_PREFIX
-        + "jmx-manager-port=" + AvailablePortHelper.getRandomAvailableTCPPort());
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString,
-        resultString.contains(
-            "Exception in thread \"main\" java.lang.RuntimeException: A PID file already exists and a Locator may be running in "
-                + IOUtils.tryGetCanonicalFileElseGetAbsoluteFile(workingDirectory)));
-    assertTrue(resultString,
-        resultString.contains(
-            "Caused by: org.apache.geode.internal.process.FileAlreadyExistsException: Pid file already exists: "
-                + IOUtils.tryGetCanonicalFileElseGetAbsoluteFile(pidFile)));
-  }
-
-  /*
-   * This method makes an effort to get the PID of the running process. If it is unable to determine
-   * accurately, it simply returns 1.
-   */
-  private int getPidOrOne() {
-    int pid = 1;
-    String[] name = ManagementFactory.getRuntimeMXBean().getName().split("@");
-    if (name.length > 1) {
-      try {
-        pid = Integer.parseInt(name[0]);
-      } catch (NumberFormatException nex) {
-        // Ignored
-      }
-    }
-
-    return pid;
-  }
-
-  @Test
-  public void test001StartLocatorFailsFastOnMissingGemFirePropertiesFile() throws IOException {
-    String gemfirePropertiesPathname = "/path/to/missing/gemfire.properties";
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-    String pathName = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    final File workingDirectory = temporaryFolder.newFolder(pathName);
-
-    command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathName);
-    command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_LOCATOR__PORT, "0");
-    command.addOption(CliStrings.START_LOCATOR__PROPERTIES, gemfirePropertiesPathname);
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager=false");
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-port=0");
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-start=false");
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString,
-        resultString
-            .contains(MessageFormat.format(CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE,
-                StringUtils.EMPTY, gemfirePropertiesPathname)));
-  }
-
   /**
    * Test to verify GEODE-2138
    *
@@ -447,42 +330,6 @@ public class LauncherLifecycleCommandsDUnitTest extends CliCommandTestBase {
   }
 
   @Test
-  public void test002StartLocatorFailsFastOnMissingGemFireSecurityPropertiesFile()
-      throws IOException {
-    String gemfireSecurityPropertiesPathname = "/path/to/missing/gemfire-security.properties";
-    String pathName = getClass().getSimpleName().concat("_").concat(getTestMethodName());
-    final File workingDirectory = temporaryFolder.newFolder(pathName);
-
-    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-    command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathName);
-    command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-    command.addOption(CliStrings.START_LOCATOR__PORT, "0");
-    command.addOption(CliStrings.START_LOCATOR__SECURITY_PROPERTIES,
-        gemfireSecurityPropertiesPathname);
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager=false");
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-port=0");
-    command.addOption(CliStrings.START_LOCATOR__J,
-        "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-start=false");
-
-    CommandResult result = executeCommand(command.toString());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-
-    String resultString = toString(result);
-
-    assertTrue(resultString,
-        resultString
-            .contains(MessageFormat.format(CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE,
-                "Security ", gemfireSecurityPropertiesPathname)));
-  }
-
-  @Test
   public void test003StartServerFailsFastOnMissingCacheXmlFile() throws IOException {
     String cacheXmlPathname = "/path/to/missing/cache.xml";
 
@@ -582,128 +429,6 @@ public class LauncherLifecycleCommandsDUnitTest extends CliCommandTestBase {
   }
 
   @Test
-  public void test006StartLocatorInRelativeDirectory() {
-    final int locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    File workingDirectory = new File(pathname);
-
-    assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-    try {
-      CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-      command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathname);
-      command.addOption(CliStrings.START_LOCATOR__CONNECT, Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__DIR, pathname);
-      command.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-      command.addOption(CliStrings.START_LOCATOR__ENABLE__SHARED__CONFIGURATION,
-          Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-      command.addOption(CliStrings.START_LOCATOR__J, "-D" + DistributionConfig.GEMFIRE_PREFIX
-          + "jmx-manager-port=" + AvailablePortHelper.getRandomAvailableTCPPort());
-
-      CommandResult result = executeCommand(command.toString());
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      String locatorOutput = toString(result);
-
-      assertNotNull(locatorOutput);
-      assertTrue("Locator output was: " + locatorOutput, locatorOutput.contains(
-          "Locator in " + IOUtils.tryGetCanonicalFileElseGetAbsoluteFile(workingDirectory)));
-    } finally {
-      stopLocator(workingDirectory);
-    }
-  }
-
-  @Test
-  public void test007StatusLocatorUsingMemberNameIDWhenGfshIsNotConnected() {
-    CommandResult result =
-        executeCommand(CliStrings.STATUS_LOCATOR + " --name=" + getTestMethodName());
-
-    assertNotNull(result);
-    assertEquals(Result.Status.ERROR, result.getStatus());
-    assertEquals(
-        CliStrings.format(CliStrings.STATUS_SERVICE__GFSH_NOT_CONNECTED_ERROR_MESSAGE, "Locator"),
-        StringUtils.trim(toString(result)));
-  }
-
-  @Test
-  public void test008StatusLocatorUsingMemberName() throws IOException {
-    final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
-
-    final int jmxManagerPort = ports[0];
-    final int locatorPort = ports[1];
-
-    String pathname = (getClass().getSimpleName() + "_" + getTestMethodName());
-    File workingDirectory = temporaryFolder.newFolder(pathname);
-
-    assertTrue(workingDirectory.isDirectory() || workingDirectory.mkdir());
-
-    try {
-      CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_LOCATOR);
-
-      command.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, pathname);
-      command.addOption(CliStrings.START_LOCATOR__CONNECT, Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__DIR, workingDirectory.getCanonicalPath());
-      command.addOption(CliStrings.START_LOCATOR__PORT, String.valueOf(locatorPort));
-      command.addOption(CliStrings.START_LOCATOR__ENABLE__SHARED__CONFIGURATION,
-          Boolean.FALSE.toString());
-      command.addOption(CliStrings.START_LOCATOR__FORCE, Boolean.TRUE.toString());
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "http-service-port=0");
-      command.addOption(CliStrings.START_LOCATOR__J,
-          "-D" + DistributionConfig.GEMFIRE_PREFIX + "jmx-manager-port=" + jmxManagerPort);
-
-      CommandResult result = executeCommand(command.toString());
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      LocatorLauncher locatorLauncher = new LocatorLauncher.Builder()
-          .setCommand(LocatorLauncher.Command.STATUS).setBindAddress(null).setPort(locatorPort)
-          .setWorkingDirectory(workingDirectory.getPath()).build();
-
-      assertNotNull(locatorLauncher);
-
-      LocatorState expectedLocatorState =
-          locatorLauncher.waitOnStatusResponse(60, 10, TimeUnit.SECONDS);
-
-      assertNotNull(expectedLocatorState);
-      assertEquals(Status.ONLINE, expectedLocatorState.getStatus());
-
-      result = executeCommand(
-          String.format("%1$s --locator=localhost[%2$d]", CliStrings.CONNECT, locatorPort));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-
-      result = executeCommand(
-          String.format("%1$s --name=invalidLocatorMemberName", CliStrings.STATUS_LOCATOR));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.ERROR, result.getStatus());
-      assertEquals(
-          CliStrings.format(CliStrings.STATUS_LOCATOR__NO_LOCATOR_FOUND_FOR_MEMBER_ERROR_MESSAGE,
-              "invalidLocatorMemberName"),
-          StringUtils.trim(toString(result)));
-
-      result =
-          executeCommand(String.format("%1$s --name=%2$s", CliStrings.STATUS_LOCATOR, pathname));
-
-      assertNotNull(result);
-      assertEquals(Result.Status.OK, result.getStatus());
-      assertTrue(serviceStateStatusStringNormalized(toString(result))
-          .contains(serviceStateStatusStringNormalized(expectedLocatorState)));
-    } finally {
-      stopLocator(workingDirectory);
-    }
-  }
-
-  @Test
   public void test009StatusLocatorUsingMemberId() throws Exception {
     final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
 
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java
new file mode 100644
index 0000000..3c2c7f6
--- /dev/null
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommandDUnitTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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 static org.apache.geode.management.internal.cli.i18n.CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__DIR;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__LOCATORS;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__MEMBER_NAME;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__PORT;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__PROPERTIES;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.START_LOCATOR__SECURITY_PROPERTIES;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.text.MessageFormat;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.internal.process.ProcessType;
+import org.apache.geode.internal.util.IOUtils;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.dunit.rules.ClusterStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.rules.GfshCommandRule;
+
+public class StartLocatorCommandDUnitTest {
+  private static MemberVM locator;
+  private static String locatorConnectionString;
+
+  @ClassRule
+  public static GfshCommandRule gfsh = new GfshCommandRule();
+
+  @ClassRule
+  public static final ClusterStartupRule cluster = new ClusterStartupRule();
+
+  @Rule
+  public TemporaryFolder tempFolder = new TemporaryFolder();
+
+  @BeforeClass
+  public static void before() {
+    // locator used to clean up JVMs after test
+    locator = cluster.startLocatorVM(0, 0);
+
+    locatorConnectionString = "localhost[" + locator.getPort() + "]";
+  }
+
+  @AfterClass
+  public static void after() throws Exception {
+    gfsh.connectAndVerify(locator);
+    gfsh.execute("shutdown --include-locators");
+  }
+
+  @Test
+  public void testWithConflictingPIDFile() throws Exception {
+    final String fileName = ProcessType.LOCATOR.getPidFileName();
+    final String memberName = "testWithConflictingPIDFile-locator";
+
+    // create dir for pid file
+    File dir = tempFolder.newFolder();
+
+    // create pid file
+    File pidFile = new File(dir.getAbsolutePath(), fileName);
+    assertThat(pidFile.createNewFile()).isTrue();
+
+    // write pid to pid file
+    try (FileWriter fileWriter = new FileWriter(pidFile, false)) {
+      fileWriter.write(getPidOrOne().toString() + "\n");
+      fileWriter.flush();
+    }
+
+    assertThat(pidFile.isFile()).isTrue();
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, pidFile.getParentFile().getCanonicalPath())
+        .addOption(START_LOCATOR__PORT, "0");
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    final String expectedError = "A PID file already exists and a Locator may be running in "
+        + pidFile.getParentFile().getCanonicalPath();
+    final String expectedCause = "Caused by: "
+        + "org.apache.geode.internal.process.FileAlreadyExistsException: Pid file already exists: "
+        + pidFile.getCanonicalPath();
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+    assertThat(result.getMessageFromContent()).contains(expectedCause);
+  }
+
+  @Test
+  public void testWithMissingGemFirePropertiesFile() throws IOException {
+    final String missingPropertiesPath = "/path/to/missing/gemfire.properties";
+    final String memberName = "testWithMissingGemFirePropertiesFile-locator";
+    final String expectedError =
+        MessageFormat.format(GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE, "", missingPropertiesPath);
+
+    File workingDir = tempFolder.newFolder();
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__DIR, workingDir.getAbsolutePath())
+        .addOption(START_LOCATOR__PROPERTIES, missingPropertiesPath);
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMissingGemFireSecurityPropertiesFile() throws IOException {
+    final String missingSecurityPropertiesPath = "/path/to/missing/gemfire-security.properties";
+    final String memberName = "testWithMissingGemFireSecurityPropertiesFile-locator";
+    final String expectedError = MessageFormat.format(GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE,
+        "Security ", missingSecurityPropertiesPath);
+
+    File workingDir = tempFolder.newFolder();
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, workingDir.getAbsolutePath())
+        .addOption(START_LOCATOR__SECURITY_PROPERTIES, missingSecurityPropertiesPath);
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithUnavailablePort() throws IOException {
+    final Integer locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithUnavailablePort-locator";
+    final String unexpectedMessage = "[" + locatorPort + "] as locator is currently online.";
+    final String expectedMessage = "java.net.BindException: Network is unreachable; port ("
+        + locatorPort + ") is not available on localhost.";
+
+    try (Socket interferingProcess = new Socket()) {
+      interferingProcess.bind(new InetSocketAddress(locatorPort));
+
+      File workingDir = tempFolder.newFolder();
+
+      CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+          .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+          .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+          .addOption(START_LOCATOR__DIR, workingDir.getAbsolutePath())
+          .addOption(START_LOCATOR__PORT, locatorPort.toString());
+
+      CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+      assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+      assertThat(result.getMessageFromContent()).doesNotContain(unexpectedMessage);
+      assertThat(result.getMessageFromContent()).contains(expectedMessage);
+    }
+  }
+
+  @Test
+  public void testWithAvailablePort() throws IOException {
+    final Integer locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String memberName = "testWithAvailablePort-locator";
+    final String expectedMessage =
+        "[" + locatorPort + "] as " + memberName + " is currently online.";
+
+    File workingDir = tempFolder.newFolder();
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, workingDir.getAbsolutePath())
+        .addOption(START_LOCATOR__PORT, locatorPort.toString());
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMessageFromContent()).contains(expectedMessage);
+  }
+
+  @Test
+  public void testWithDefaultLocatorPort() throws IOException {
+    final String memberName = "testWithDefaultLocatorPort-locator";
+    final String unexpectedMessage = "[0] as " + memberName + " is currently online.";
+    final String expectedMessage = "\\[\\d+\\] as " + memberName + " is currently online.";
+
+    File workingDir = tempFolder.newFolder();
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, workingDir.getAbsolutePath())
+        .addOption(START_LOCATOR__PORT, "0");
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMessageFromContent()).doesNotContain(unexpectedMessage);
+    assertThat(result.getMessageFromContent()).containsPattern(expectedMessage);
+  }
+
+  @Ignore // Ignore until test until tests in CI use a different user
+  @Test
+  public void testInMissingRelativeDirectoryWithoutCreatePermissions() {
+    // path to a missing dir that cannot be created due to insufficient permissions
+    final String missingDirPath = "/missing/path/to/start/in";
+    final String expectedMessage = "Could not create directory " + missingDirPath
+        + ". Please verify directory path or user permissions.";
+    final String memberName = "testWithMissingRelativeDirectory-locator";
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, missingDirPath);
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedMessage);
+  }
+
+  @Test
+  public void testInMissingRelativeDirectoryThatCanBeCreated() {
+    // path to a missing dir that can be created
+    final String missingDirPath = System.getProperty("user.dir") + "/missing/path/to/start/in";
+    final String memberName = "testWithMissingRelativeDirectory-locator";
+    final String expectedMessage = "Locator in " + missingDirPath;
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, missingDirPath);
+
+    try {
+      CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+      assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+      assertThat(result.getMessageFromContent()).contains(expectedMessage);
+    } finally {
+      File toDelete = new File(missingDirPath);
+      deleteLocatorFiles(toDelete);
+    }
+  }
+
+  @Test
+  public void testWithRelativeDirectory() throws IOException {
+    final Integer locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final File dir = tempFolder.newFolder();
+    final String memberName = "testWithRelativeDirectory-locator";
+    final String expectedMessage =
+        "Locator in " + IOUtils.tryGetCanonicalFileElseGetAbsoluteFile(dir);
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__DIR, dir.getAbsolutePath())
+        .addOption(START_LOCATOR__PORT, locatorPort.toString());
+
+    CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMessageFromContent()).contains(expectedMessage);
+  }
+
+  @Test
+  public void testWithCurrentDirectory() {
+    final Integer locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    final String expectedMessage = "Locator in " + System.getProperty("user.dir");
+    final String expectedVersionPattern = "Geode Version: \\d+\\.\\d+\\.\\d+";
+    final String memberName = "testWithCurrentDirectory-locator";
+
+    CommandStringBuilder command = new CommandStringBuilder(START_LOCATOR)
+        .addOption(START_LOCATOR__MEMBER_NAME, memberName)
+        .addOption(START_LOCATOR__LOCATORS, locatorConnectionString)
+        .addOption(START_LOCATOR__PORT, locatorPort.toString());
+
+    try {
+      CommandResult result = gfsh.executeCommand(command.getCommandString());
+
+      assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+      assertThat(result.getMessageFromContent()).contains(expectedMessage);
+
+      // Verify GEODE-2138
+      assertThat(result.getMessageFromContent()).doesNotContain("Gemfire")
+          .doesNotContain("GemFire");
+      assertThat(result.getMessageFromContent()).containsPattern(expectedVersionPattern);
+    } finally {
+      String pathToFile = System.getProperty("user.dir") + "/" + memberName;
+      File toDelete = new File(pathToFile);
+      deleteLocatorFiles(toDelete);
+    }
+  }
+
+  private void deleteLocatorFiles(File toDelete) {
+    File[] nestedToDelete = toDelete.listFiles();
+
+    if (nestedToDelete != null && nestedToDelete.length > 0) {
+      for (File file : nestedToDelete) {
+        deleteLocatorFiles(file);
+      }
+    }
+
+    toDelete.delete();
+  }
+
+  /**
+   * Attempts to determine the PID of the running process from the ManagementFactory's runtime MBean
+   *
+   * @return 1 if unable to determine the pid
+   * @return the PID if possible
+   */
+  private Integer getPidOrOne() {
+    Integer pid = 1;
+    String[] name = ManagementFactory.getRuntimeMXBean().getName().split("@");
+    if (name.length > 1) {
+      try {
+        pid = Integer.parseInt(name[0]);
+      } catch (NumberFormatException nex) {
+        // Ignored
+      }
+    }
+
+    return pid;
+  }
+}
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java
new file mode 100644
index 0000000..8249f28
--- /dev/null
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/StatusLocatorCommandDunitTest.java
@@ -0,0 +1,243 @@
+/*
+ * 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 static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.geode.internal.i18n.LocalizedStrings.Launcher_Builder_UNKNOWN_HOST_ERROR_MESSAGE;
+import static org.apache.geode.internal.i18n.LocalizedStrings.Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.LOCATOR_TERM_NAME;
+import static org.apache.geode.management.internal.cli.i18n.CliStrings.STATUS_LOCATOR;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.distributed.LocatorLauncher;
+import org.apache.geode.distributed.LocatorLauncher.LocatorState;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.test.junit.rules.GfshCommandRule;
+import org.apache.geode.test.junit.rules.GfshCommandRule.PortType;
+
+public class StatusLocatorCommandDunitTest {
+  private static final Integer TIMEOUT = 300;
+  private static final Integer INTERVAL = 10;
+  private static final String locatorName = "locator";
+
+  private static LocatorLauncher locator;
+
+  @ClassRule
+  public static GfshCommandRule gfsh = new GfshCommandRule();
+
+  @ClassRule
+  public static TemporaryFolder tempDir = new TemporaryFolder();
+
+  @BeforeClass
+  public static void before() throws IOException {
+    File workingDir = tempDir.newFolder("workingDir");
+
+    locator = new LocatorLauncher.Builder().setMemberName(locatorName).setPort(0)
+        .setWorkingDirectory(workingDir.getAbsolutePath())
+        .set("cluster-configuration-dir", workingDir.getAbsolutePath()).build();
+    locator.start();
+  }
+
+  @AfterClass
+  public static void after() {
+    locator.stop();
+  }
+
+  @Test
+  public void testWithDisconnectedGfsh() throws Exception {
+    final String expectedResult =
+        CliStrings.format(CliStrings.STATUS_SERVICE__GFSH_NOT_CONNECTED_ERROR_MESSAGE,
+            LOCATOR_TERM_NAME);
+
+    if (gfsh.isConnected()) {
+      gfsh.disconnect();
+    }
+
+    gfsh.executeAndAssertThat(STATUS_LOCATOR + " --name=" + locatorName)
+        .statusIsError()
+        .containsOutput(expectedResult);
+  }
+
+  @Test
+  public void testWithMemberAddress() throws Exception {
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    LocatorState state = locator.waitOnStatusResponse(TIMEOUT, INTERVAL, SECONDS);
+
+    CommandResult result =
+        gfsh.executeCommand(STATUS_LOCATOR + " --host=localhost --port=" + locator.getPort());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertStatusCommandOutput(result.getMessageFromContent(), state);
+  }
+
+  @Test
+  public void testWithInvalidMemberAddress() throws Exception {
+    final String expectedError =
+        Launcher_Builder_UNKNOWN_HOST_ERROR_MESSAGE.toString(LOCATOR_TERM_NAME);
+
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    CommandResult result =
+        gfsh.executeCommand(STATUS_LOCATOR + " --host=invalidHostName --port=" + locator.getPort());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMemberName() throws Exception {
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    LocatorState state = locator.waitOnStatusResponse(TIMEOUT, INTERVAL, SECONDS);
+
+    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --name=" + locatorName);
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertStatusCommandOutput(result.getMessageFromContent(), state);
+  }
+
+  @Test
+  public void testWithInvalidMemberName() throws Exception {
+    final String expectedError = CliStrings.format(
+        CliStrings.STATUS_LOCATOR__NO_LOCATOR_FOUND_FOR_MEMBER_ERROR_MESSAGE, "invalidLocatorName");
+
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --name=invalidLocatorName");
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithMemberID() throws Exception {
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    LocatorState state = locator.waitOnStatusResponse(TIMEOUT, INTERVAL, SECONDS);
+
+    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --name=" + locator.getMemberId());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertStatusCommandOutput(result.getMessageFromContent(), state);
+  }
+
+  @Test
+  public void testWithInvalidMemberID() throws Exception {
+    final String expectedError =
+        CliStrings.format(CliStrings.STATUS_LOCATOR__NO_LOCATOR_FOUND_FOR_MEMBER_ERROR_MESSAGE,
+            locator.getMemberId() + "1");
+
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    CommandResult result =
+        gfsh.executeCommand(STATUS_LOCATOR + " --name=" + locator.getMemberId() + "1");
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithDirOnline() throws Exception {
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    LocatorState state = locator.waitOnStatusResponse(TIMEOUT, INTERVAL, SECONDS);
+
+    CommandResult result =
+        gfsh.executeCommand(STATUS_LOCATOR + " --dir=" + locator.getWorkingDirectory());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertStatusCommandOutput(result.getMessageFromContent(), state);
+  }
+
+  @Test
+  public void testWithDirOffline() throws Exception {
+    if (gfsh.isConnected()) {
+      gfsh.disconnect();
+    }
+
+    LocatorState state = locator.waitOnStatusResponse(TIMEOUT, INTERVAL, SECONDS);
+
+    CommandResult result =
+        gfsh.executeCommand(STATUS_LOCATOR + " --dir=" + locator.getWorkingDirectory());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertStatusCommandOutput(result.getMessageFromContent(), state);
+  }
+
+  @Test
+  public void testWithInvalidDirOnline() throws Exception {
+    final String expectedError =
+        Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE.toString(LOCATOR_TERM_NAME);
+
+    gfsh.connectAndVerify(locator.getPort(), PortType.locator);
+
+    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --dir=/invalid/working/dir");
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  @Test
+  public void testWithInvalidDirOffline() throws Exception {
+    final String expectedError =
+        Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE.toString(LOCATOR_TERM_NAME);
+
+    if (gfsh.isConnected()) {
+      gfsh.disconnect();
+    }
+
+    CommandResult result = gfsh.executeCommand(STATUS_LOCATOR + " --dir=/invalid/working/dir");
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getMessageFromContent()).contains(expectedError);
+  }
+
+  public void assertStatusCommandOutput(String locatorStatusCommandMessage, LocatorState state) {
+    assertSoftly(softly -> {
+      softly.assertThat(locatorStatusCommandMessage)
+          .contains("Process ID: " + state.getPid())
+          .contains("Geode Version: " + state.getGemFireVersion())
+          .contains("Java Version: " + state.getJavaVersion())
+          .contains("Log File: " + state.getLogFile())
+          .contains("JVM Arguments: " + parseJvmArgs(state.getJvmArguments()))
+          .containsPattern("Uptime: \\d+");
+    });
+  }
+
+  public String parseJvmArgs(List<String> jvmArgs) {
+    String parsedArgs = jvmArgs.get(0);
+
+    for (int i = 1; i < jvmArgs.size(); i++) {
+      parsedArgs += " ";
+      parsedArgs += jvmArgs.get(i);
+    }
+
+    return parsedArgs;
+  }
+}
diff --git a/geode-core/src/integrationTest/java/org/apache/geode/test/junit/rules/GfshCommandRule.java b/geode-core/src/integrationTest/java/org/apache/geode/test/junit/rules/GfshCommandRule.java
index 8a03309f04..f999cd7 100644
--- a/geode-core/src/integrationTest/java/org/apache/geode/test/junit/rules/GfshCommandRule.java
+++ b/geode-core/src/integrationTest/java/org/apache/geode/test/junit/rules/GfshCommandRule.java
@@ -39,7 +39,7 @@ import org.apache.geode.test.junit.assertions.CommandResultAssert;
 
 /**
  * Class which eases the connection to the locator/jmxManager in Gfsh shell and execute gfsh
- * commands.
+ * commands. This rule can't be used to start locators or servers; instead use GfshRule.
  *
  * <p>
  * if used with {@link ConnectionConfiguration}, you will need to specify a port number when