You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sa...@apache.org on 2020/07/27 13:24:01 UTC
[cassandra] branch trunk updated: CASSANDRA-15942 Improve tooling
testing framework
This is an automated email from the ASF dual-hosted git repository.
samt pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new a821214 CASSANDRA-15942 Improve tooling testing framework
a821214 is described below
commit a8212149aba7a85139de9758988f6e01f7bc8d91
Author: Bereng <be...@gmail.com>
AuthorDate: Tue Jul 14 16:44:58 2020 +0200
CASSANDRA-15942 Improve tooling testing framework
Patch by Berenguer Blasi; reviewed by Robert Stupp
and Sam Tunnicliffe for CASSANDRA-15942
---
.../config/DatabaseDescriptorRefTest.java | 2 +-
test/unit/org/apache/cassandra/cql3/CQLTester.java | 66 +++-
.../org/apache/cassandra/tools/BulkLoaderTest.java | 28 +-
.../apache/cassandra/tools/ClearSnapshotTest.java | 91 ++---
.../cassandra/tools/CompactionStressTest.java | 41 +-
.../org/apache/cassandra/tools/GetVersionTest.java | 6 +-
.../apache/cassandra/tools/LoaderOptionsTest.java | 2 +-
.../{ToolsTester.java => OfflineToolUtils.java} | 66 +---
.../tools/SSTableExpiredBlockersTest.java | 10 +-
.../apache/cassandra/tools/SSTableExportTest.java | 11 +-
.../cassandra/tools/SSTableLevelResetterTest.java | 11 +-
.../cassandra/tools/SSTableMetadataViewerTest.java | 10 +-
.../cassandra/tools/SSTableOfflineRelevelTest.java | 10 +-
.../tools/SSTableRepairedAtSetterTest.java | 11 +-
.../cassandra/tools/StandaloneSSTableUtilTest.java | 11 +-
.../cassandra/tools/StandaloneScrubberTest.java | 11 +-
.../cassandra/tools/StandaloneSplitterTest.java | 8 +-
.../cassandra/tools/StandaloneUpgraderTest.java | 11 +-
.../cassandra/tools/StandaloneVerifierTest.java | 11 +-
.../org/apache/cassandra/tools/ToolRunner.java | 437 +++++++++++++++++++++
20 files changed, 676 insertions(+), 178 deletions(-)
diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
index 576862b..8f731e2 100644
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
@@ -49,7 +49,7 @@ import static org.junit.Assert.fail;
* unexpected threads.
*
* {@link DatabaseDescriptor#toolInitialization()} is tested via unit tests extending
- * {@link org.apache.cassandra.tools.ToolsTester}.
+ * {@link org.apache.cassandra.tools.OfflineToolUtils}.
*/
public class DatabaseDescriptorRefTest
{
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index 619fdad..0455f97 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -22,9 +22,12 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
+import java.rmi.server.RMISocketFactory;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -34,6 +37,12 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXServiceURL;
+
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
@@ -80,6 +89,7 @@ import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.JMXServerUtils;
import org.apache.cassandra.security.ThreadAwareSecurityManager;
import static junit.framework.Assert.assertNotNull;
@@ -103,6 +113,11 @@ public abstract class CQLTester
public static final String RACK1 = "rack1";
private static org.apache.cassandra.transport.Server server;
+ private static JMXConnectorServer jmxServer;
+ protected static String jmxHost;
+ protected static int jmxPort;
+ protected static MBeanServerConnection jmxConnection;
+
protected static final int nativePort;
protected static final InetAddress nativeAddr;
protected static final Set<InetAddressAndPort> remoteAddrs = new HashSet<>();
@@ -240,6 +255,43 @@ public abstract class CQLTester
AuditLogManager.instance.initialize();
isServerPrepared = true;
}
+
+ /**
+ * Starts the JMX server. It's safe to call this method multiple times.
+ */
+ public static void startJMXServer() throws Exception
+ {
+ if (jmxServer != null)
+ return;
+
+ InetAddress loopback = InetAddress.getLoopbackAddress();
+ jmxHost = loopback.getHostAddress();
+ try (ServerSocket sock = new ServerSocket())
+ {
+ sock.bind(new InetSocketAddress(loopback, 0));
+ jmxPort = sock.getLocalPort();
+ }
+
+ jmxServer = JMXServerUtils.createJMXServer(jmxPort, true);
+ jmxServer.start();
+ }
+
+ public static void createMBeanServerConnection() throws Exception
+ {
+ assert jmxServer != null : "jmxServer not started";
+
+ Map<String, Object> env = new HashMap<>();
+ env.put("com.sun.jndi.rmi.factory.socket", RMISocketFactory.getDefaultSocketFactory());
+ JMXConnector jmxc = JMXConnectorFactory.connect(getJMXServiceURL(), env);
+ jmxConnection = jmxc.getMBeanServerConnection();
+ }
+
+ public static JMXServiceURL getJMXServiceURL() throws MalformedURLException
+ {
+ assert jmxServer != null : "jmxServer not started";
+
+ return new JMXServiceURL(String.format("service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", jmxHost, jmxPort));
+ }
public static void cleanupAndLeaveDirs() throws IOException
{
@@ -399,7 +451,19 @@ public abstract class CQLTester
}
});
}
-
+
+ public static List<String> buildNodetoolArgs(List<String> args)
+ {
+ List<String> allArgs = new ArrayList<>();
+ allArgs.add("bin/nodetool");
+ allArgs.add("-p");
+ allArgs.add(Integer.toString(jmxPort));
+ allArgs.add("-h");
+ allArgs.add(jmxHost);
+ allArgs.addAll(args);
+ return allArgs;
+ }
+
// lazy initialization for all tests that require Java Driver
protected static void requireNetwork() throws ConfigurationException
{
diff --git a/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java b/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java
index 6ed38a0..354511a 100644
--- a/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java
+++ b/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java
@@ -24,15 +24,22 @@ import org.junit.runner.RunWith;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(OrderedJUnit4ClassRunner.class)
-public class BulkLoaderTest extends ToolsTester
+public class BulkLoaderTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
- public void testBulkLoader_NoArgs()
+ public void testBulkLoader_NoArgs() throws Exception
{
- runTool(1, "org.apache.cassandra.tools.BulkLoader");
+ ToolRunner tool = runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader");
+ assertEquals(1, tool.getExitCode());
+ assertTrue(!tool.getStderr().isEmpty());
+
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -40,13 +47,14 @@ public class BulkLoaderTest extends ToolsTester
assertKeyspaceNotLoaded();
assertServerNotLoaded();
}
-
+
@Test
public void testBulkLoader_WithArgs() throws Exception
{
try
{
- runTool(0, "org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"))
+ .waitAndAssertOnCleanExit();
fail();
}
catch (RuntimeException e)
@@ -69,7 +77,8 @@ public class BulkLoaderTest extends ToolsTester
{
try
{
- runTool(0, "org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", "--port", "9042", sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", "--port", "9042", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"))
+ .waitAndAssertOnCleanExit();
fail();
}
catch (RuntimeException e)
@@ -92,7 +101,8 @@ public class BulkLoaderTest extends ToolsTester
{
try
{
- runTool(0, "org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1:9042", "--port", "9041", sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1:9042", "--port", "9041", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"))
+ .waitAndAssertOnCleanExit();
fail();
}
catch (RuntimeException e)
@@ -115,7 +125,7 @@ public class BulkLoaderTest extends ToolsTester
{
try
{
- runTool(1, "org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", "--port", "9041", sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", "--port", "9041", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
}
catch (RuntimeException e)
{
@@ -128,7 +138,7 @@ public class BulkLoaderTest extends ToolsTester
{
try
{
- runTool(1, "org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1:9041", sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1:9041", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
}
catch (RuntimeException e)
{
diff --git a/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java b/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java
index a73493b..7e70467 100644
--- a/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java
+++ b/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java
@@ -20,102 +20,85 @@ package org.apache.cassandra.tools;
import java.io.IOException;
import java.util.Map;
+
import javax.management.openmbean.TabularData;
-import org.apache.commons.lang3.ArrayUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.apache.cassandra.SchemaLoader;
-import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.service.EmbeddedCassandraService;
+import org.apache.cassandra.cql3.CQLTester;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
-public class ClearSnapshotTest extends ToolsTester
+public class ClearSnapshotTest extends CQLTester
{
- private static EmbeddedCassandraService cassandra;
- private static String initialJmxPortValue;
private static NodeProbe probe;
- private static final int JMX_PORT = 7188;
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
@BeforeClass
- public static void setup() throws IOException
+ public static void setup() throws Exception
{
- // Set system property to enable JMX port on localhost for embedded server
- initialJmxPortValue = System.getProperty("cassandra.jmx.local.port");
- System.setProperty("cassandra.jmx.local.port", String.valueOf(JMX_PORT));
-
- SchemaLoader.prepareServer();
- // CASSANDRA-15776 - size_estimates and table_estimates get truncated on startup, and auto snapshot is true by default for tests
- // set it false so the test state doesn't see those snapshots
- DatabaseDescriptor.setAutoSnapshot(false);
- cassandra = new EmbeddedCassandraService();
- cassandra.start();
-
- probe = new NodeProbe("127.0.0.1", JMX_PORT);
+ startJMXServer();
+ probe = new NodeProbe(jmxHost, jmxPort);
}
@AfterClass
public static void teardown() throws IOException
{
- cassandra.stop();
- if (initialJmxPortValue != null)
- {
- System.setProperty("cassandra.jmx.local.port", initialJmxPortValue);
- }
-
probe.close();
}
- private String[] constructParamaterArray(final String command, final String... commandParams)
- {
- String[] baseCommandLine = {"-p", String.valueOf(JMX_PORT), command};
- return ArrayUtils.addAll(baseCommandLine, commandParams);
- }
-
@Test
public void testClearSnapshot_NoArgs() throws IOException
{
- runTool(2, "org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("clearsnapshot"));
+ ToolRunner tool = runner.invokeNodetool("clearsnapshot");
+ assertEquals(2, tool.getExitCode());
+ assertTrue("Tool stderr: " + tool.getStderr(), tool.getStderr().contains("Specify snapshot name or --all"));
+
+ runner.invokeNodetool("clearsnapshot", "--all").waitAndAssertOnCleanExit();
}
@Test
public void testClearSnapshot_AllAndName() throws IOException
{
- runTool(2, "org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("clearsnapshot", "-t", "some-name", "--all"));
+ ToolRunner tool = runner.invokeNodetool("clearsnapshot", "-t", "some-name", "--all");
+ assertEquals(2, tool.getExitCode());
+ assertTrue("Tool stderr: " + tool.getStderr(), tool.getStderr().contains("Specify only one of snapshot name or --all"));
}
@Test
public void testClearSnapshot_RemoveByName() throws IOException
{
- runTool(0,"org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("snapshot","-t","some-name"));
-
- Map<String, TabularData> snapshots_before = probe.getSnapshotDetails();
- Assert.assertTrue(snapshots_before.containsKey("some-name"));
-
- runTool(0,"org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("clearsnapshot","-t","some-name"));
- Map<String, TabularData> snapshots_after = probe.getSnapshotDetails();
- Assert.assertFalse(snapshots_after.containsKey("some-name"));
+ ToolRunner tool = runner.invokeNodetool("snapshot","-t","some-name").waitAndAssertOnCleanExit();
+ assertTrue(!tool.getStdout().isEmpty());
+
+ Map<String, TabularData> snapshots_before = probe.getSnapshotDetails();
+ Assert.assertTrue(snapshots_before.containsKey("some-name"));
+
+ tool = runner.invokeNodetool("clearsnapshot","-t","some-name").waitAndAssertOnCleanExit();
+ assertTrue(!tool.getStdout().isEmpty());
+
+ Map<String, TabularData> snapshots_after = probe.getSnapshotDetails();
+ Assert.assertFalse(snapshots_after.containsKey("some-name"));
}
@Test
public void testClearSnapshot_RemoveMultiple() throws IOException
{
- runTool(0,"org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("snapshot","-t","some-name"));
- runTool(0,"org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("snapshot","-t","some-other-name"));
-
+ ToolRunner tool = runner.invokeNodetool("snapshot","-t","some-name").waitAndAssertOnCleanExit();
+ assertTrue(!tool.getStdout().isEmpty());
+ tool = runner.invokeNodetool("snapshot","-t","some-other-name").waitAndAssertOnCleanExit();
+ assertTrue(!tool.getStdout().isEmpty());
+
Map<String, TabularData> snapshots_before = probe.getSnapshotDetails();
Assert.assertTrue(snapshots_before.size() == 2);
- runTool(0,"org.apache.cassandra.tools.NodeTool",
- constructParamaterArray("clearsnapshot","--all"));
+ tool = runner.invokeNodetool("clearsnapshot","--all").waitAndAssertOnCleanExit();
+ assertTrue(!tool.getStdout().isEmpty());
+
Map<String, TabularData> snapshots_after = probe.getSnapshotDetails();
Assert.assertTrue(snapshots_after.size() == 0);
}
diff --git a/test/unit/org/apache/cassandra/tools/CompactionStressTest.java b/test/unit/org/apache/cassandra/tools/CompactionStressTest.java
index c8b0b97..651e24d 100644
--- a/test/unit/org/apache/cassandra/tools/CompactionStressTest.java
+++ b/test/unit/org/apache/cassandra/tools/CompactionStressTest.java
@@ -26,12 +26,14 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
@RunWith(OrderedJUnit4ClassRunner.class)
-public class CompactionStressTest extends ToolsTester
+public class CompactionStressTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testNoArgs()
{
- runTool(0, "org.apache.cassandra.stress.CompactionStress");
+ runner.invokeClassAsTool("org.apache.cassandra.stress.CompactionStress").waitAndAssertOnCleanExit();
}
@Test
@@ -41,20 +43,27 @@ public class CompactionStressTest extends ToolsTester
File file = new File(classLoader.getResource("blogpost.yaml").getFile());
String profileFile = file.getAbsolutePath();
- runTool(0,
- "org.apache.cassandra.stress.CompactionStress",
- "write",
- "-d", "build/test/cassandra",
- "-g", "0",
- "-p", profileFile,
- "-t", "4");
-
- runTool(0,
- "org.apache.cassandra.stress.CompactionStress",
- "compact",
- "-d", "build/test/cassandra",
- "-p", profileFile,
- "-t", "4");
+ runner.invokeClassAsTool("org.apache.cassandra.stress.CompactionStress",
+ "write",
+ "-d",
+ "build/test/cassandra",
+ "-g",
+ "0",
+ "-p",
+ profileFile,
+ "-t",
+ "4")
+ .waitAndAssertOnCleanExit();
+
+ runner.invokeClassAsTool("org.apache.cassandra.stress.CompactionStress",
+ "compact",
+ "-d",
+ "build/test/cassandra",
+ "-p",
+ profileFile,
+ "-t",
+ "4")
+ .waitAndAssertOnCleanExit();
}
}
diff --git a/test/unit/org/apache/cassandra/tools/GetVersionTest.java b/test/unit/org/apache/cassandra/tools/GetVersionTest.java
index 84e2f49..c5f5282 100644
--- a/test/unit/org/apache/cassandra/tools/GetVersionTest.java
+++ b/test/unit/org/apache/cassandra/tools/GetVersionTest.java
@@ -24,12 +24,14 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
@RunWith(OrderedJUnit4ClassRunner.class)
-public class GetVersionTest extends ToolsTester
+public class GetVersionTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testGetVersion()
{
- runTool(0, "org.apache.cassandra.tools.GetVersion");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.GetVersion").waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/LoaderOptionsTest.java b/test/unit/org/apache/cassandra/tools/LoaderOptionsTest.java
index 67258ad..6377c59 100644
--- a/test/unit/org/apache/cassandra/tools/LoaderOptionsTest.java
+++ b/test/unit/org/apache/cassandra/tools/LoaderOptionsTest.java
@@ -23,7 +23,7 @@ import org.junit.Test;
import org.apache.cassandra.config.DatabaseDescriptor;
-import static org.apache.cassandra.tools.ToolsTester.sstableDirName;
+import static org.apache.cassandra.tools.OfflineToolUtils.sstableDirName;
import static org.junit.Assert.*;
// LoaderOptionsTester for custom configuration
diff --git a/test/unit/org/apache/cassandra/tools/ToolsTester.java b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
similarity index 80%
rename from test/unit/org/apache/cassandra/tools/ToolsTester.java
rename to test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
index 0bb9beb..9383f7d 100644
--- a/test/unit/org/apache/cassandra/tools/ToolsTester.java
+++ b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
@@ -23,9 +23,7 @@ import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.security.Permission;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -35,20 +33,20 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
+
import org.junit.BeforeClass;
import org.slf4j.LoggerFactory;
import static org.apache.cassandra.utils.FBUtilities.preventIllegalAccessWarnings;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * Base unit test class for standalone tools
+ * Helper class for running a tool and doing in-process checks
*/
-public abstract class ToolsTester
+public abstract class OfflineToolUtils
{
static
{
@@ -193,69 +191,13 @@ public abstract class ToolsTester
fail(clazz + " has not been loaded");
}
- public void runTool(int expectedExitCode, String clazz, String... args)
- {
- try
- {
- // install security manager to get informed about the exit-code
- System.setSecurityManager(new SecurityManager()
- {
- public void checkExit(int status)
- {
- throw new SystemExitException(status);
- }
-
- public void checkPermission(Permission perm)
- {
- }
-
- public void checkPermission(Permission perm, Object context)
- {
- }
- });
-
- try
- {
- Class.forName(clazz).getDeclaredMethod("main", String[].class).invoke(null, (Object) args);
- }
- catch (InvocationTargetException e)
- {
- Throwable cause = e.getCause();
- if (cause instanceof Error)
- throw (Error) cause;
- if (cause instanceof RuntimeException)
- throw (RuntimeException) cause;
- throw e;
- }
-
- assertEquals("Unexpected exit code", expectedExitCode, 0);
- }
- catch (SystemExitException e)
- {
- assertEquals("Unexpected exit code", expectedExitCode, e.status);
- }
- catch (InvocationTargetException e)
- {
- throw new RuntimeException(e.getTargetException());
- }
- catch (Exception e)
- {
- throw new RuntimeException(e);
- }
- finally
- {
- // uninstall security manager
- System.setSecurityManager(null);
- }
- }
-
@BeforeClass
public static void setupTester()
{
System.setProperty("cassandra.partitioner", "org.apache.cassandra.dht.Murmur3Partitioner");
// may start an async appender
- LoggerFactory.getLogger(ToolsTester.class);
+ LoggerFactory.getLogger(OfflineToolUtils.class);
ThreadMXBean threads = ManagementFactory.getThreadMXBean();
initialThreads = Arrays.asList(threads.getThreadInfo(threads.getAllThreadIds()));
diff --git a/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java b/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java
index 1c09174..ad2dc3e 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class SSTableExpiredBlockersTest extends ToolsTester
+public class SSTableExpiredBlockersTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testSSTableExpiredBlockers_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.SSTableExpiredBlockers");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExpiredBlockers").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -42,7 +46,7 @@ public class SSTableExpiredBlockersTest extends ToolsTester
public void testSSTableExpiredBlockers_WithArgs()
{
// returns exit code 1, since no sstables are there
- runTool(1, "org.apache.cassandra.tools.SSTableExpiredBlockers", "system_schema", "tables");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExpiredBlockers", "system_schema", "tables").getExitCode());
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/SSTableExportTest.java b/test/unit/org/apache/cassandra/tools/SSTableExportTest.java
index 0e49bb5..54af60c 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableExportTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableExportTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class SSTableExportTest extends ToolsTester
+public class SSTableExportTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testSSTableExport_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.SSTableExport");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExport").getExitCode());
assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class SSTableExportTest extends ToolsTester
@Test
public void testSSTableExport_WithArgs() throws Exception
{
- runTool(0, "org.apache.cassandra.tools.SSTableExport", findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExport",findOneSSTable("legacy_sstables", "legacy_ma_simple"))
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java b/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java
index b70fcef..947a988 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class SSTableLevelResetterTest extends ToolsTester
+public class SSTableLevelResetterTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testSSTableLevelResetter_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.SSTableLevelResetter");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableLevelResetter").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class SSTableLevelResetterTest extends ToolsTester
@Test
public void testSSTableLevelResetter_WithArgs()
{
- runTool(0, "org.apache.cassandra.tools.SSTableLevelResetter", "--really-reset", "system_schema", "tables");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableLevelResetter", "--really-reset", "system_schema", "tables")
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java b/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java
index 8b782e3..9370315 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class SSTableMetadataViewerTest extends ToolsTester
+public class SSTableMetadataViewerTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testSSTableOfflineRelevel_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.SSTableMetadataViewer");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableMetadataViewer").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,7 @@ public class SSTableMetadataViewerTest extends ToolsTester
@Test
public void testSSTableOfflineRelevel_WithArgs()
{
- runTool(0, "org.apache.cassandra.tools.SSTableMetadataViewer", "ks", "tab");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableMetadataViewer", "ks", "tab").waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/SSTableOfflineRelevelTest.java b/test/unit/org/apache/cassandra/tools/SSTableOfflineRelevelTest.java
index 0c6eecf..1d155bc 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableOfflineRelevelTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableOfflineRelevelTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class SSTableOfflineRelevelTest extends ToolsTester
+public class SSTableOfflineRelevelTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testSSTableOfflineRelevel_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.SSTableOfflineRelevel");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableOfflineRelevel").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -42,7 +46,7 @@ public class SSTableOfflineRelevelTest extends ToolsTester
public void testSSTableOfflineRelevel_WithArgs()
{
// Note: SSTableOfflineRelevel exits with code 1 if no sstables to relevel have been found
- runTool(1, "org.apache.cassandra.tools.SSTableOfflineRelevel", "system_schema", "tables");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableOfflineRelevel", "system_schema", "tables").getExitCode());
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java b/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java
index 9dece5a..737b0eb 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class SSTableRepairedAtSetterTest extends ToolsTester
+public class SSTableRepairedAtSetterTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testSSTableRepairedAtSetter_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.SSTableRepairedAtSetter");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableRepairedAtSetter").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class SSTableRepairedAtSetterTest extends ToolsTester
@Test
public void testSSTableRepairedAtSetter_WithArgs() throws Exception
{
- runTool(0, "org.apache.cassandra.tools.SSTableRepairedAtSetter", "--really-set", "--is-repaired", findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableRepairedAtSetter", "--really-set", "--is-repaired", findOneSSTable("legacy_sstables", "legacy_ma_simple"))
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneSSTableUtilTest.java b/test/unit/org/apache/cassandra/tools/StandaloneSSTableUtilTest.java
index 2026a79..834f537 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneSSTableUtilTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneSSTableUtilTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class StandaloneSSTableUtilTest extends ToolsTester
+public class StandaloneSSTableUtilTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testStandaloneSSTableUtil_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.StandaloneSSTableUtil");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneSSTableUtil").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class StandaloneSSTableUtilTest extends ToolsTester
@Test
public void testStandaloneSSTableUtil_WithArgs()
{
- runTool(0, "org.apache.cassandra.tools.StandaloneSSTableUtil", "--debug", "-c", "system_schema", "tables");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneSSTableUtil", "--debug", "-c", "system_schema", "tables")
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java b/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java
index 6aef5eb..a99f657 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class StandaloneScrubberTest extends ToolsTester
+public class StandaloneScrubberTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testStandaloneScrubber_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.StandaloneScrubber");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneScrubber").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class StandaloneScrubberTest extends ToolsTester
@Test
public void testStandaloneScrubber_WithArgs()
{
- runTool(0, "org.apache.cassandra.tools.StandaloneScrubber", "--debug", "system_schema", "tables");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneScrubber", "--debug", "system_schema", "tables")
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java b/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java
index c0f8593..753a258 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java
@@ -24,9 +24,13 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class StandaloneSplitterTest extends ToolsTester
+public class StandaloneSplitterTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@BeforeClass
public static void before()
{
@@ -39,7 +43,7 @@ public class StandaloneSplitterTest extends ToolsTester
@Test
public void testStandaloneSplitter_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.StandaloneSplitter");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneSplitter").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java b/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java
index f703a49..1bfbbd2 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class StandaloneUpgraderTest extends ToolsTester
+public class StandaloneUpgraderTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testStandaloneUpgrader_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.StandaloneUpgrader");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneUpgrader").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class StandaloneUpgraderTest extends ToolsTester
@Test
public void testStandaloneUpgrader_WithArgs()
{
- runTool(0, "org.apache.cassandra.tools.StandaloneUpgrader", "--debug", "system_schema", "tables");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneUpgrader", "--debug", "system_schema", "tables")
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
index 38418f3..e33a154 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
@@ -23,13 +23,17 @@ import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import static org.junit.Assert.assertEquals;
+
@RunWith(OrderedJUnit4ClassRunner.class)
-public class StandaloneVerifierTest extends ToolsTester
+public class StandaloneVerifierTest extends OfflineToolUtils
{
+ private ToolRunner.Runners runner = new ToolRunner.Runners();
+
@Test
public void testStandaloneVerifier_NoArgs()
{
- runTool(1, "org.apache.cassandra.tools.StandaloneVerifier");
+ assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneVerifier").getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -41,7 +45,8 @@ public class StandaloneVerifierTest extends ToolsTester
@Test
public void testStandaloneVerifier_WithArgs()
{
- runTool(0, "org.apache.cassandra.tools.StandaloneVerifier", "--debug", "system_schema", "tables");
+ runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneVerifier", "--debug", "system_schema", "tables")
+ .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/ToolRunner.java b/test/unit/org/apache/cassandra/tools/ToolRunner.java
new file mode 100644
index 0000000..61e32b0
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/ToolRunner.java
@@ -0,0 +1,437 @@
+/*
+ * 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.cassandra.tools;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import com.google.common.base.Preconditions;
+
+import org.apache.commons.io.IOUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.OfflineToolUtils.SystemExitException;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class ToolRunner implements AutoCloseable
+{
+ protected static final Logger logger = LoggerFactory.getLogger(ToolRunner.class);
+
+ private final List<String> allArgs = new ArrayList<>();
+ private Process process;
+ private final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+ private final ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
+ private InputStream stdin;
+ private boolean stdinAutoClose;
+ private long defaultTimeoutMillis = TimeUnit.SECONDS.toMillis(30);
+ private Thread ioWatcher;
+ private Map<String, String> envs;
+ private boolean runOutOfProcess = true;
+
+ public ToolRunner(List<String> args)
+ {
+ this.allArgs.addAll(args);
+ }
+
+ public ToolRunner(List<String> args, boolean runOutOfProcess)
+ {
+ this.allArgs.addAll(args);
+ this.runOutOfProcess = runOutOfProcess;
+ }
+
+ public ToolRunner withStdin(InputStream stdin, boolean autoClose)
+ {
+ this.stdin = stdin;
+ this.stdinAutoClose = autoClose;
+ return this;
+ }
+
+ public ToolRunner withEnvs(Map<String, String> envs)
+ {
+ Preconditions.checkArgument(runOutOfProcess, "Not supported");
+ this.envs = envs;
+ return this;
+ }
+
+ public ToolRunner start()
+ {
+ if (process != null)
+ throw new IllegalStateException("Process already started. Create a new ToolRunner instance for each invocation.");
+
+ logger.debug("Starting {} with args {}", runOutOfProcess ? "process" : "class" , argsToLogString());
+
+ try
+ {
+ if (runOutOfProcess)
+ {
+ ProcessBuilder pb = new ProcessBuilder(allArgs);
+ if (envs != null)
+ pb.environment().putAll(envs);
+ process = pb.start();
+ }
+ else
+ {
+ PrintStream originalSysOut = System.out;
+ PrintStream originalSysErr = System.err;
+ InputStream originalSysIn = System.in;
+ originalSysOut.flush();
+ originalSysErr.flush();
+ ByteArrayOutputStream toolOut = new ByteArrayOutputStream();
+ ByteArrayOutputStream toolErr = new ByteArrayOutputStream();
+
+ System.setIn(stdin == null ? originalSysIn : stdin);
+ int exit = 0;
+ try (PrintStream newOut = new PrintStream(toolOut); PrintStream newErr = new PrintStream(toolErr);)
+ {
+ System.setOut(newOut);
+ System.setErr(newErr);
+ String clazz = allArgs.get(0);
+ String[] clazzArgs = allArgs.subList(1, allArgs.size()).toArray(new String[0]);
+ exit = runClassAsTool(clazz, clazzArgs);
+ }
+
+ final int exitCode = exit;
+ System.setOut(originalSysOut);
+ System.setErr(originalSysErr);
+ System.setIn(originalSysIn);
+
+ process = new Process() {
+
+ @Override
+ public void destroy()
+ {
+ }
+
+ @Override
+ public int exitValue()
+ {
+ return exitCode;
+ }
+
+ @Override
+ public InputStream getErrorStream()
+ {
+ return new ByteArrayInputStream(toolErr.toByteArray());
+ }
+
+ @Override
+ public InputStream getInputStream()
+ {
+ return new ByteArrayInputStream(toolOut.toByteArray());
+ }
+
+ @Override
+ public OutputStream getOutputStream()
+ {
+ if (stdin == null)
+ return null;
+
+ ByteArrayOutputStream out = null;
+ try
+ {
+ out = new ByteArrayOutputStream(stdin.available());
+ IOUtils.copy(stdin, out);
+ }
+ catch(IOException e)
+ {
+ throw new RuntimeException("Failed to get stdin", e);
+ }
+ return out;
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException
+ {
+ return exitValue();
+ }
+
+ };
+ }
+
+ ioWatcher = new Thread(this::watchIO);
+ ioWatcher.setDaemon(true);
+ ioWatcher.start();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Failed to start " + allArgs, e);
+ }
+
+ return this;
+ }
+
+ private void watchIO()
+ {
+ OutputStream in = process.getOutputStream();
+ InputStream err = process.getErrorStream();
+ InputStream out = process.getInputStream();
+ while (true)
+ {
+ boolean errHandled;
+ boolean outHandled;
+ try
+ {
+ if (stdin != null)
+ {
+ IOUtils.copy(stdin, in);
+ if (stdinAutoClose)
+ {
+ in.close();
+ stdin = null;
+ }
+ }
+ errHandled = IOUtils.copy(err, errBuffer) > 0;
+ outHandled = IOUtils.copy(out, outBuffer) > 0;
+ }
+ catch(IOException e1)
+ {
+ logger.error("Error trying to use in/err/out from process");
+ Thread.currentThread().interrupt();
+ break;
+ }
+ if (!errHandled && !outHandled)
+ {
+ if (!process.isAlive())
+ return;
+ try
+ {
+ Thread.sleep(50L);
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ }
+ }
+
+ public int runClassAsTool(String clazz, String... args)
+ {
+ try
+ {
+ // install security manager to get informed about the exit-code
+ System.setSecurityManager(new SecurityManager()
+ {
+ public void checkExit(int status)
+ {
+ throw new SystemExitException(status);
+ }
+
+ public void checkPermission(Permission perm)
+ {
+ }
+
+ public void checkPermission(Permission perm, Object context)
+ {
+ }
+ });
+
+ try
+ {
+ Class.forName(clazz).getDeclaredMethod("main", String[].class).invoke(null, (Object) args);
+ }
+ catch (InvocationTargetException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof Error)
+ throw (Error) cause;
+ if (cause instanceof RuntimeException)
+ throw (RuntimeException) cause;
+ throw e;
+ }
+
+ return 0;
+ }
+ catch (SystemExitException e)
+ {
+ return e.status;
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new RuntimeException(e.getTargetException());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ finally
+ {
+ // uninstall security manager
+ System.setSecurityManager(null);
+ }
+ }
+
+ public boolean isRunning()
+ {
+ return process != null && process.isAlive();
+ }
+
+ public boolean waitFor()
+ {
+ return waitFor(defaultTimeoutMillis, TimeUnit.MILLISECONDS);
+ }
+
+ public boolean waitFor(long time, TimeUnit timeUnit)
+ {
+ try
+ {
+ if (!process.waitFor(time, timeUnit))
+ return false;
+ ioWatcher.join();
+ return true;
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public ToolRunner waitAndAssertOnExitCode()
+ {
+ assertTrue(String.format("Tool %s didn't terminate",
+ argsToLogString()),
+ waitFor());
+ return assertOnExitCode();
+ }
+
+ public ToolRunner waitAndAssertOnCleanExit()
+ {
+ return waitAndAssertOnExitCode().assertEmptyStdErr();
+ }
+
+ public ToolRunner assertEmptyStdErr()
+ {
+ assertTrue(getStderr().isEmpty());
+ return this;
+ }
+
+ public ToolRunner assertOnExitCode()
+ {
+ int code = getExitCode();
+ if (code != 0)
+ fail(String.format("%s%nexited with code %d%nstderr:%n%s%nstdout:%n%s",
+ argsToLogString(),
+ code,
+ getStderr(),
+ getStdout()));
+ return this;
+ }
+
+ public String argsToLogString()
+ {
+ return allArgs.stream().collect(Collectors.joining(",\n ", "[", "]"));
+ }
+
+ public int getExitCode()
+ {
+ return process.exitValue();
+ }
+
+ public String getStdout()
+ {
+ return outBuffer.toString();
+ }
+
+ public String getStderr()
+ {
+ return errBuffer.toString();
+ }
+
+ public void forceKill()
+ {
+ try
+ {
+ process.exitValue();
+ // process no longer alive - just ignore that fact
+ }
+ catch (IllegalThreadStateException e)
+ {
+ process.destroyForcibly();
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ forceKill();
+ }
+
+ static class Runners
+ {
+ protected ToolRunner invokeNodetool(String... args)
+ {
+ return invokeNodetool(Arrays.asList(args));
+ }
+
+ protected ToolRunner invokeNodetool(List<String> args)
+ {
+ return invokeTool(buildNodetoolArgs(args), true);
+ }
+
+ private static List<String> buildNodetoolArgs(List<String> args)
+ {
+ return CQLTester.buildNodetoolArgs(args);
+ }
+
+ protected ToolRunner invokeClassAsTool(String... args)
+ {
+ return invokeClassAsTool(Arrays.asList(args));
+ }
+
+ protected ToolRunner invokeClassAsTool(List<String> args)
+ {
+ return invokeTool(args, false);
+ }
+
+ protected ToolRunner invokeTool(String... args)
+ {
+ return invokeTool(Arrays.asList(args));
+ }
+
+ protected ToolRunner invokeTool(List<String> args)
+ {
+ return invokeTool(args, true);
+ }
+
+ protected ToolRunner invokeTool(List<String> args, boolean runOutOfProcess)
+ {
+ ToolRunner runner = new ToolRunner(args, runOutOfProcess);
+ runner.start().waitFor();
+ return runner;
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org