You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by dc...@apache.org on 2020/09/30 22:03:44 UTC
[cassandra] branch trunk updated: Add UX tests to intree LHF tooling
This is an automated email from the ASF dual-hosted git repository.
dcapwell 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 9802a70 Add UX tests to intree LHF tooling
9802a70 is described below
commit 9802a70f68cdcd239a9128f90f5d8f9a941168de
Author: Berenguer Blasi <be...@gmail.com>
AuthorDate: Wed Sep 30 12:13:15 2020 -0700
Add UX tests to intree LHF tooling
patch by Berenguer Blasi; reviewed by Brandon Williams, David Capwell for CASSANDRA-15991
---
.../org/apache/cassandra/tools/SSTableExport.java | 2 -
.../cassandra/tools/SSTableMetadataViewer.java | 1 -
.../cassandra/tools/StandaloneSSTableUtil.java | 4 +-
.../test/FqlReplayDDLExclusionTest.java | 27 +-
test/unit/org/apache/cassandra/cql3/CQLTester.java | 30 +-
.../apache/cassandra/tools/AuditLogViewerTest.java | 93 +++
.../org/apache/cassandra/tools/BulkLoaderTest.java | 120 ++--
.../apache/cassandra/tools/ClearSnapshotTest.java | 41 +-
.../cassandra/tools/CompactionStressTest.java | 47 +-
.../cassandra/tools/GetFullQueryLogTest.java | 36 +-
.../org/apache/cassandra/tools/GetVersionTest.java | 6 +-
.../cassandra/tools/JMXCompatabilityTest.java | 9 +-
.../org/apache/cassandra/tools/JMXToolTest.java | 17 +-
.../apache/cassandra/tools/OfflineToolUtils.java | 9 +-
.../tools/SSTableExpiredBlockersTest.java | 45 +-
.../apache/cassandra/tools/SSTableExportTest.java | 151 ++++-
.../cassandra/tools/SSTableLevelResetterTest.java | 55 +-
.../cassandra/tools/SSTableMetadataViewerTest.java | 139 ++++-
.../cassandra/tools/SSTableOfflineRelevelTest.java | 45 +-
.../tools/SSTableRepairedAtSetterTest.java | 84 ++-
.../cassandra/tools/StandaloneSSTableUtilTest.java | 136 +++-
.../cassandra/tools/StandaloneScrubberTest.java | 152 ++++-
.../cassandra/tools/StandaloneSplitterTest.java | 100 ++-
.../cassandra/tools/StandaloneUpgraderTest.java | 86 ++-
.../cassandra/tools/StandaloneVerifierTest.java | 141 ++++-
.../org/apache/cassandra/tools/ToolRunner.java | 693 ++++++++++++---------
...tVersionTest.java => ToolsEnvsConfigsTest.java} | 30 +-
.../tools/cassandrastress/CassandrastressTest.java | 50 ++
.../{GetVersionTest.java => cqlsh/CqlshTest.java} | 35 +-
29 files changed, 1801 insertions(+), 583 deletions(-)
diff --git a/src/java/org/apache/cassandra/tools/SSTableExport.java b/src/java/org/apache/cassandra/tools/SSTableExport.java
index 394f4b6..ca01cc3 100644
--- a/src/java/org/apache/cassandra/tools/SSTableExport.java
+++ b/src/java/org/apache/cassandra/tools/SSTableExport.java
@@ -46,8 +46,6 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
-import org.apache.cassandra.io.sstable.metadata.MetadataComponent;
-import org.apache.cassandra.io.sstable.metadata.MetadataType;
import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.utils.FBUtilities;
diff --git a/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java b/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
index e99f454..a4da97c 100755
--- a/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
+++ b/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
@@ -73,7 +73,6 @@ import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import com.google.common.collect.MinMaxPriorityQueue;
-import org.apache.commons.lang3.time.DurationFormatUtils;
/**
* Shows the contents of sstable metadata
diff --git a/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java b/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java
index 9a7847a..cca48fc 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.tools;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.schema.Schema;
-import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Directories;
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
import org.apache.cassandra.utils.OutputHandler;
@@ -28,7 +27,6 @@ import org.apache.commons.cli.*;
import java.io.File;
import java.io.IOException;
-import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import static org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
@@ -62,7 +60,7 @@ public class StandaloneSSTableUtil
if (options.cleanup)
{
- handler.output("Cleanuping up...");
+ handler.output("Cleaning up...");
LifecycleTransaction.removeUnfinishedLeftovers(metadata);
}
else
diff --git a/test/distributed/org/apache/cassandra/distributed/test/FqlReplayDDLExclusionTest.java b/test/distributed/org/apache/cassandra/distributed/test/FqlReplayDDLExclusionTest.java
index 1f53b98..f117c51 100644
--- a/test/distributed/org/apache/cassandra/distributed/test/FqlReplayDDLExclusionTest.java
+++ b/test/distributed/org/apache/cassandra/distributed/test/FqlReplayDDLExclusionTest.java
@@ -27,6 +27,7 @@ import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.IInvokableInstance;
import org.apache.cassandra.distributed.api.QueryResults;
import org.apache.cassandra.tools.ToolRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
import static org.apache.cassandra.distributed.api.Feature.NATIVE_PROTOCOL;
@@ -68,14 +69,12 @@ public class FqlReplayDDLExclusionTest extends TestBaseImpl
node.executeInternal("DROP TABLE fql_ks.fql_table;");
- final ToolRunner.Runners runners = new ToolRunner.Runners();
-
// without --replay-ddl-statements, the replay will fail on insert because underlying table is not there
- final ToolRunner negativeRunner = runners.invokeClassAsTool("org.apache.cassandra.fqltool.FullQueryLogTool",
- "replay",
- "--keyspace", "fql_ks",
- "--target", "127.0.0.1",
- "--", temporaryFolder.getRoot().getAbsolutePath());
+ final ToolResult negativeRunner = ToolRunner.invokeClass("org.apache.cassandra.fqltool.FullQueryLogTool",
+ "replay",
+ "--keyspace", "fql_ks",
+ "--target", "127.0.0.1",
+ "--", temporaryFolder.getRoot().getAbsolutePath());
assertEquals(0, negativeRunner.getExitCode());
@@ -90,13 +89,13 @@ public class FqlReplayDDLExclusionTest extends TestBaseImpl
}
// here we replay with --replay-ddl-statements so table will be created and insert will succeed
- final ToolRunner positiveRunner = runners.invokeClassAsTool("org.apache.cassandra.fqltool.FullQueryLogTool",
- "replay",
- "--keyspace", "fql_ks",
- "--target", "127.0.0.1",
- // important
- "--replay-ddl-statements",
- "--", temporaryFolder.getRoot().getAbsolutePath());
+ final ToolResult positiveRunner = ToolRunner.invokeClass("org.apache.cassandra.fqltool.FullQueryLogTool",
+ "replay",
+ "--keyspace", "fql_ks",
+ "--target", "127.0.0.1",
+ // important
+ "--replay-ddl-statements",
+ "--", temporaryFolder.getRoot().getAbsolutePath());
assertEquals(0, positiveRunner.getExitCode());
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index 4392236..3e2f220 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -464,7 +464,7 @@ public abstract class CQLTester
}
});
}
-
+
public static List<String> buildNodetoolArgs(List<String> args)
{
List<String> allArgs = new ArrayList<>();
@@ -472,11 +472,35 @@ public abstract class CQLTester
allArgs.add("-p");
allArgs.add(Integer.toString(jmxPort));
allArgs.add("-h");
- allArgs.add(jmxHost);
+ allArgs.add(jmxHost == null ? "127.0.0.1" : jmxHost);
allArgs.addAll(args);
return allArgs;
}
-
+
+ public static List<String> buildCqlshArgs(List<String> args)
+ {
+ List<String> allArgs = new ArrayList<>();
+ allArgs.add("bin/cqlsh");
+ allArgs.add(nativeAddr.getHostAddress());
+ allArgs.add(Integer.toString(nativePort));
+ allArgs.add("-e");
+ allArgs.addAll(args);
+ return allArgs;
+ }
+
+ public static List<String> buildCassandraStressArgs(List<String> args)
+ {
+ List<String> allArgs = new ArrayList<>();
+ allArgs.add("tools/bin/cassandra-stress");
+ allArgs.addAll(args);
+ if (args.indexOf("-port") == -1)
+ {
+ allArgs.add("-port");
+ allArgs.add("native=" + Integer.toString(nativePort));
+ }
+ 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/AuditLogViewerTest.java b/test/unit/org/apache/cassandra/tools/AuditLogViewerTest.java
index 649712a..ed23088 100644
--- a/test/unit/org/apache/cassandra/tools/AuditLogViewerTest.java
+++ b/test/unit/org/apache/cassandra/tools/AuditLogViewerTest.java
@@ -22,10 +22,14 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
import org.apache.commons.io.FileUtils;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -38,12 +42,19 @@ import net.openhft.chronicle.queue.ExcerptAppender;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.wire.WireOut;
import org.apache.cassandra.audit.BinAuditLogger;
+import org.apache.cassandra.tools.ToolRunner.ObservableTool;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class AuditLogViewerTest
{
private Path path;
+ private final String toolPath = "tools/bin/auditlogviewer";
@Before
public void setUp() throws IOException
@@ -62,6 +73,88 @@ public class AuditLogViewerTest
}
@Test
+ public void testNoArgs()
+ {
+ ToolResult tool = ToolRunner.invoke(toolPath);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Audit log files directory path is a required argument."));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testMaybeChangeDocs()
+ {
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invoke(toolPath, "-h");
+ String help = "usage: auditlogviewer <path1> [<path2>...<pathN>] [options]\n" +
+ "--\n" +
+ "View the audit log contents in human readable format\n" +
+ "--\n" +
+ "Options are:\n" +
+ " -f,--follow Upon reacahing the end of the log continue\n" +
+ " indefinitely waiting for more records\n" +
+ " -h,--help display this help message\n" +
+ " -i,--ignore Silently ignore unsupported records\n" +
+ " -r,--roll_cycle <arg> How often to roll the log file was rolled. May be\n" +
+ " necessary for Chronicle to correctly parse file names. (MINUTELY, HOURLY,\n" +
+ " DAILY). Default HOURLY.\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testHelpArg()
+ {
+ Arrays.asList("-h", "--help").forEach(arg -> {
+ ToolResult tool = ToolRunner.invoke(toolPath, arg);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertTrue(tool.getCleanedStderr(),tool.getCleanedStderr().isEmpty());
+ tool.assertOnExitCode();
+ });
+ }
+
+ @Test
+ public void testIgnoreArg()
+ {
+ Arrays.asList("-i", "--ignore").forEach(arg -> {
+ ToolResult tool = ToolRunner.invoke(toolPath, path.toAbsolutePath().toString(), arg);
+ assertTrue(tool.getStdout(), tool.getStdout().isEmpty());
+ // @IgnoreAssert see CASSANDRA-16021
+// assertTrue(tool.getCleanedStderr(),
+// tool.getCleanedStderr().isEmpty() // j8 is fine
+// || tool.getCleanedStderr().startsWith("WARNING: An illegal reflective access operation has occurred")); //j11 throws an error
+ tool.assertOnExitCode();
+ });
+ }
+
+ @Test
+ public void testFollowNRollArgs()
+ {
+ Lists.cartesianProduct(Arrays.asList("-f", "--follow"), Arrays.asList("-r", "--roll_cycle")).forEach(arg -> {
+ try (ObservableTool tool = ToolRunner.invokeAsync(toolPath,
+ path.toAbsolutePath().toString(),
+ arg.get(0),
+ arg.get(1),
+ "TEST_SECONDLY");)
+ {
+ // Tool is running in the background 'following' so wait and then we have to kill it
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch(InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ assertTrue(tool.getPartialStdout(), tool.getPartialStdout().isEmpty());
+ // @IgnoreAssert see CASSANDRA-16021
+ // assertTrue(tool.getCleanedStderr(),
+ // tool.getCleanedStderr().isEmpty() // j8 is fine
+ // || tool.getCleanedStderr().startsWith("WARNING: An illegal reflective access operation has occurred")); //j11 throws an error
+ }
+ });
+ }
+
+ @Test
public void testDisplayRecord()
{
List<String> records = new ArrayList<>();
diff --git a/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java b/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java
index 354511a..382f352 100644
--- a/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java
+++ b/test/unit/org/apache/cassandra/tools/BulkLoaderTest.java
@@ -23,22 +23,22 @@ import org.junit.runner.RunWith;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@RunWith(OrderedJUnit4ClassRunner.class)
public class BulkLoaderTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
public void testBulkLoader_NoArgs() throws Exception
{
- ToolRunner tool = runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader");
+ ToolResult tool = ToolRunner.invokeClass(BulkLoader.class);
assertEquals(1, tool.getExitCode());
- assertTrue(!tool.getStderr().isEmpty());
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsString("Missing sstable directory argument"));
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
@@ -51,19 +51,17 @@ public class BulkLoaderTest extends OfflineToolUtils
@Test
public void testBulkLoader_WithArgs() throws Exception
{
- try
- {
- runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"))
- .waitAndAssertOnCleanExit();
- fail();
- }
- catch (RuntimeException e)
- {
- if (!(e.getCause() instanceof BulkLoadException))
- throw e;
- if (!(e.getCause().getCause() instanceof NoHostAvailableException))
- throw e;
- }
+ ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
+ "-d",
+ "127.9.9.1",
+ OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
+
+ assertEquals(-1, tool.getExitCode());
+ if (!(tool.getException().getCause() instanceof BulkLoadException))
+ throw tool.getException();
+ if (!(tool.getException().getCause().getCause() instanceof NoHostAvailableException))
+ throw tool.getException();
+
assertNoUnexpectedThreadsStarted(null, new String[]{"globalEventExecutor-1-1", "globalEventExecutor-1-2"});
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -75,20 +73,20 @@ public class BulkLoaderTest extends OfflineToolUtils
@Test
public void testBulkLoader_WithArgs1() throws Exception
{
- try
- {
- 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)
- {
- if (!(e.getCause() instanceof BulkLoadException))
- throw e;
- if (!(e.getCause().getCause() instanceof NoHostAvailableException))
- throw e;
- }
- assertNoUnexpectedThreadsStarted(null, new String[]{"globalEventExecutor-1-1", "globalEventExecutor-1-2"});
+ ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
+ "-d",
+ "127.9.9.1",
+ "--port",
+ "9042",
+ OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
+
+ assertEquals(-1, tool.getExitCode());
+ if (!(tool.getException().getCause() instanceof BulkLoadException))
+ throw tool.getException();
+ if (!(tool.getException().getCause().getCause() instanceof NoHostAvailableException))
+ throw tool.getException();
+
+ assertNoUnexpectedThreadsStarted(null, new String[] { "globalEventExecutor-1-1", "globalEventExecutor-1-2" });
assertSchemaNotLoaded();
assertCLSMNotLoaded();
assertSystemKSNotLoaded();
@@ -99,20 +97,20 @@ public class BulkLoaderTest extends OfflineToolUtils
@Test
public void testBulkLoader_WithArgs2() throws Exception
{
- try
- {
- 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)
- {
- if (!(e.getCause() instanceof BulkLoadException))
- throw e;
- if (!(e.getCause().getCause() instanceof NoHostAvailableException))
- throw e;
- }
- assertNoUnexpectedThreadsStarted(null, new String[]{"globalEventExecutor-1-1", "globalEventExecutor-1-2"});
+ ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
+ "-d",
+ "127.9.9.1:9042",
+ "--port",
+ "9041",
+ OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
+
+ assertEquals(-1, tool.getExitCode());
+ if (!(tool.getException().getCause() instanceof BulkLoadException))
+ throw tool.getException();
+ if (!(tool.getException().getCause().getCause() instanceof NoHostAvailableException))
+ throw tool.getException();
+
+ assertNoUnexpectedThreadsStarted(null, new String[] { "globalEventExecutor-1-1", "globalEventExecutor-1-2" });
assertSchemaNotLoaded();
assertCLSMNotLoaded();
assertSystemKSNotLoaded();
@@ -123,26 +121,24 @@ public class BulkLoaderTest extends OfflineToolUtils
@Test(expected = NoHostAvailableException.class)
public void testBulkLoader_WithArgs3() throws Throwable
{
- try
- {
- runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1", "--port", "9041", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
- }
- catch (RuntimeException e)
- {
- throw e.getCause().getCause();
- }
+ ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
+ "-d",
+ "127.9.9.1",
+ "--port",
+ "9041",
+ OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ assertEquals(-1, tool.getExitCode());
+ throw tool.getException().getCause().getCause();
}
@Test(expected = NoHostAvailableException.class)
public void testBulkLoader_WithArgs4() throws Throwable
{
- try
- {
- runner.invokeClassAsTool("org.apache.cassandra.tools.BulkLoader", "-d", "127.9.9.1:9041", OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
- }
- catch (RuntimeException e)
- {
- throw e.getCause().getCause();
- }
+ ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
+ "-d",
+ "127.9.9.1:9041",
+ OfflineToolUtils.sstableDirName("legacy_sstables", "legacy_ma_simple"));
+ assertEquals(-1, tool.getExitCode());
+ throw tool.getException().getCause().getCause();
}
}
diff --git a/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java b/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java
index 7e70467..b631822 100644
--- a/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java
+++ b/test/unit/org/apache/cassandra/tools/ClearSnapshotTest.java
@@ -29,14 +29,16 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class ClearSnapshotTest extends CQLTester
{
private static NodeProbe probe;
- private ToolRunner.Runners runner = new ToolRunner.Runners();
@BeforeClass
public static void setup() throws Exception
@@ -52,33 +54,36 @@ public class ClearSnapshotTest extends CQLTester
}
@Test
- public void testClearSnapshot_NoArgs() throws IOException
+ public void testClearSnapshot_NoArgs()
{
- ToolRunner tool = runner.invokeNodetool("clearsnapshot");
+ ToolResult tool = ToolRunner.invokeNodetool("clearsnapshot");
assertEquals(2, tool.getExitCode());
- assertTrue("Tool stderr: " + tool.getStderr(), tool.getStderr().contains("Specify snapshot name or --all"));
+ assertTrue("Tool stderr: " + tool.getCleanedStderr(), tool.getCleanedStderr().contains("Specify snapshot name or --all"));
- runner.invokeNodetool("clearsnapshot", "--all").waitAndAssertOnCleanExit();
+ tool = ToolRunner.invokeNodetool("clearsnapshot", "--all");
+ tool.assertOnCleanExit();
}
@Test
- public void testClearSnapshot_AllAndName() throws IOException
+ public void testClearSnapshot_AllAndName()
{
- ToolRunner tool = runner.invokeNodetool("clearsnapshot", "-t", "some-name", "--all");
+ ToolResult tool = ToolRunner.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"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Specify only one of snapshot name or --all"));
}
@Test
- public void testClearSnapshot_RemoveByName() throws IOException
+ public void testClearSnapshot_RemoveByName()
{
- ToolRunner tool = runner.invokeNodetool("snapshot","-t","some-name").waitAndAssertOnCleanExit();
+ ToolResult tool = ToolRunner.invokeNodetool("snapshot","-t","some-name");
+ tool.assertOnCleanExit();
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();
+ tool = ToolRunner.invokeNodetool("clearsnapshot","-t","some-name");
+ tool.assertOnCleanExit();
assertTrue(!tool.getStdout().isEmpty());
Map<String, TabularData> snapshots_after = probe.getSnapshotDetails();
@@ -86,17 +91,21 @@ public class ClearSnapshotTest extends CQLTester
}
@Test
- public void testClearSnapshot_RemoveMultiple() throws IOException
+ public void testClearSnapshot_RemoveMultiple()
{
- ToolRunner tool = runner.invokeNodetool("snapshot","-t","some-name").waitAndAssertOnCleanExit();
- assertTrue(!tool.getStdout().isEmpty());
- tool = runner.invokeNodetool("snapshot","-t","some-other-name").waitAndAssertOnCleanExit();
+ ToolResult tool = ToolRunner.invokeNodetool("snapshot","-t","some-name");
+ tool.assertOnCleanExit();
assertTrue(!tool.getStdout().isEmpty());
+
+ tool = ToolRunner.invokeNodetool("snapshot","-t","some-other-name");
+ tool.assertOnCleanExit();
+ assertTrue(!tool.getStdout().isEmpty());
Map<String, TabularData> snapshots_before = probe.getSnapshotDetails();
Assert.assertTrue(snapshots_before.size() == 2);
- tool = runner.invokeNodetool("clearsnapshot","--all").waitAndAssertOnCleanExit();
+ tool = ToolRunner.invokeNodetool("clearsnapshot","--all");
+ tool.assertOnCleanExit();
assertTrue(!tool.getStdout().isEmpty());
Map<String, TabularData> snapshots_after = probe.getSnapshotDetails();
diff --git a/test/unit/org/apache/cassandra/tools/CompactionStressTest.java b/test/unit/org/apache/cassandra/tools/CompactionStressTest.java
index 651e24d..09b82fe 100644
--- a/test/unit/org/apache/cassandra/tools/CompactionStressTest.java
+++ b/test/unit/org/apache/cassandra/tools/CompactionStressTest.java
@@ -24,16 +24,16 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
@RunWith(OrderedJUnit4ClassRunner.class)
public class CompactionStressTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
public void testNoArgs()
{
- runner.invokeClassAsTool("org.apache.cassandra.stress.CompactionStress").waitAndAssertOnCleanExit();
+ ToolResult tool = ToolRunner.invokeClass("org.apache.cassandra.stress.CompactionStress");
+ tool.assertOnCleanExit();
}
@Test
@@ -43,27 +43,26 @@ public class CompactionStressTest extends OfflineToolUtils
File file = new File(classLoader.getResource("blogpost.yaml").getFile());
String profileFile = file.getAbsolutePath();
- runner.invokeClassAsTool("org.apache.cassandra.stress.CompactionStress",
- "write",
- "-d",
- "build/test/cassandra",
- "-g",
- "0",
- "-p",
- profileFile,
- "-t",
- "4")
- .waitAndAssertOnCleanExit();
+ ToolResult tool = ToolRunner.invokeClass("org.apache.cassandra.stress.CompactionStress",
+ "write",
+ "-d",
+ "build/test/cassandra",
+ "-g",
+ "0",
+ "-p",
+ profileFile,
+ "-t",
+ "4");
+ tool.assertOnCleanExit();
- runner.invokeClassAsTool("org.apache.cassandra.stress.CompactionStress",
- "compact",
- "-d",
- "build/test/cassandra",
- "-p",
- profileFile,
- "-t",
- "4")
- .waitAndAssertOnCleanExit();
+ tool = ToolRunner.invokeClass("org.apache.cassandra.stress.CompactionStress",
+ "compact",
+ "-d",
+ "build/test/cassandra",
+ "-p",
+ profileFile,
+ "-t",
+ "4");
+ tool.assertOnCleanExit();
}
-
}
diff --git a/test/unit/org/apache/cassandra/tools/GetFullQueryLogTest.java b/test/unit/org/apache/cassandra/tools/GetFullQueryLogTest.java
index 82d5482..44007a5 100644
--- a/test/unit/org/apache/cassandra/tools/GetFullQueryLogTest.java
+++ b/test/unit/org/apache/cassandra/tools/GetFullQueryLogTest.java
@@ -29,6 +29,7 @@ import org.junit.rules.TemporaryFolder;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.fql.FullQueryLoggerOptions;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -36,7 +37,6 @@ import static org.junit.Assert.assertTrue;
public class GetFullQueryLogTest extends CQLTester
{
private static NodeProbe probe;
- private ToolRunner.Runners runner = new ToolRunner.Runners();
@ClassRule
public static TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -55,7 +55,7 @@ public class GetFullQueryLogTest extends CQLTester
}
@After
- public void afterTest()
+ public void afterTest() throws InterruptedException
{
disableFullQueryLog();
}
@@ -87,29 +87,39 @@ public class GetFullQueryLogTest extends CQLTester
private String getFullQueryLog()
{
- return runner.invokeNodetool("getfullquerylog").waitAndAssertOnCleanExit().getStdout();
+ ToolResult tool = ToolRunner.invokeNodetool("getfullquerylog");
+ tool.assertOnCleanExit();
+ return tool.getStdout();
}
private void resetFullQueryLog()
{
- runner.invokeNodetool("resetfullquerylog").waitAndAssertOnCleanExit();
+ ToolRunner.invokeNodetool("resetfullquerylog").assertOnCleanExit();
}
private void disableFullQueryLog()
{
- runner.invokeNodetool("disablefullquerylog").waitAndAssertOnCleanExit();
+ ToolRunner.invokeNodetool("disablefullquerylog").assertOnCleanExit();
}
private void enableFullQueryLog()
{
- runner.invokeNodetool("enablefullquerylog",
- "--path", temporaryFolder.getRoot().toString(),
- "--blocking", "false",
- "--max-archive-retries", "5",
- "--archive-command", "/path/to/script.sh %path",
- "--max-log-size", "100000",
- "--max-queue-weight", "10000",
- "--roll-cycle", "DAILY").waitAndAssertOnCleanExit();
+ ToolRunner.invokeNodetool("enablefullquerylog",
+ "--path",
+ temporaryFolder.getRoot().toString(),
+ "--blocking",
+ "false",
+ "--max-archive-retries",
+ "5",
+ "--archive-command",
+ "/path/to/script.sh %path",
+ "--max-log-size",
+ "100000",
+ "--max-queue-weight",
+ "10000",
+ "--roll-cycle",
+ "DAILY")
+ .assertOnCleanExit();
}
private void testChangedOutput(final String getFullQueryLogOutput)
diff --git a/test/unit/org/apache/cassandra/tools/GetVersionTest.java b/test/unit/org/apache/cassandra/tools/GetVersionTest.java
index c5f5282..1feeb45 100644
--- a/test/unit/org/apache/cassandra/tools/GetVersionTest.java
+++ b/test/unit/org/apache/cassandra/tools/GetVersionTest.java
@@ -22,16 +22,16 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
@RunWith(OrderedJUnit4ClassRunner.class)
public class GetVersionTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
public void testGetVersion()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.GetVersion").waitAndAssertOnCleanExit();
+ ToolResult tool = ToolRunner.invokeClass(GetVersion.class);
+ tool.assertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/JMXCompatabilityTest.java b/test/unit/org/apache/cassandra/tools/JMXCompatabilityTest.java
index 816257e..2494dd2 100644
--- a/test/unit/org/apache/cassandra/tools/JMXCompatabilityTest.java
+++ b/test/unit/org/apache/cassandra/tools/JMXCompatabilityTest.java
@@ -13,11 +13,10 @@ import com.datastax.driver.core.SimpleStatement;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.service.CassandraDaemon;
import org.apache.cassandra.service.GCInspector;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
import org.apache.cassandra.transport.ProtocolVersion;
import org.assertj.core.api.Assertions;
-import static org.apache.cassandra.tools.ToolRunner.Runners.invokeTool;
-
/**
* This class is to monitor the JMX compatability cross different versions, and relies on a gold set of metrics which
* were generated following the instructions below. These tests only check for breaking changes, so will ignore any
@@ -66,7 +65,7 @@ public class JMXCompatabilityTest extends CQLTester
executeNet(ProtocolVersion.CURRENT, new SimpleStatement("SELECT * FROM " + name + " WHERE pk=?", 42));
String script = "tools/bin/jmxtool dump -f yaml --url service:jmx:rmi:///jndi/rmi://" + jmxHost + ":" + jmxPort + "/jmxrmi > " + TMP.getRoot().getAbsolutePath() + "/out.yaml";
- invokeTool("bash", "-c", script).assertOnExitCode().assertCleanStdErr();
+ ToolRunner.invoke("bash", "-c", script).assertOnCleanExit();
CREATED_TABLE = true;
}
@@ -164,8 +163,8 @@ public class JMXCompatabilityTest extends CQLTester
args.add("--exclude-operation");
args.add(a);
});
- ToolRunner result = invokeTool(args);
- result.assertOnExitCode().assertCleanStdErr();
+ ToolResult result = ToolRunner.invoke(args);
+ result.assertOnCleanExit();
Assertions.assertThat(result.getStdout()).isEmpty();
}
}
diff --git a/test/unit/org/apache/cassandra/tools/JMXToolTest.java b/test/unit/org/apache/cassandra/tools/JMXToolTest.java
index 229354b..4fba8cd 100644
--- a/test/unit/org/apache/cassandra/tools/JMXToolTest.java
+++ b/test/unit/org/apache/cassandra/tools/JMXToolTest.java
@@ -10,6 +10,7 @@ import org.junit.Test;
import org.apache.cassandra.io.util.DataInputBuffer;
import org.apache.cassandra.io.util.DataOutputBuffer;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
import org.apache.cassandra.utils.Generators;
import org.assertj.core.api.Assertions;
import org.quicktheories.core.Gen;
@@ -35,8 +36,8 @@ public class JMXToolTest
@Test
public void cliHelp()
{
- ToolRunner result = jmxtool();
- result.assertCleanStdErr().assertOnExitCode();
+ ToolResult result = jmxtool();
+ result.assertOnCleanExit();
Assertions.assertThat(result.getStdout())
.isEqualTo("usage: jmxtool <command> [<args>]\n" +
@@ -53,8 +54,8 @@ public class JMXToolTest
@Test
public void cliHelpDiff()
{
- ToolRunner result = jmxtool("help", "diff");
- result.assertCleanStdErr().assertOnExitCode();
+ ToolResult result = jmxtool("help", "diff");
+ result.assertOnCleanExit();
Assertions.assertThat(result.getStdout())
.isEqualTo("NAME\n" +
@@ -107,8 +108,8 @@ public class JMXToolTest
@Test
public void cliHelpDump()
{
- ToolRunner result = jmxtool("help", "dump");
- result.assertCleanStdErr().assertOnExitCode();
+ ToolResult result = jmxtool("help", "dump");
+ result.assertOnCleanExit();
Assertions.assertThat(result.getStdout())
.isEqualTo("NAME\n" +
@@ -132,12 +133,12 @@ public class JMXToolTest
"\n");
}
- private static ToolRunner jmxtool(String... args)
+ private static ToolResult jmxtool(String... args)
{
List<String> cmd = new ArrayList<>(1 + args.length);
cmd.add("tools/bin/jmxtool");
cmd.addAll(Arrays.asList(args));
- return ToolRunner.Runners.invokeTool(cmd);
+ return ToolRunner.invoke(cmd);
}
private void serde(JMXTool.Dump.Format serializer, JMXTool.Diff.Format deserializer)
diff --git a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
index 9383f7d..ae82fd8 100644
--- a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
+++ b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
@@ -110,7 +110,7 @@ public abstract class OfflineToolUtils
.filter(threadName -> optional.stream().anyMatch(pattern -> pattern.matcher(threadName).matches()))
.collect(Collectors.toSet());
- if (!current.isEmpty())
+ if (!remain.isEmpty())
System.err.println("Unexpected thread names: " + remain);
if (!notPresent.isEmpty())
System.err.println("Mandatory thread missing: " + notPresent);
@@ -240,4 +240,11 @@ public abstract class OfflineToolUtils
FileUtils.copyDirectory(new File(srcDir, "legacy_tables"), new File(dataDir, "legacy_sstables"));
return dataDir;
}
+
+ protected void assertCorrectEnvPostTest()
+ {
+ assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
+ assertSchemaLoaded();
+ assertServerNotLoaded();
+ }
}
diff --git a/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java b/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java
index ad2dc3e..0476453 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableExpiredBlockersTest.java
@@ -22,18 +22,24 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class SSTableExpiredBlockersTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testSSTableExpiredBlockers_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExpiredBlockers").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(SSTableExpiredBlockers.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +49,31 @@ public class SSTableExpiredBlockersTest extends OfflineToolUtils
}
@Test
- public void testSSTableExpiredBlockers_WithArgs()
+ public void testMaybeChangeDocs()
{
- // returns exit code 1, since no sstables are there
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExpiredBlockers", "system_schema", "tables").getExitCode());
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(SSTableExpiredBlockers.class);
+ String help = "Usage: sstableexpiredblockers <keyspace> <table>\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgsIgnored()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExpiredBlockers.class, "--debugwrong", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No sstables for"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExpiredBlockers.class, "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No sstables for"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
}
}
diff --git a/test/unit/org/apache/cassandra/tools/SSTableExportTest.java b/test/unit/org/apache/cassandra/tools/SSTableExportTest.java
index 54af60c..15949af 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableExportTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableExportTest.java
@@ -18,22 +18,39 @@
package org.apache.cassandra.tools;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
@RunWith(OrderedJUnit4ClassRunner.class)
public class SSTableExportTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
+ private ObjectMapper mapper = new ObjectMapper();
+ private TypeReference<List<Map<String, Object>>> jacksonListOfMapsType = new TypeReference<List<Map<String, Object>>>() {};
+
@Test
- public void testSSTableExport_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExport").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("You must supply exactly one sstable"));
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +60,130 @@ public class SSTableExportTest extends OfflineToolUtils
}
@Test
- public void testSSTableExport_WithArgs() throws Exception
+ public void testMaybeChangeDocs()
+ {
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class);
+ String help = "usage: sstabledump <sstable file path> <options>\n" +
+ " \n" +
+ "Dump contents of given SSTable to standard output in JSON format.\n" +
+ " -d CQL row per line internal representation\n" +
+ " -e enumerate partition keys only\n" +
+ " -k <arg> Partition key\n" +
+ " -l Output json lines, by partition\n" +
+ " -t Print raw timestamps instead of iso8601 date strings\n" +
+ " -x <arg> Excluded partition key\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, "--debugwrong", findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testDefaultCall() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class,findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ List<Map<String, Object>> parsed = mapper.readValue(tool.getStdout(), jacksonListOfMapsType);
+ assertTrue(tool.getStdout(), parsed.get(0).get("partition") != null);
+ assertTrue(tool.getStdout(), parsed.get(0).get("rows") != null);
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ @Test
+ public void testCQLRowArg() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, findOneSSTable("legacy_sstables", "legacy_ma_simple"), "-d");
+ assertThat(tool.getStdout(), CoreMatchers.startsWith("[0]"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ @Test
+ public void testPKOnlyArg() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, findOneSSTable("legacy_sstables", "legacy_ma_simple"), "-e");
+ assertEquals(tool.getStdout(), "[ [ \"0\" ], [ \"1\" ], [ \"2\" ], [ \"3\" ], [ \"4\" ]\n]", tool.getStdout());
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ @Test
+ public void testPKArg() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, findOneSSTable("legacy_sstables", "legacy_ma_simple"), "-k", "0");
+ List<Map<String, Object>> parsed = mapper.readValue(tool.getStdout(), jacksonListOfMapsType);
+ assertEquals(tool.getStdout(), 1, parsed.size());
+ assertEquals(tool.getStdout(), "0", ((List) ((Map) parsed.get(0).get("partition")).get("key")).get(0));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ @Test
+ public void testExcludePKArg() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, findOneSSTable("legacy_sstables", "legacy_ma_simple"), "-x", "0");
+ List<Map<String, Object>> parsed = mapper.readValue(tool.getStdout(), jacksonListOfMapsType);
+ assertEquals(tool.getStdout(), 4, parsed.size());
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ @Test
+ public void testTSFormatArg() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, findOneSSTable("legacy_sstables", "legacy_ma_simple"), "-t");
+ List<Map<String, Object>> parsed = mapper.readValue(tool.getStdout(), jacksonListOfMapsType);
+ assertEquals(tool.getStdout(),
+ "1445008632854000",
+ ((Map) ((List<Map>) parsed.get(0).get("rows")).get(0).get("liveness_info")).get("tstamp"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ @Test
+ public void testJSONLineArg() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableExport.class, findOneSSTable("legacy_sstables", "legacy_ma_simple"), "-l");
+ try
+ {
+ mapper.readValue(tool.getStdout(), jacksonListOfMapsType);
+ fail("Shouldn't be able to deserialize that output, now it's not a collection anymore.");
+ }
+ catch(MismatchedInputException e)
+ {
+ }
+
+ int parsedCount = 0;
+ for (String jsonLine : tool.getStdout().split("\\R"))
+ {
+ Map line = mapper.readValue(jsonLine, Map.class);
+ assertTrue(jsonLine, line.containsKey("partition"));
+ parsedCount++;
+ }
+
+ assertEquals(tool.getStdout(), 5, parsedCount);
+ assertThat(tool.getStdout(), CoreMatchers.startsWith("{\""));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertPostTestEnv();
+ }
+
+ private void assertPostTestEnv()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableExport",findOneSSTable("legacy_sstables", "legacy_ma_simple"))
- .waitAndAssertOnCleanExit();
assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaNotLoaded();
assertCLSMNotLoaded();
assertSystemKSNotLoaded();
assertServerNotLoaded();
diff --git a/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java b/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java
index 947a988..e413b14 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableLevelResetterTest.java
@@ -22,18 +22,23 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class SSTableLevelResetterTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testSSTableLevelResetter_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableLevelResetter").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(SSTableLevelResetter.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +48,42 @@ public class SSTableLevelResetterTest extends OfflineToolUtils
}
@Test
- public void testSSTableLevelResetter_WithArgs()
+ public void testMaybeChangeDocs()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableLevelResetter", "--really-reset", "system_schema", "tables")
- .waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(SSTableLevelResetter.class, "-h");
+ String help = "This command should be run with Cassandra stopped, otherwise you will get very strange behavior\n" +
+ "Verify that Cassandra is not running and then execute the command like this:\n" +
+ "Usage: sstablelevelreset --really-reset <keyspace> <table>\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableLevelResetter.class, "--debugwrong", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableLevelResetter.class, "--really-reset", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Found no sstables,"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testMissingSecurityFlagCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableLevelResetter.class, "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
}
}
diff --git a/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java b/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java
index 9370315..db0c958 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableMetadataViewerTest.java
@@ -18,22 +18,36 @@
package org.apache.cassandra.tools;
+import java.util.Arrays;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.tuple.Pair;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
@RunWith(OrderedJUnit4ClassRunner.class)
public class SSTableMetadataViewerTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testSSTableOfflineRelevel_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableMetadataViewer").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class);
+ {
+ assertTrue(tool.getStdout(), tool.getStdout().isEmpty());
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Options:"));
+ assertEquals(1, tool.getExitCode());
+ }
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,9 +57,122 @@ public class SSTableMetadataViewerTest extends OfflineToolUtils
}
@Test
- public void testSSTableOfflineRelevel_WithArgs()
+ public void testMaybeChangeDocs()
+ {
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class, "-h");
+ assertEquals("You must supply at least one sstable\n" +
+ "usage: sstablemetadata <options> <sstable...> [-c] [-g <arg>] [-s] [-t <arg>] [-u]\n" +
+ "\n" +
+ "Dump information about SSTable[s] for Apache Cassandra 3.x\n" +
+ "Options:\n" +
+ " -c,--colors Use ANSI color sequences\n" +
+ " -g,--gc_grace_seconds <arg> Time to use when calculating droppable tombstones\n" +
+ " -s,--scan Full sstable scan for additional details. Only available in 3.0+ sstables. Defaults: false\n" +
+ " -t,--timestamp_unit <arg> Time unit that cell timestamps are written with\n" +
+ " -u,--unicode Use unicode to draw histograms and progress bars\n\n"
+ , tool.getCleanedStderr());
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class, "--debugwrong", "ks", "tab");
+ assertTrue(tool.getStdout(), tool.getStdout().isEmpty());
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Options:"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class, "ks", "tab");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No such file"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertGoodEnvPostTest();
+ }
+
+ @Test
+ public void testFlagArgs()
+ {
+ Arrays.asList("-c",
+ "--colors",
+ "-s",
+ "--scan",
+ "-u",
+ "--unicode")
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class, arg, "ks", "tab");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No such file"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertGoodEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testGCArg()
+ {
+ Arrays.asList(Pair.of("-g", ""),
+ Pair.of("-g", "w"),
+ Pair.of("--gc_grace_seconds", ""),
+ Pair.of("--gc_grace_seconds", "w"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "ks",
+ "tab");
+ assertEquals(-1, tool.getExitCode());
+ Assertions.assertThat(tool.getStderr()).contains(NumberFormatException.class.getSimpleName());
+ });
+
+ Arrays.asList(Pair.of("-g", "5"), Pair.of("--gc_grace_seconds", "5")).forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "ks",
+ "tab");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No such file"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertGoodEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testTSUnitArg()
+ {
+ Arrays.asList(Pair.of("-t", ""),
+ Pair.of("-t", "w"),
+ Pair.of("--timestamp_unit", ""),
+ Pair.of("--timestamp_unit", "w"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "ks",
+ "tab");
+ assertEquals(-1, tool.getExitCode());
+ Assertions.assertThat(tool.getStderr()).contains(IllegalArgumentException.class.getSimpleName());
+ });
+
+ Arrays.asList(Pair.of("-t", "SECONDS"), Pair.of("--timestamp_unit", "SECONDS")).forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(SSTableMetadataViewer.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "ks",
+ "tab");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No such file"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertGoodEnvPostTest();
+ });
+ }
+
+ private void assertGoodEnvPostTest()
{
- 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 1d155bc..a49dd49 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableOfflineRelevelTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableOfflineRelevelTest.java
@@ -22,18 +22,23 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class SSTableOfflineRelevelTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testSSTableOfflineRelevel_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableOfflineRelevel").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(SSTableOfflineRelevel.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +48,32 @@ public class SSTableOfflineRelevelTest extends OfflineToolUtils
}
@Test
- public void testSSTableOfflineRelevel_WithArgs()
+ public void testMaybeChangeDocs()
{
- // Note: SSTableOfflineRelevel exits with code 1 if no sstables to relevel have been found
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableOfflineRelevel", "system_schema", "tables").getExitCode());
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(SSTableOfflineRelevel.class, "-h");
+ String help = "This command should be run with Cassandra stopped!\n" +
+ "Usage: sstableofflinerelevel [--dry-run] <keyspace> <columnfamily>\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableOfflineRelevel.class, "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No sstables to relevel for system_schema.tables"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testDryrunArg()
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableOfflineRelevel.class, "--dry-run", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("No sstables to relevel for system_schema.tables"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
}
}
diff --git a/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java b/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java
index 737b0eb..e1f5f95 100644
--- a/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java
+++ b/test/unit/org/apache/cassandra/tools/SSTableRepairedAtSetterTest.java
@@ -18,22 +18,33 @@
package org.apache.cassandra.tools;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.io.util.FileUtils;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class SSTableRepairedAtSetterTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testSSTableRepairedAtSetter_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableRepairedAtSetter").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,10 +54,69 @@ public class SSTableRepairedAtSetterTest extends OfflineToolUtils
}
@Test
- public void testSSTableRepairedAtSetter_WithArgs() throws Exception
+ public void testMaybeChangeDocs()
+ {
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class, "-h");
+ String help = "This command should be run with Cassandra stopped, otherwise you will get very strange behavior\n" +
+ "Verify that Cassandra is not running and then execute the command like this:\n" +
+ "Usage: sstablerepairedset --really-set [--is-repaired | --is-unrepaired] [-f <sstable-list> | <sstables>]\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp() throws IOException
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class,
+ "--debugwrong",
+ findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testIsrepairedArg() throws Exception
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class,
+ "--really-set",
+ "--is-repaired",
+ findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ tool.assertOnCleanExit();
+ assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
+ assertSchemaNotLoaded();
+ assertCLSMNotLoaded();
+ assertSystemKSNotLoaded();
+ assertKeyspaceNotLoaded();
+ assertServerNotLoaded();
+ }
+
+ @Test
+ public void testIsunrepairedArg() throws Exception
+ {
+ ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class,
+ "--really-set",
+ "--is-unrepaired",
+ findOneSSTable("legacy_sstables", "legacy_ma_simple"));
+ tool.assertOnCleanExit();
+ assertNoUnexpectedThreadsStarted(null, OPTIONAL_THREADS_WITH_SCHEMA);
+ assertSchemaNotLoaded();
+ assertCLSMNotLoaded();
+ assertSystemKSNotLoaded();
+ assertKeyspaceNotLoaded();
+ assertServerNotLoaded();
+ }
+
+ @Test
+ public void testFilesArg() throws Exception
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.SSTableRepairedAtSetter", "--really-set", "--is-repaired", findOneSSTable("legacy_sstables", "legacy_ma_simple"))
- .waitAndAssertOnCleanExit();
+ File tmpFile = FileUtils.createTempFile("sstablelist.txt", "tmp");
+ tmpFile.deleteOnExit();
+ Files.write(tmpFile.toPath(), findOneSSTable("legacy_sstables", "legacy_ma_simple").getBytes());
+
+ String file = tmpFile.getAbsolutePath();
+ ToolResult tool = ToolRunner.invokeClass(SSTableRepairedAtSetter.class, "--really-set", "--is-repaired", "-f", file);
+ tool.assertOnCleanExit();
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 834f537..460fb10 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneSSTableUtilTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneSSTableUtilTest.java
@@ -18,22 +18,31 @@
package org.apache.cassandra.tools;
+import java.util.Arrays;
+
+import org.apache.commons.lang3.tuple.Pair;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class StandaloneSSTableUtilTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testStandaloneSSTableUtil_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneSSTableUtil").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +52,119 @@ public class StandaloneSSTableUtilTest extends OfflineToolUtils
}
@Test
- public void testStandaloneSSTableUtil_WithArgs()
+ public void testWrongArgFailsAndPrintsHelp()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneSSTableUtil", "--debug", "-c", "system_schema", "tables")
- .waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class, "--debugwrong", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testMaybeChangeDocs()
+ {
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class, "-h");
+ String help = "usage: sstableutil [options] <keyspace> <column_family>\n" +
+ "--\n" +
+ "List sstable files for the provided table.\n" +
+ "--\n" +
+ "Options are:\n" +
+ " -c,--cleanup clean-up any outstanding transactions\n" +
+ " -d,--debug display stack traces\n" +
+ " -h,--help display this help message\n" +
+ " -o,--oplog include operation logs\n" +
+ " -t,--type <arg> all (list all files, final or temporary), tmp (list\n" +
+ " temporary files only), final (list final files only),\n" +
+ " -v,--verbose verbose output\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class, "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Listing files..."));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testListFilesArgs()
+ {
+ Arrays.asList("-d", "--debug", "-o", "-oplog", "-v", "--verbose").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class,
+ arg,
+ "system_schema",
+ "tables");
+ Assertions.assertThat(tool.getStdout()).as("Arg: [%s]", arg).isEqualTo("Listing files...\n");
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testCleanupArg()
+ {
+ Arrays.asList("-c", "--cleanup").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Cleaning up..."));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testHelpArg()
+ {
+ Arrays.asList("-h", "--help").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class, arg);
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testTypeArg()
+ {
+ Arrays.asList("-t", "--type").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat("Arg: [" + arg + "]", tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
+ assertEquals("Arg: [" + arg + "]", 1, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ });
+
+ //'-t wrong' renders 'all' file types
+ Arrays.asList(Pair.of("-t", "all"),
+ Pair.of("-t", "tmp"),
+ Pair.of("-t", "final"),
+ Pair.of("-t", "wrong"),
+ Pair.of("--type", "all"),
+ Pair.of("--type", "tmp"),
+ Pair.of("--type", "final"),
+ Pair.of("--type", "wrong"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSSTableUtil.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "system_schema",
+ "tables");
+ Assertions.assertThat(tool.getStdout()).as("Arg: [%s]", arg).isEqualTo("Listing files...\n");
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
}
}
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java b/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java
index a99f657..3593025 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java
@@ -18,22 +18,32 @@
package org.apache.cassandra.tools;
+import java.util.Arrays;
+
+import org.apache.commons.lang3.tuple.Pair;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
@RunWith(OrderedJUnit4ClassRunner.class)
public class StandaloneScrubberTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testStandaloneScrubber_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneScrubber").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +53,134 @@ public class StandaloneScrubberTest extends OfflineToolUtils
}
@Test
- public void testStandaloneScrubber_WithArgs()
+ public void testMaybeChangeDocs()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneScrubber", "--debug", "system_schema", "tables")
- .waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-h");
+ String help = "usage: sstablescrub [options] <keyspace> <column_family>\n" +
+ "--\n" +
+ "Scrub the sstable for the provided table.\n" +
+ "--\n" +
+ "Options are:\n" +
+ " --debug display stack traces\n" +
+ " -e,--header-fix <arg> Option whether and how to perform a check of the sstable serialization-headers and fix\n" +
+ " known, fixable issues.\n" +
+ " Possible argument values:\n" +
+ " - validate-only: validate the serialization-headers, but do not fix those. Do not continue with scrub - i.e. only\n" +
+ " validate the header (dry-run of fix-only).\n" +
+ " - validate: (default) validate the serialization-headers, but do not fix those and only continue with scrub if no error\n" +
+ " were detected.\n" +
+ " - fix-only: validate and fix the serialization-headers, don't continue with scrub.\n" +
+ " - fix: validate and fix the serialization-headers, do not fix and do not continue with scrub if the serialization-header\n" +
+ " check encountered errors.\n" +
+ " - off: don't perform the serialization-header checks.\n" +
+ " -h,--help display this help message\n" +
+ " -m,--manifest-check only check and repair the leveled manifest, without actually scrubbing the sstables\n" +
+ " -n,--no-validate do not validate columns using column validator\n" +
+ " -r,--reinsert-overflowed-ttl Rewrites rows with overflowed expiration date affected by CASSANDRA-14092 with the\n" +
+ " maximum supported expiration date of 2038-01-19T03:14:06+00:00. The rows are rewritten with the original timestamp\n" +
+ " incremented by one millisecond to override/supersede any potential tombstone that may have been generated during\n" +
+ " compaction of the affected rows.\n" +
+ " -s,--skip-corrupted skip corrupt rows in counter tables\n" +
+ " -v,--verbose verbose output\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, "--debugwrong", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Pre-scrub sstables snapshotted into snapshot"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testFlagArgs()
+ {
+ Arrays.asList("--debug",
+ "-m",
+ "--manifest-check",
+ "-n",
+ "--no-validate",
+ "-r",
+ "--reinsert-overflowed-ttl",
+ "-s",
+ "--skip-corrupted",
+ "-v",
+ "--verbose")
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Pre-scrub sstables snapshotted into snapshot"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testHelpArg()
+ {
+ Arrays.asList("-h", "--help").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, arg);
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testHeaderFixArg()
+ {
+ Arrays.asList(Pair.of("-e", ""),
+ Pair.of("-e", "wrong"),
+ Pair.of("--header-fix", ""),
+ Pair.of("--header-fix", "wrong"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "system_schema",
+ "tables");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertTrue("Arg: [" + arg + "]\n" + tool.getCleanedStderr(), tool.getCleanedStderr().contains("Invalid argument value"));
+ assertEquals(1, tool.getExitCode());
+ });
+
+ Arrays.asList(Pair.of("-e", "validate-only"),
+ Pair.of("-e", "validate"),
+ Pair.of("-e", "fix-only"),
+ Pair.of("-e", "fix"),
+ Pair.of("-e", "off"),
+ Pair.of("--header-fix", "validate-only"),
+ Pair.of("--header-fix", "validate"),
+ Pair.of("--header-fix", "fix-only"),
+ Pair.of("--header-fix", "fix"),
+ Pair.of("--header-fix", "off"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "system_schema",
+ "tables");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Pre-scrub sstables snapshotted into snapshot"));
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
}
}
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java b/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java
index 753a258..287275b 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneSplitterTest.java
@@ -18,19 +18,27 @@
package org.apache.cassandra.tools;
+import java.util.Arrays;
+
+import org.apache.commons.lang3.tuple.Pair;
+
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class StandaloneSplitterTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
+ // Note: StandaloneSplitter modifies sstables
+
@BeforeClass
public static void before()
{
@@ -41,9 +49,12 @@ public class StandaloneSplitterTest extends OfflineToolUtils
}
@Test
- public void testStandaloneSplitter_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneSplitter").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No sstables to split"));
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -52,5 +63,84 @@ public class StandaloneSplitterTest extends OfflineToolUtils
assertServerNotLoaded();
}
- // Note: StandaloneSplitter modifies sstables
+ @Test
+ public void testMaybeChangeDocs()
+ {
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class, "-h");
+ String help = "usage: sstablessplit [options] <filename> [<filename>]*\n" +
+ "--\n" +
+ "Split the provided sstables files in sstables of maximum provided file\n" +
+ "size (see option --size).\n" +
+ "--\n" +
+ "Options are:\n" +
+ " --debug display stack traces\n" +
+ " -h,--help display this help message\n" +
+ " --no-snapshot don't snapshot the sstables before splitting\n" +
+ " -s,--size <size> maximum size in MB for the output sstables (default:\n" +
+ " 50)\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class, "--debugwrong", "mockFile");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testWrongFilename()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class, "mockFile");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Skipping inexisting file mockFile"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No valid sstables to split"));
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testFlagArgs()
+ {
+ Arrays.asList("--debug", "--no-snapshot").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class, arg, "mockFile");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Skipping inexisting file mockFile"));
+ assertThat("Arg: [" + arg + "]", tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No valid sstables to split"));
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testSizeArg()
+ {
+ Arrays.asList(Pair.of("-s", ""), Pair.of("-s", "w"), Pair.of("--size", ""), Pair.of("--size", "w"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "mockFile");
+ assertEquals(-1, tool.getExitCode());
+ Assertions.assertThat(tool.getStderr()).contains(NumberFormatException.class.getSimpleName());
+ });
+
+ Arrays.asList(Pair.of("-s", "0"),
+ Pair.of("-s", "1000"),
+ Pair.of("-s", "-1"),
+ Pair.of("--size", "0"),
+ Pair.of("--size", "1000"),
+ Pair.of("--size", "-1"))
+ .forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneSplitter.class,
+ arg.getLeft(),
+ arg.getRight(),
+ "mockFile");
+ assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsStringIgnoringCase("Skipping inexisting file mockFile"));
+ assertThat("Arg: [" + arg + "]", tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No valid sstables to split"));
+ assertEquals(1, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ });
+ }
}
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java b/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java
index 1bfbbd2..3a4177b 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneUpgraderTest.java
@@ -18,22 +18,29 @@
package org.apache.cassandra.tools;
+import java.util.Arrays;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class StandaloneUpgraderTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
@Test
- public void testStandaloneUpgrader_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneUpgrader").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(StandaloneUpgrader.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +50,71 @@ public class StandaloneUpgraderTest extends OfflineToolUtils
}
@Test
- public void testStandaloneUpgrader_WithArgs()
+ public void testMaybeChangeDocs()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneUpgrader", "--debug", "system_schema", "tables")
- .waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(StandaloneUpgrader.class, "-h");
+ String help = "usage: sstableupgrade [options] <keyspace> <cf> [snapshot]\n" +
+ "--\n" +
+ "Upgrade the sstables in the given cf (or snapshot) to the current version\n" +
+ "of Cassandra.This operation will rewrite the sstables in the specified cf\n" +
+ "to match the currently installed version of Cassandra.\n" +
+ "The snapshot option will only upgrade the specified snapshot. Upgrading\n" +
+ "snapshots is required before attempting to restore a snapshot taken in a\n" +
+ "major version older than the major version Cassandra is currently running.\n" +
+ "This will replace the files in the given snapshot as well as break any\n" +
+ "hard links to live sstables.\n" +
+ "--\n" +
+ "Options are:\n" +
+ " --debug display stack traces\n" +
+ " -h,--help display this help message\n" +
+ " -k,--keep-source do not delete the source sstables\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneUpgrader.class, "--debugwrong", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneUpgrader.class, "system_schema", "tables");
+ Assertions.assertThat(tool.getStdout()).isEqualTo("Found 0 sstables that need upgrading.\n");
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testFlagArgs()
+ {
+ Arrays.asList("--debug", "-k", "--keep-source").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneUpgrader.class,
+ arg,
+ "system_schema",
+ "tables");
+ Assertions.assertThat(tool.getStdout()).as("Arg: [%s]", arg).isEqualTo("Found 0 sstables that need upgrading.\n");
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testHelpArg()
+ {
+ Arrays.asList("-h", "--help").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneUpgrader.class, arg);
+ Assertions.assertThat(tool.getStdout()).as("Arg: [%s]", arg).isNotEmpty();
+ Assertions.assertThat(tool.getCleanedStderr()).as("Arg: [%s]", arg).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
}
}
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
index e33a154..f736edd 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
@@ -18,22 +18,30 @@
package org.apache.cassandra.tools;
+import java.util.Arrays;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.assertj.core.api.Assertions;
+import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class StandaloneVerifierTest extends OfflineToolUtils
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
+
@Test
- public void testStandaloneVerifier_NoArgs()
+ public void testNoArgsPrintsHelp()
{
- assertEquals(1, runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneVerifier").getExitCode());
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Missing arguments"));
+ assertEquals(1, tool.getExitCode());
assertNoUnexpectedThreadsStarted(null, null);
assertSchemaNotLoaded();
assertCLSMNotLoaded();
@@ -43,12 +51,125 @@ public class StandaloneVerifierTest extends OfflineToolUtils
}
@Test
- public void testStandaloneVerifier_WithArgs()
+ public void testMaybeChangeDocs()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.StandaloneVerifier", "--debug", "system_schema", "tables")
- .waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(EXPECTED_THREADS_WITH_SCHEMA, OPTIONAL_THREADS_WITH_SCHEMA);
- assertSchemaLoaded();
- assertServerNotLoaded();
+ // If you added, modified options or help, please update docs if necessary
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "-h");
+ String help = "usage: sstableverify [options] <keyspace> <column_family>\n" +
+ "--\n" +
+ "Verify the sstable for the provided table.\n" +
+ "--\n" +
+ "Options are:\n" +
+ " -c,--check_version make sure sstables are the latest version\n" +
+ " --debug display stack traces\n" +
+ " -e,--extended extended verification\n" +
+ " -h,--help display this help message\n" +
+ " -q,--quick do a quick check, don't read all data\n" +
+ " -r,--mutate_repair_status don't mutate repair status\n" +
+ " -t,--token_range <range> long token range of the format left,right.\n" +
+ " This may be provided multiple times to define multiple different ranges\n" +
+ " -v,--verbose verbose output\n";
+ Assertions.assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testWrongArgFailsAndPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "--debugwrong", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
+ assertEquals(1, tool.getExitCode());
+ }
+
+ @Test
+ public void testDefaultCall()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("using the following options"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ assertEquals(0,tool.getExitCode());
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testDebugArg()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, "--debug", "system_schema", "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("debug=true"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ }
+
+ @Test
+ public void testExtendedArg()
+ {
+ Arrays.asList("-e", "--extended").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("extended=true"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testQuickArg()
+ {
+ Arrays.asList("-q", "--quick").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("quick=true"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testRepairStatusArg()
+ {
+ Arrays.asList("-r", "--mutate_repair_status").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("mutateRepairStatus=true"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testHelpArg()
+ {
+ Arrays.asList("-h", "--help").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, arg);
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
+ }
+
+ @Test
+ public void testVerboseArg()
+ {
+ Arrays.asList("-v", "--verbose").forEach(arg -> {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
+ arg,
+ "system_schema",
+ "tables");
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("verbose=true"));
+ Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+ tool.assertOnExitCode();
+ assertCorrectEnvPostTest();
+ });
}
}
diff --git a/test/unit/org/apache/cassandra/tools/ToolRunner.java b/test/unit/org/apache/cassandra/tools/ToolRunner.java
index 7334347..98fdaed 100644
--- a/test/unit/org/apache/cassandra/tools/ToolRunner.java
+++ b/test/unit/org/apache/cassandra/tools/ToolRunner.java
@@ -18,7 +18,6 @@
package org.apache.cassandra.tools;
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -28,13 +27,17 @@ import java.lang.reflect.InvocationTargetException;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
-import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
-import org.apache.commons.io.IOUtils;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,178 +47,11 @@ import org.apache.cassandra.tools.OfflineToolUtils.SystemExitException;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-public class ToolRunner implements AutoCloseable
+public class ToolRunner
{
protected static final Logger logger = LoggerFactory.getLogger(ToolRunner.class);
private static final ImmutableList<String> DEFAULT_CLEANERS = ImmutableList.of("(?im)^picked up.*\\R");
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- private final List<String> allArgs = new ArrayList<>();
- private Process process;
- @SuppressWarnings("resource")
- private final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
- @SuppressWarnings("resource")
- private final ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
- private InputStream stdin;
- private Thread[] ioWatchers;
- 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)
- {
- this.stdin = stdin;
- 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;
- 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(EMPTY_STRING_ARRAY);
- 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;
- 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()
- {
- return exitValue();
- }
-
- };
- }
-
- // each stream tends to use a bounded buffer, so need to process each stream in its own thread else we
- // might block on an idle stream, not consuming the other stream which is blocked in the other process
- // as nothing is consuming
- int numWatchers = 2;
- // only need a stdin watcher when forking
- boolean includeStdinWatcher = runOutOfProcess && stdin != null;
- if (includeStdinWatcher)
- numWatchers = 3;
- ioWatchers = new Thread[numWatchers];
- ioWatchers[0] = new Thread(new StreamGobbler<>(process.getErrorStream(), errBuffer));
- ioWatchers[0].setDaemon(true);
- ioWatchers[0].setName("IO Watcher stderr for " + allArgs);
- ioWatchers[0].start();
-
- ioWatchers[1] = new Thread(new StreamGobbler<>(process.getInputStream(), outBuffer));
- ioWatchers[1].setDaemon(true);
- ioWatchers[1].setName("IO Watcher stdout for " + allArgs);
- ioWatchers[1].start();
-
- if (includeStdinWatcher)
- {
- ioWatchers[2] = new Thread(new StreamGobbler<>(stdin, process.getOutputStream()));
- ioWatchers[2].setDaemon(true);
- ioWatchers[2].setName("IO Watcher stdin for " + allArgs);
- ioWatchers[2].start();
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException("Failed to start " + allArgs, e);
- }
-
- return this;
- }
public static int runClassAsTool(String clazz, String... args)
{
@@ -273,228 +109,479 @@ public class ToolRunner implements AutoCloseable
}
}
- public boolean isRunning()
+ private static final class StreamGobbler<T extends OutputStream> implements Runnable
{
- return process != null && process.isAlive();
- }
+ private static final int BUFFER_SIZE = 8_192;
- public int waitFor()
- {
- try
+ private final InputStream input;
+ private final T out;
+
+ private StreamGobbler(InputStream input, T out)
{
- int rc = process.waitFor();
- // must call first in order to make sure the stdin ioWatcher will exit
- onComplete();
- for (Thread t : ioWatchers)
- t.join();
- return rc;
+ this.input = input;
+ this.out = out;
}
- catch (InterruptedException e)
+
+ public void run()
{
- throw new RuntimeException(e);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ while (true)
+ {
+ try
+ {
+ int read = input.read(buffer);
+ if (read == -1)
+ {
+ return;
+ }
+ out.write(buffer, 0, read);
+ }
+ catch (IOException e)
+ {
+ logger.error("Unexpected IO Error while reading stream", e);
+ return;
+ }
+ }
}
}
- public ToolRunner waitAndAssertOnExitCode()
- {
- assertExitCode(waitFor());
- return this;
- }
-
- public ToolRunner waitAndAssertOnCleanExit()
+ /**
+ * Invokes Cqlsh. The first arg is the cql to execute
+ */
+ public static ToolResult invokeCqlsh(String... args)
{
- return waitAndAssertOnExitCode().assertCleanStdErr();
+ return invokeCqlsh(Arrays.asList(args));
}
-
+
/**
- * Checks if the stdErr is empty after removing any potential JVM env info output and other noise
- *
- * Some JVM configs may output env info on stdErr. We need to remove those to see what was the tool's actual stdErr
- * @return The ToolRunner instance
+ * Invokes Cqlsh. The first arg is the cql to execute
*/
- public ToolRunner assertCleanStdErr()
+ public static ToolResult invokeCqlsh(List<String> args)
{
- assertTrue("Failed because cleaned stdErr wasn't empty: " + getCleanedStderr(), getCleanedStderr().isEmpty());
- return this;
+ return invoke(CQLTester.buildCqlshArgs(args));
}
- public ToolRunner assertOnExitCode()
+ public static ToolResult invokeCassandraStress(String... args)
{
- assertExitCode(getExitCode());
- return this;
+ return invokeCassandraStress(Arrays.asList(args));
}
- private void assertExitCode(int code)
+ public static ToolResult invokeCassandraStress(List<String> args)
{
- if (code != 0)
- fail(String.format("%s%nexited with code %d%nstderr:%n%s%nstdout:%n%s",
- argsToLogString(),
- code,
- getStderr(),
- getStdout()));
+ return invoke(CQLTester.buildCassandraStressArgs(args));
}
- public String argsToLogString()
+ public static ToolResult invokeNodetool(String... args)
{
- return allArgs.stream().collect(Collectors.joining(",\n ", "[", "]"));
+ return invokeNodetool(Arrays.asList(args));
}
- public int getExitCode()
+ public static ToolResult invokeNodetool(List<String> args)
{
- return process.exitValue();
+ return invoke(CQLTester.buildNodetoolArgs(args));
}
- public String getStdout()
+ public static ToolResult invoke(List<String> args)
{
- return outBuffer.toString();
+ return invoke(args.toArray(new String[args.size()]));
}
- public String getStderr()
+ public static ToolResult invoke(String... args)
{
- return errBuffer.toString();
+ try (ObservableTool t = invokeAsync(args))
+ {
+ return t.waitComplete();
+ }
}
- /**
- * Checks if the stdErr is empty after removing any potential JVM env info output and other noise
- *
- * Some JVM configs may output env info on stdErr. We need to remove those to see what was the tool's actual stdErr
- *
- * @param regExpCleaners List of regExps to remove from stdErr
- * @return The stdErr with all excludes removed
- */
- public String getCleanedStderr(List<String> regExpCleaners)
+ public static ObservableTool invokeAsync(String... args)
{
- String sanitizedStderr = getStderr();
- for (String regExp: regExpCleaners)
- sanitizedStderr = sanitizedStderr.replaceAll(regExp, "");
- return sanitizedStderr;
+ return invokeAsync(Collections.emptyMap(), null, Arrays.asList(args));
}
- /**
- * Checks if the stdErr is empty after removing any potential JVM env info output. Uses default list of excludes
- *
- * {@link #getCleanedStderr(List)}
- */
- public String getCleanedStderr()
+ public static ToolResult invoke(Map<String, String> env, InputStream stdin, List<String> args)
{
- return getCleanedStderr(DEFAULT_CLEANERS);
+ try (ObservableTool t = invokeAsync(env, stdin, args))
+ {
+ return t.waitComplete();
+ }
}
- public void forceKill()
+ public static ObservableTool invokeAsync(Map<String, String> env, InputStream stdin, List<String> args)
{
+ ProcessBuilder pb = new ProcessBuilder(args);
+ if (env != null && !env.isEmpty())
+ pb.environment().putAll(env);
try
{
- process.exitValue();
- // process no longer alive - just ignore that fact
+ return new ForkedObservableTool(pb.start(), stdin, args);
}
- catch (IllegalThreadStateException e)
+ catch (IOException e)
{
- process.destroyForcibly();
+ return new FailedObservableTool(e, args);
}
}
- @Override
- public void close()
+ public static ToolResult invokeClass(String klass, String... args)
{
- forceKill();
- onComplete();
+ return invokeClass(klass, null, args);
}
- private void onComplete()
+ public static ToolResult invokeClass(Class<?> klass, String... args)
{
- if (stdin != null)
+ return invokeClass(klass.getName(), null, args);
+ }
+
+ public static ToolResult invokeClass(String klass, InputStream stdin, String... args)
+ {
+ List<String> allArgs = new ArrayList<>();
+ allArgs.add(klass);
+ allArgs.addAll(Arrays.asList(args));
+
+ PrintStream originalSysOut = System.out;
+ PrintStream originalSysErr = System.err;
+ InputStream originalSysIn = System.in;
+ originalSysOut.flush();
+ originalSysErr.flush();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream err = new ByteArrayOutputStream();
+
+ System.setIn(stdin == null ? originalSysIn : stdin);
+
+ try (PrintStream newOut = new PrintStream(out);
+ PrintStream newErr = new PrintStream(err))
{
- try
- {
- stdin.close();
- }
- catch (IOException e)
- {
- logger.warn("Error closing stdin for {}", allArgs, e);
- }
+ System.setOut(newOut);
+ System.setErr(newErr);
+ int rc = runClassAsTool(klass, args);
+ out.flush();
+ err.flush();
+ return new ToolResult(allArgs, rc, out.toString(), err.toString(), null);
+ }
+ catch (Exception e)
+ {
+ return new ToolResult(allArgs,
+ -1,
+ out.toString(),
+ err.toString() + "\n" + Throwables.getStackTraceAsString(e),
+ e);
+ }
+ finally
+ {
+ System.setOut(originalSysOut);
+ System.setErr(originalSysErr);
+ System.setIn(originalSysIn);
}
}
- private static final class StreamGobbler<T extends OutputStream> implements Runnable
+ public static Builder builder(List<String> args)
{
- private static final int BUFFER_SIZE = 8_192;
+ return new Builder(args);
+ }
- private final InputStream input;
- private final T out;
+ public static final class ToolResult
+ {
+ private final List<String> allArgs;
+ private final int exitCode;
+ private final String stdout;
+ private final String stderr;
+ private final Exception e;
- private StreamGobbler(InputStream input, T out)
+ private ToolResult(List<String> allArgs, int exitCode, String stdout, String stderr, Exception e)
{
- this.input = input;
- this.out = out;
+ this.allArgs = allArgs;
+ this.exitCode = exitCode;
+ this.stdout = stdout;
+ this.stderr = stderr;
+ this.e = e;
}
- public void run()
+ public int getExitCode()
{
- byte[] buffer = new byte[BUFFER_SIZE];
- while (true)
- {
- try
- {
- int read = input.read(buffer);
- if (read == -1)
- {
- return;
- }
- out.write(buffer, 0, read);
- }
- catch (IOException e)
- {
- logger.error("Unexpected IO Error while reading stream", e);
- return;
- }
- }
+ return exitCode;
+ }
+
+ public String getStdout()
+ {
+ return stdout;
+ }
+
+ public String getStderr()
+ {
+ return stderr;
+ }
+
+ public Exception getException()
+ {
+ return e;
+ }
+
+ /**
+ * Checks if the stdErr is empty after removing any potential JVM env info output and other noise
+ *
+ * Some JVM configs may output env info on stdErr. We need to remove those to see what was the tool's actual
+ * stdErr
+ *
+ * @return The ToolRunner instance
+ */
+ public void assertCleanStdErr()
+ {
+ assertTrue("Failed because cleaned stdErr wasn't empty: " + getCleanedStderr(),
+ getCleanedStderr().isEmpty());
+ }
+
+ public void assertOnExitCode()
+ {
+ assertExitCode(getExitCode());
+ }
+
+ private void assertExitCode(int code)
+ {
+ if (code != 0)
+ fail(String.format("%s%nexited with code %d%nstderr:%n%s%nstdout:%n%s",
+ argsToLogString(),
+ code,
+ getStderr(),
+ getStdout()));
}
+
+ public String argsToLogString()
+ {
+ return allArgs.stream().collect(Collectors.joining(",\n ", "[", "]"));
+ }
+
+ /**
+ * Returns stdErr after removing any potential JVM env info output through the provided cleaners
+ *
+ * Some JVM configs may output env info on stdErr. We need to remove those to see what was the tool's actual
+ * stdErr
+ *
+ * @param regExpCleaners
+ * List of regExps to remove from stdErr
+ * @return The stdErr with all excludes removed
+ */
+ public String getCleanedStderr(List<String> regExpCleaners)
+ {
+ String sanitizedStderr = getStderr();
+ for (String regExp : regExpCleaners)
+ sanitizedStderr = sanitizedStderr.replaceAll(regExp, "");
+ return sanitizedStderr;
+ }
+
+ /**
+ * Returns stdErr after removing any potential JVM env info output. Uses default list of excludes
+ *
+ * {@link #getCleanedStderr(List)}
+ */
+ public String getCleanedStderr()
+ {
+ return getCleanedStderr(DEFAULT_CLEANERS);
+ }
+
+ public void assertOnCleanExit()
+ {
+ assertOnExitCode();
+ assertCleanStdErr();
+ }
+ }
+
+ public interface ObservableTool extends AutoCloseable
+ {
+ String getPartialStdout();
+
+ String getPartialStderr();
+
+ boolean isDone();
+
+ ToolResult waitComplete();
+
+ @Override
+ void close();
}
- public static class Runners
+ private static final class FailedObservableTool implements ObservableTool
{
- public static ToolRunner invokeNodetool(String... args)
+ private final List<String> args;
+ private final IOException error;
+
+ private FailedObservableTool(IOException error, List<String> args)
+ {
+ this.args = args;
+ this.error = error;
+ }
+
+ @Override
+ public String getPartialStdout()
+ {
+ return "";
+ }
+
+ @Override
+ public String getPartialStderr()
{
- return invokeNodetool(Arrays.asList(args));
+ return error.getMessage();
}
- public static ToolRunner invokeNodetool(List<String> args)
+ @Override
+ public boolean isDone()
{
- return invokeTool(buildNodetoolArgs(args), true);
+ return true;
}
- private static List<String> buildNodetoolArgs(List<String> args)
+ @Override
+ public ToolResult waitComplete()
{
- return CQLTester.buildNodetoolArgs(args);
+ return new ToolResult(args, -1, getPartialStdout(), getPartialStderr(), error);
}
- public static ToolRunner invokeClassAsTool(String... args)
+ @Override
+ public void close()
{
- return invokeClassAsTool(Arrays.asList(args));
+
}
+ }
- public static ToolRunner invokeClassAsTool(List<String> args)
+ private static final class ForkedObservableTool implements ObservableTool
+ {
+ @SuppressWarnings("resource")
+ private final ByteArrayOutputStream err = new ByteArrayOutputStream();
+ @SuppressWarnings("resource")
+ private final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ @SuppressWarnings("resource")
+ private final InputStream stdin;
+ private final Process process;
+ private final Thread[] ioWatchers;
+ private final List<String> args;
+
+ private ForkedObservableTool(Process process, InputStream stdin, List<String> args)
{
- return invokeTool(args, false);
+ this.process = process;
+ this.args = args;
+ this.stdin = stdin;
+
+ // Each stream tends to use a bounded buffer, so need to process each stream in its own thread else we
+ // might block on an idle stream, not consuming the other stream which is blocked in the other process
+ // as nothing is consuming
+ int numWatchers = 2;
+ // only need a stdin watcher when forking
+ boolean includeStdinWatcher = stdin != null;
+ if (includeStdinWatcher)
+ numWatchers = 3;
+ ioWatchers = new Thread[numWatchers];
+ ioWatchers[0] = new Thread(new StreamGobbler<>(process.getErrorStream(), err));
+ ioWatchers[0].setDaemon(true);
+ ioWatchers[0].setName("IO Watcher stderr");
+ ioWatchers[0].start();
+
+ ioWatchers[1] = new Thread(new StreamGobbler<>(process.getInputStream(), out));
+ ioWatchers[1].setDaemon(true);
+ ioWatchers[1].setName("IO Watcher stdout");
+ ioWatchers[1].start();
+
+ if (includeStdinWatcher)
+ {
+ ioWatchers[2] = new Thread(new StreamGobbler<>(stdin, process.getOutputStream()));
+ ioWatchers[2].setDaemon(true);
+ ioWatchers[2].setName("IO Watcher stdin");
+ ioWatchers[2].start();
+ }
+ }
+
+ @Override
+ public String getPartialStdout()
+ {
+ return out.toString();
+ }
+
+ @Override
+ public String getPartialStderr()
+ {
+ return err.toString();
}
- public static ToolRunner invokeTool(String... args)
+ @Override
+ public boolean isDone()
{
- return invokeTool(Arrays.asList(args));
+ return !process.isAlive();
}
- public static ToolRunner invokeTool(List<String> args)
+ @Override
+ public ToolResult waitComplete()
{
- return invokeTool(args, true);
+ try
+ {
+ int rc = process.waitFor();
+ onComplete();
+ return new ToolResult(args, rc, out.toString(), err.toString(), null);
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
}
- public static ToolRunner invokeTool(List<String> args, boolean runOutOfProcess)
+ private void onComplete() throws InterruptedException
{
- try (ToolRunner runner = new ToolRunner(args, runOutOfProcess).start())
+ try
{
- runner.waitFor();
- return runner;
+ if (stdin != null)
+ stdin.close();
}
+ catch (IOException e)
+ {
+ logger.warn("Error closing stdin", e);
+ }
+ for (Thread t : ioWatchers)
+ t.join();
+ }
+
+ @Override
+ public void close()
+ {
+ if (!process.isAlive())
+ return;
+ process.destroyForcibly();
+ }
+ }
+
+ public static final class Builder
+ {
+ private final Map<String, String> env = new HashMap<>();
+ private final List<String> args;
+ private InputStream stdin;
+
+ public Builder(List<String> args)
+ {
+ this.args = Objects.requireNonNull(args);
+ }
+
+ public Builder withEnv(String key, String value)
+ {
+ env.put(key, value);
+ return this;
+ }
+
+ public Builder withEnvs(Map<String, String> map)
+ {
+ env.putAll(map);
+ return this;
+ }
+
+ public Builder withStdin(InputStream input)
+ {
+ this.stdin = input;
+ return this;
+ }
+
+ public ObservableTool invokeAsync()
+ {
+ return ToolRunner.invokeAsync(env, stdin, args);
+ }
+
+ public ToolResult invoke()
+ {
+ return ToolRunner.invoke(env, stdin, args);
}
}
}
diff --git a/test/unit/org/apache/cassandra/tools/GetVersionTest.java b/test/unit/org/apache/cassandra/tools/ToolsEnvsConfigsTest.java
similarity index 53%
copy from test/unit/org/apache/cassandra/tools/GetVersionTest.java
copy to test/unit/org/apache/cassandra/tools/ToolsEnvsConfigsTest.java
index c5f5282..fd39901 100644
--- a/test/unit/org/apache/cassandra/tools/GetVersionTest.java
+++ b/test/unit/org/apache/cassandra/tools/ToolsEnvsConfigsTest.java
@@ -18,25 +18,27 @@
package org.apache.cassandra.tools;
+import java.util.Collections;
+
+import com.google.common.collect.ImmutableMap;
+
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+
+import static org.junit.Assert.assertTrue;
-@RunWith(OrderedJUnit4ClassRunner.class)
-public class GetVersionTest extends OfflineToolUtils
+public class ToolsEnvsConfigsTest
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
+ //Some JDK can output env info on stdout/err. Check we can clean them
+ @SuppressWarnings("resource")
@Test
- public void testGetVersion()
+ public void testJDKEnvInfoDefaultCleaners()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.GetVersion").waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(null, null);
- assertSchemaNotLoaded();
- assertCLSMNotLoaded();
- assertSystemKSNotLoaded();
- assertKeyspaceNotLoaded();
- assertServerNotLoaded();
+ ToolResult tool = ToolRunner.invoke(ImmutableMap.of("_JAVA_OPTIONS", "-Djava.net.preferIPv4Stack=true"),
+ null,
+ CQLTester.buildNodetoolArgs(Collections.emptyList()));
+ assertTrue("Cleaned Stderr was not empty: " + tool.getCleanedStderr(), tool.getCleanedStderr().isEmpty());
}
}
diff --git a/test/unit/org/apache/cassandra/tools/cassandrastress/CassandrastressTest.java b/test/unit/org/apache/cassandra/tools/cassandrastress/CassandrastressTest.java
new file mode 100644
index 0000000..8ba091a
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/cassandrastress/CassandrastressTest.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.cassandra.tools.cassandrastress;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.hamcrest.CoreMatchers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class CassandrastressTest extends CQLTester
+{
+ @BeforeClass
+ public static void setUp()
+ {
+ requireNetwork();
+ }
+
+ @Test
+ public void testNoArgsPrintsHelp()
+ {
+ ToolResult tool = ToolRunner.invokeCassandraStress();
+ assertThat(tool.getStdout(), CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertTrue("Tool stderr: " + tool.getCleanedStderr(), tool.getCleanedStderr().isEmpty());
+ assertEquals(1, tool.getExitCode());
+ }
+
+}
diff --git a/test/unit/org/apache/cassandra/tools/GetVersionTest.java b/test/unit/org/apache/cassandra/tools/cqlsh/CqlshTest.java
similarity index 53%
copy from test/unit/org/apache/cassandra/tools/GetVersionTest.java
copy to test/unit/org/apache/cassandra/tools/cqlsh/CqlshTest.java
index c5f5282..4e6dd20 100644
--- a/test/unit/org/apache/cassandra/tools/GetVersionTest.java
+++ b/test/unit/org/apache/cassandra/tools/cqlsh/CqlshTest.java
@@ -16,27 +16,32 @@
* limitations under the License.
*/
-package org.apache.cassandra.tools;
+package org.apache.cassandra.tools.cqlsh;
+import org.junit.BeforeClass;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.hamcrest.CoreMatchers;
-@RunWith(OrderedJUnit4ClassRunner.class)
-public class GetVersionTest extends OfflineToolUtils
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class CqlshTest extends CQLTester
{
- private ToolRunner.Runners runner = new ToolRunner.Runners();
-
+ @BeforeClass
+ public static void setUp()
+ {
+ requireNetwork();
+ }
+
@Test
- public void testGetVersion()
+ public void testKeyspaceRequired()
{
- runner.invokeClassAsTool("org.apache.cassandra.tools.GetVersion").waitAndAssertOnCleanExit();
- assertNoUnexpectedThreadsStarted(null, null);
- assertSchemaNotLoaded();
- assertCLSMNotLoaded();
- assertSystemKSNotLoaded();
- assertKeyspaceNotLoaded();
- assertServerNotLoaded();
+ ToolResult tool = ToolRunner.invokeCqlsh("SELECT * FROM test");
+ assertThat(tool.getCleanedStderr(), CoreMatchers.containsStringIgnoringCase("No keyspace has been specified"));
+ assertEquals(2, tool.getExitCode());
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org