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

bereng pushed a commit to branch cassandra-4.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-4.0 by this push:
     new 95257c0  Sstableverify unit test operate on SSTables
95257c0 is described below

commit 95257c0805eeeb7209a421148b59796c3fb426ed
Author: Bereng <[email protected]>
AuthorDate: Tue Jan 18 07:45:27 2022 +0100

    Sstableverify unit test operate on SSTables
    
    patch by Brian Houser; reviewed by Andres de la Peña, Berenguer Blasi for 
CASSANDRA-17009
    
    Co-authored-by: Brian Houser <[email protected]>
    Co-authored-by: Berenguer Blasi <[email protected]>
---
 .gitignore                                         |   1 +
 .../apache/cassandra/tools/StandaloneVerifier.java |  40 +++-
 test/unit/org/apache/cassandra/db/VerifyTest.java  |  42 ++--
 .../apache/cassandra/tools/OfflineToolUtils.java   |   3 -
 .../tools/StandaloneVerifierOnSSTablesTest.java    | 222 +++++++++++++++++++++
 .../cassandra/tools/StandaloneVerifierTest.java    |  30 ++-
 6 files changed, 304 insertions(+), 34 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7d5e24d..3b01673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ src/gen-java/
 src/resources/org/apache/cassandra/config/
 logs/
 data/
+!test/data
 conf/hotspot_compiler
 doc/cql3/CQL.html
 doc/build/
diff --git a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java 
b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
index 4e4a80a..df276bd 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
@@ -18,26 +18,37 @@
  */
 package org.apache.cassandra.tools;
 
-import org.apache.cassandra.schema.Schema;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.ParseException;
+
+import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.db.ColumnFamilyStore;
 import org.apache.cassandra.db.Directories;
 import org.apache.cassandra.db.Keyspace;
-import org.apache.cassandra.db.compaction.*;
+import org.apache.cassandra.db.compaction.CompactionManager;
+import org.apache.cassandra.db.compaction.Verifier;
 import org.apache.cassandra.dht.Murmur3Partitioner;
 import org.apache.cassandra.dht.Range;
 import org.apache.cassandra.dht.Token;
 import org.apache.cassandra.io.sstable.Component;
-import org.apache.cassandra.io.sstable.CorruptSSTableException;
 import org.apache.cassandra.io.sstable.Descriptor;
 import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.schema.Schema;
 import org.apache.cassandra.utils.JVMStabilityInspector;
 import org.apache.cassandra.utils.OutputHandler;
-import org.apache.commons.cli.*;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import static org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
 
@@ -56,7 +67,8 @@ public class StandaloneVerifier
     public static void main(String args[])
     {
         Options options = Options.parseArgs(args);
-        Util.initDatabaseDescriptor();
+        initDatabaseDescriptorForTool();
+
         System.out.println("sstableverify using the following options: " + 
options);
 
         try
@@ -98,6 +110,7 @@ public class StandaloneVerifier
                     System.err.println(String.format("Error Loading %s: %s", 
entry.getKey(), e.getMessage()));
                     if (options.debug)
                         e.printStackTrace(System.err);
+                    System.exit(1);
                 }
             }
             Verifier.Options verifyOptions = 
Verifier.options().invokeDiskFailurePolicy(false)
@@ -134,6 +147,13 @@ public class StandaloneVerifier
         }
     }
 
+    private static void initDatabaseDescriptorForTool() {
+        if (Boolean.getBoolean(Util.ALLOW_TOOL_REINIT_FOR_TEST))
+            DatabaseDescriptor.toolInitialization(false); //Necessary for 
testing
+        else
+            Util.initDatabaseDescriptor();
+    }
+
     private static class Options
     {
         public final String keyspaceName;
diff --git a/test/unit/org/apache/cassandra/db/VerifyTest.java 
b/test/unit/org/apache/cassandra/db/VerifyTest.java
index 8e3fbe4..b2f4344 100644
--- a/test/unit/org/apache/cassandra/db/VerifyTest.java
+++ b/test/unit/org/apache/cassandra/db/VerifyTest.java
@@ -18,13 +18,31 @@
  */
 package org.apache.cassandra.db;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
+
 import com.google.common.base.Charsets;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
 
+import org.apache.cassandra.UpdateBuilder;
 import org.apache.cassandra.Util;
 import org.apache.cassandra.batchlog.Batch;
 import org.apache.cassandra.batchlog.BatchlogManager;
 import org.apache.cassandra.cache.ChunkCache;
-import org.apache.cassandra.UpdateBuilder;
 import org.apache.cassandra.db.compaction.CompactionManager;
 import org.apache.cassandra.db.compaction.Verifier;
 import org.apache.cassandra.db.marshal.UUIDType;
@@ -45,20 +63,6 @@ import org.apache.cassandra.schema.CompressionParams;
 import org.apache.cassandra.schema.KeyspaceParams;
 import org.apache.cassandra.service.StorageService;
 import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.commons.lang3.StringUtils;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.*;
-import java.net.UnknownHostException;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-import java.util.zip.CRC32;
-import java.util.zip.CheckedInputStream;
 
 import static org.apache.cassandra.SchemaLoader.counterCFMD;
 import static org.apache.cassandra.SchemaLoader.createKeyspace;
@@ -69,6 +73,14 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+/**
+ * Test for {@link Verifier}.
+ * 
+ * Note: the complete coverage is composed of:
+ * - {@link org.apache.cassandra.tools.StandaloneVerifierOnSSTablesTest}
+ * - {@link org.apache.cassandra.tools.StandaloneVerifierTest}
+ * - {@link VerifyTest}
+ */
 public class VerifyTest
 {
     public static final String KEYSPACE = "Keyspace1";
diff --git a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java 
b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
index c1678d7..7bd1143 100644
--- a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
+++ b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
@@ -33,10 +33,7 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import org.apache.commons.io.FileUtils;
-
-import org.junit.AfterClass;
 import org.junit.BeforeClass;
-
 import org.slf4j.LoggerFactory;
 
 import static 
org.apache.cassandra.utils.FBUtilities.preventIllegalAccessWarnings;
diff --git 
a/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java 
b/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
new file mode 100644
index 0000000..7df28d2
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.tools;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.UpdateBuilder;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.PartitionPosition;
+import org.apache.cassandra.db.compaction.CompactionManager;
+import org.apache.cassandra.io.sstable.Component;
+import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.schema.KeyspaceParams;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.tools.ToolRunner.ToolResult;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.assertj.core.api.Assertions;
+
+import static org.apache.cassandra.SchemaLoader.standardCFMD;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Class that tests tables for {@link StandaloneVerifier} by updating using 
{@link SchemaLoader}
+ * Similar in vein to other {@link SchemaLoader} type tests, as well as {@link 
StandaloneUpgraderOnSStablesTest}.
+ * Since the tool mainly exercises the {@link 
org.apache.cassandra.db.compaction.Verifier}, we elect to
+ * not run every conceivable option as many tests are already covered by 
{@link org.apache.cassandra.db.VerifyTest}.
+ * 
+ * Note: the complete coverage is composed of:
+ * - {@link StandaloneVerifierOnSSTablesTest}
+ * - {@link StandaloneVerifierTest}
+ * - {@link org.apache.cassandra.db.VerifyTest}
+ */
+public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
+{
+    @BeforeClass
+    public static void setup()
+    {
+        // since legacy tables test data uses ByteOrderedPartitioner that's 
what we need
+        // for the check version to work
+        System.setProperty("cassandra.partitioner", 
"org.apache.cassandra.dht.ByteOrderedPartitioner");
+        System.setProperty(Util.ALLOW_TOOL_REINIT_FOR_TEST, "true"); // 
Necessary for testing`
+        SchemaLoader.loadSchema();
+        StorageService.instance.initServer();
+    }
+
+    @AfterClass
+    public static void teardown() throws Exception
+    {
+        SchemaLoader.cleanupAndLeaveDirs();
+        System.clearProperty(Util.ALLOW_TOOL_REINIT_FOR_TEST);
+    }
+
+    @Test
+    public void testCheckVersionValidVersion() throws Exception
+    {
+        String keyspaceName = "StandaloneVerifierTestCheckVersionWorking";
+        String workingTable = "workingCheckTable";
+
+        createAndPopulateTable(keyspaceName, workingTable, x -> {});
+
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, workingTable, "-c");
+        assertEquals(0, tool.getExitCode());
+        assertCorrectEnvPostTest();
+        tool.assertOnCleanExit();
+    }
+
+    @Test
+    public void testCheckVersionWithWrongVersion() throws Exception
+    {
+        String keyspace = "StandaloneVerifierTestWrongVersions";
+        String tableName = "legacy_ma_simple";
+
+        createAndPopulateTable(keyspace, tableName, cfs -> {
+            // let's just copy old version files from test data into the 
source dir
+            File testDataDir = new 
File("test/data/legacy-sstables/ma/legacy_tables/legacy_ma_simple");
+            for (File cfsDir : cfs.getDirectories().getCFDirectories())
+            {
+                FileUtils.copyDirectory(testDataDir, cfsDir);
+            }
+        });
+
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspace, tableName, "-c");
+
+        assertEquals(1, tool.getExitCode());
+        Assertions.assertThat(tool.getStdout()).contains("is not the latest 
version, run upgradesstables");
+    }
+
+    @Test
+    public void testWorkingDataFile() throws Exception
+    {
+        String keyspaceName = "StandaloneVerifierTestWorkingDataKs";
+        String workingTable = "workingTable";
+
+        createAndPopulateTable(keyspaceName, workingTable, x -> {});
+
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, workingTable);
+        assertEquals(0, tool.getExitCode());
+        assertCorrectEnvPostTest();
+        tool.assertOnCleanExit();
+    }
+
+    @Test
+    public void testCorruptStatsFile() throws Exception
+    {
+        String keyspaceName = "StandaloneVerifierTestCorruptStatsKs";
+        String corruptStatsTable = "corruptStatsTable";
+        createAndPopulateTable(keyspaceName, corruptStatsTable, cfs -> {
+            SSTableReader sstable = cfs.getLiveSSTables().iterator().next();
+            try (RandomAccessFile file = new 
RandomAccessFile(sstable.descriptor.filenameFor(Component.STATS), "rw"))
+            {
+                file.seek(0);
+                file.writeBytes(StringUtils.repeat('z', 2));
+            }
+        });
+
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, corruptStatsTable);
+
+        assertEquals(1, tool.getExitCode());
+        Assertions.assertThat(tool.getStderr()).contains("Error Loading", 
corruptStatsTable);
+    }
+
+    @Test
+    public void testCorruptDataFile() throws Exception
+    {
+        String keyspaceName = "StandaloneVerifierTestCorruptDataKs";
+        String corruptDataTable = "corruptDataTable";
+
+        createAndPopulateTable(keyspaceName, corruptDataTable, cfs -> {
+            SSTableReader sstable = cfs.getLiveSSTables().iterator().next();
+            long row0Start = 
sstable.getPosition(PartitionPosition.ForKey.get(ByteBufferUtil.bytes("0"), 
cfs.getPartitioner()), SSTableReader.Operator.EQ).position;
+            long row1Start = 
sstable.getPosition(PartitionPosition.ForKey.get(ByteBufferUtil.bytes("1"), 
cfs.getPartitioner()), SSTableReader.Operator.EQ).position;
+            long startPosition = Math.min(row0Start, row1Start);
+
+            try (RandomAccessFile file = new 
RandomAccessFile(sstable.getFilename(), "rw"))
+            {
+                file.seek(startPosition);
+                file.writeBytes(StringUtils.repeat('z', 2));
+            }
+        });
+
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, corruptDataTable);
+        assertEquals(1, tool.getExitCode());
+        Assertions.assertThat(tool.getStdout()).contains("Invalid SSTable", 
corruptDataTable);
+    }
+
+    /**
+     * Since we are testing a verifier, we'd like to corrupt files to verify 
code paths
+     * This function definition is used by {@link this#createAndPopulateTable}.
+     *
+     * CFS is the open ColumnFamilyStore for a given keyspace, table
+     */
+    @FunctionalInterface
+    private interface CorruptFunction
+    {
+        public void apply(ColumnFamilyStore cfs) throws Exception;
+    }
+
+    /**
+     * This function sets up the keyspace, and table schema for a standardCFMD 
table.
+     * <p>
+     * This will also populate the tableName with a few rows.  After 
completion the
+     * server will be shutdown.
+     *
+     * @param keyspace the name of the keyspace in which the table should be 
created
+     * @param tableName new table name of the standard CFMD table
+     * @param corruptionFn function called to corrupt or change the contents 
on disk, is passed the Cfs of the table name.
+     * @throws Exception on error.
+     */
+    private static void createAndPopulateTable(String keyspace, String 
tableName, CorruptFunction corruptionFn) throws Exception
+    {
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    standardCFMD(keyspace, tableName));
+
+        CompactionManager.instance.disableAutoCompaction();
+
+        Keyspace k = Keyspace.open(keyspace);
+        ColumnFamilyStore cfs = k.getColumnFamilyStore(tableName);
+
+        populateTable(cfs, 2);
+
+        corruptionFn.apply(cfs);
+    }
+
+    private static void populateTable(ColumnFamilyStore cfs, int 
partitionsPerSSTable)
+    {
+        for (int i = 0; i < partitionsPerSSTable; i++)
+        {
+            UpdateBuilder.create(cfs.metadata(), String.valueOf(i))
+                         .newRow("c1").add("val", "1")
+                         .newRow("c2").add("val", "2")
+                         .apply();
+        }
+
+        cfs.forceBlockingFlush();
+    }
+}
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java 
b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
index 73890cd..9e6bb14 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
@@ -29,6 +29,12 @@ import org.hamcrest.CoreMatchers;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
+/**
+* Note: the complete coverage is composed of:
+ * - {@link StandaloneVerifierOnSSTablesTest}
+ * - {@link StandaloneVerifierTest}
+ * - {@link org.apache.cassandra.db.VerifyTest}
+*/
 public class StandaloneVerifierTest extends OfflineToolUtils
 {
     @Test
@@ -70,6 +76,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
         Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
         assertEquals(0, tool.getExitCode());
         assertCorrectEnvPostTest();
+        tool.assertOnCleanExit();
+
     }
 
     @Test
@@ -78,8 +86,9 @@ public class StandaloneVerifierTest extends OfflineToolUtils
         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();
+        tool.assertOnCleanExit();
+
     }
 
     @Test
@@ -92,8 +101,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
                                                        "tables");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("extended=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
-            tool.assertOnExitCode();
             assertCorrectEnvPostTest();
+            tool.assertOnCleanExit();
         });
     }
 
@@ -107,8 +116,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
                                                        "tables");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("quick=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
-            tool.assertOnExitCode();
             assertCorrectEnvPostTest();
+            tool.assertOnCleanExit();
         });
     }
 
@@ -122,8 +131,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
                                                        "tables");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("mutateRepairStatus=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
-            tool.assertOnExitCode();
             assertCorrectEnvPostTest();
+            tool.assertOnCleanExit();
         });
     }
 
@@ -134,8 +143,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
             ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
arg);
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage:"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
-            tool.assertOnExitCode();
             assertCorrectEnvPostTest();
+            tool.assertOnCleanExit();
         });
     }
 
@@ -149,8 +158,17 @@ public class StandaloneVerifierTest extends 
OfflineToolUtils
                                                        "tables");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("verbose=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
-            tool.assertOnExitCode();
             assertCorrectEnvPostTest();
+            tool.assertOnCleanExit();
         });
     }
+
+    @Test
+    public void testTooManyArgs()
+    {
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"another arg", "system_schema", "tables");
+        assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage:"));
+        assertThat(tool.getCleanedStderr(), 
CoreMatchers.containsStringIgnoringCase("Too many arguments"));
+        assertEquals(1, tool.getExitCode());
+    }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to