diff --git a/cassandra-analytics-cdc-codec/src/test/java/org/apache/cassandra/cdc/json/JsonSerializerTests.java b/cassandra-analytics-cdc-codec/src/test/java/org/apache/cassandra/cdc/json/JsonSerializerTests.java index ee2aca021..d45ca9909 100644 --- a/cassandra-analytics-cdc-codec/src/test/java/org/apache/cassandra/cdc/json/JsonSerializerTests.java +++ b/cassandra-analytics-cdc-codec/src/test/java/org/apache/cassandra/cdc/json/JsonSerializerTests.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.util.Base64; import java.util.Iterator; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -109,7 +110,7 @@ public void testJsonBinary() throws IOException { CdcEventBuilder eventBuilder = CdcEventBuilder.of(CdcEvent.Kind.INSERT, TEST_KS, TEST_TBL_BINARY); eventBuilder.setPartitionKeys(listOf(Value.of(TEST_KS, "a", "int", TYPES.aInt().serialize(1)))); - ByteBuffer randomBytes = ByteBuffer.wrap(RandomUtils.randomBytes(128)); + ByteBuffer randomBytes = ByteBuffer.wrap(RandomUtils.randomBytes(new Random(), 128)); eventBuilder.setValueColumns(listOf( Value.of(TEST_KS, "b", "blob", TYPES.blob().serialize(randomBytes)), Value.of(TEST_KS, "c", "inet", TYPES.inet().serialize(InetAddress.getByName("127.0.0.1"))) diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroByteRecordTransformerTest.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroByteRecordTransformerTest.java index 985b3b1b8..4496121eb 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroByteRecordTransformerTest.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroByteRecordTransformerTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -107,14 +108,16 @@ public void testTtlDeletedAtByteAvroEncoding(CassandraVersion version) .withColumn("c2", bridge.text()); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); testRow.setTTL(ttlSeconds); writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); } diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroGenericRecordTransformerTest.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroGenericRecordTransformerTest.java index 2672e59df..176ed3fcf 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroGenericRecordTransformerTest.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/AvroGenericRecordTransformerTest.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cdc; import java.util.List; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -99,14 +100,16 @@ public void testBasicInsertAvroEncoding(CassandraVersion version) // Capture CqlTable from the tester via the writer callback AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); long timestampMicros = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); IntStream.range(0, tester.numRows) - .forEach(i -> writer.accept(CdcTester.newUniqueRow(tester.schema, rows), timestampMicros)); + .forEach(i -> writer.accept(CdcTester.newUniqueRow(tester.schema, rows, random), timestampMicros)); }) .withCdcEventChecker((testRows, events) -> { assertThat(events).hasSize(NUM_ROWS); @@ -158,14 +161,16 @@ public void testCollectionTypesAvroEncoding(CassandraVersion version) .withColumn("l", bridge.list(bridge.text())); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); long timestampMicros = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); IntStream.range(0, tester.numRows) - .forEach(i -> writer.accept(CdcTester.newUniqueRow(tester.schema, rows), timestampMicros)); + .forEach(i -> writer.accept(CdcTester.newUniqueRow(tester.schema, rows, random), timestampMicros)); }) .withCdcEventChecker((testRows, events) -> { assertThat(events).hasSize(NUM_ROWS); @@ -199,14 +204,16 @@ public void testAvroSerializeDeserializeRoundTrip(CassandraVersion version) .withColumn("c2", bridge.text()); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); long timestampMicros = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); IntStream.range(0, tester.numRows) - .forEach(i -> writer.accept(CdcTester.newUniqueRow(tester.schema, rows), timestampMicros)); + .forEach(i -> writer.accept(CdcTester.newUniqueRow(tester.schema, rows, random), timestampMicros)); }) .withCdcEventChecker((testRows, events) -> { assertThat(events).hasSize(NUM_ROWS); @@ -263,14 +270,16 @@ public void testDeleteEventAvroEncoding(CassandraVersion version) .withColumn("c2", bridge.text()); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); testRow = testRow.copy("c1", org.apache.cassandra.bridge.CdcBridge.UNSET_MARKER); testRow = testRow.copy("c2", null); writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); @@ -318,14 +327,16 @@ public void testMaxSupportedTtlAvroEncoding(CassandraVersion version) .withColumn("c1", bridge.aInt()); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); testRow.setTTL(largeTtl); writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); } @@ -387,14 +398,16 @@ public void testPartitionDeleteAvroEncoding(CassandraVersion version) .withColumn("c1", bridge.aInt()); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = newUniquePartitionDeletion(tester.schema, rows); + TestSchema.TestRow testRow = newUniquePartitionDeletion(tester.schema, rows, random); writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); } }) @@ -438,14 +451,16 @@ public void testMixedTtlAndNonTtlAvroEncoding(CassandraVersion version) .withColumn("c1", bridge.aInt()); AtomicReference tableRef = new AtomicReference<>(); + Random random = new Random(); testWith(bridge, cdcBridge, commitLogDir, schemaBuilder) .withNumRows(NUM_ROWS) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { tableRef.set(tester.cqlTable); for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); if (i % 2 == 0) { testRow.setTTL(ttlSeconds); diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CdcTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CdcTests.java index d24365a50..f377a9d08 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CdcTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CdcTests.java @@ -105,6 +105,7 @@ import static org.apache.cassandra.cdc.test.CdcTester.newUniqueRow; import static org.apache.cassandra.cdc.test.CdcTester.testWith; import static org.apache.cassandra.spark.CommonTestUtils.cql3Type; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.Assumptions.assumeThat; @@ -222,10 +223,11 @@ public List loadState(String jobId, int partitionId, @Nullable TokenRa }; Map writtenRows = new HashMap<>(); + Random random = new Random(); Runnable writer = () -> { IntStream.range(0, batchSize) .forEach(i -> { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(testSchema, writtenRows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(testSchema, writtenRows, random); cdcBridge.log(table, commitLog, testRow, TimeUtils.nowMicros()); writtenRows.put(testRow.getPrimaryHexKey(), testRow); }); @@ -456,12 +458,13 @@ public void log(TimeProvider timeProvider, CqlTable cqlTable, CommitLogInstance @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testSinglePartitionKey(CassandraVersion version) { - qt().forAll(cql3Type(bridge)) - .checkAssert(type -> + qt().forAll(cql3Type(bridge), qtRandom()) + .checkAssert((type, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", type)) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { assertThat(events.isEmpty()).isFalse(); for (CdcEvent event : events) @@ -483,14 +486,15 @@ public void testSinglePartitionKey(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testClusteringKey(CassandraVersion version) { - qt().forAll(cql3Type(bridge)) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) - .checkAssert(type -> + qt().forAll(cql3Type(bridge), qtRandom()) + .assuming((type, random) -> type.supportedAsPrimaryKeyColumn()) + .checkAssert((type, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withClusteringKey("ck", type) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.text())) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { for (CdcEvent event : events) { @@ -513,12 +517,12 @@ public void testClusteringKey(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testMultipleClusteringKeys(CassandraVersion version) { - qt().withExamples(50).forAll(cql3Type(bridge), cql3Type(bridge), cql3Type(bridge)) - .assuming((t1, t2, t3) -> t1.supportedAsPrimaryKeyColumn() + qt().withExamples(50).forAll(cql3Type(bridge), cql3Type(bridge), cql3Type(bridge), qtRandom()) + .assuming((t1, t2, t3, random) -> t1.supportedAsPrimaryKeyColumn() && t2.supportedAsPrimaryKeyColumn() && t3.supportedAsPrimaryKeyColumn()) .checkAssert( - (t1, t2, t3) -> + (t1, t2, t3, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withClusteringKey("ck1", t1) @@ -526,6 +530,7 @@ public void testMultipleClusteringKeys(CassandraVersion version) .withClusteringKey("ck3", t3) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.text())) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { for (CdcEvent event : events) { @@ -551,13 +556,14 @@ public void testMultipleClusteringKeys(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testSet(CassandraVersion version) { - qt().forAll(cql3Type(bridge)) - .assuming(CqlField.CqlType::supportedAsSetElement) + qt().forAll(cql3Type(bridge), qtRandom()) + .assuming((t, random) -> t.supportedAsSetElement()) .checkAssert( - t -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) + (t, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.set(t))) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { for (CdcEvent event : events) { @@ -587,13 +593,14 @@ public void testSet(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testList(CassandraVersion version) { - qt().forAll(cql3Type(bridge)) + qt().forAll(cql3Type(bridge), qtRandom()) .checkAssert( - t -> + (t, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.list(bridge.aInt()))) + .withRandom(random) .withCassandraSource((keyspace, table, columnsToFetch, primaryKeyColumns) -> { // mutations to unfrozen lists require reading the full list from Cassandra List byteBuffers = new ArrayList<>(); @@ -633,16 +640,17 @@ public void testList(CassandraVersion version) public void testVector(CassandraVersion version) { assumeThat(bridge.getVersion().versionNumber()).isGreaterThanOrEqualTo(CassandraVersion.FIVEZERO.versionNumber()); - qt().forAll(cql3Type(bridge)) + qt().forAll(cql3Type(bridge), qtRandom()) // Cassandra VectorType does not support swapping custom subtype serializer, // so we cannot use AnalyticsTimeUUIDSerializer or AnalyticsDurationSerializer. - .assuming(t -> !t.cqlName().equals(Duration.INSTANCE.name()) && !t.cqlName().equals(TimeUUID.INSTANCE.name())) + .assuming((t, random) -> !t.cqlName().equals(Duration.INSTANCE.name()) && !t.cqlName().equals(TimeUUID.INSTANCE.name())) .checkAssert( - t -> + (t, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.vector(t, 5))) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { for (CdcEvent event : events) { @@ -676,13 +684,14 @@ public void testVector(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testMap(CassandraVersion version) { - qt().withExamples(50).forAll(cql3Type(bridge), cql3Type(bridge)) - .assuming((t1, t2) -> t1.supportedAsMapKey() && t2.supportedAsMapKey()) + qt().withExamples(50).forAll(cql3Type(bridge), cql3Type(bridge), qtRandom()) + .assuming((t1, t2, random) -> t1.supportedAsMapKey() && t2.supportedAsMapKey()) .checkAssert( - (t1, t2) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) + (t1, t2, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.map(t1, t2))) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { for (CdcEvent event : events) { @@ -752,14 +761,16 @@ public void testMultiTable(CassandraVersion version) Collections.emptySet(), null, 0, schema3.withCdc); int numRows = DEFAULT_NUM_ROWS; + Random random = new Random(); AtomicReference schema1Holder = new AtomicReference<>(); CdcTester.Builder testBuilder = CdcTester.builder(bridge, cdcBridge, tableBuilder1, commitLogDir) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { for (int i = 0; i < numRows; i++) { - writer.accept(CdcTester.newUniqueRow(tester.schema, rows), + writer.accept(CdcTester.newUniqueRow(tester.schema, rows, random), TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); } }) @@ -772,7 +783,7 @@ public void write(CdcTester tester, Map prevRows = new HashMap<>(numRows); for (int i = 0; i < numRows; i++) { - writer.accept(CdcTester.newUniqueRow(schema2, prevRows), + writer.accept(CdcTester.newUniqueRow(schema2, prevRows, random), TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); } } @@ -791,7 +802,7 @@ public void write(CdcTester tester, Map prevRows = new HashMap<>(numRows); for (int i = 0; i < numRows; i++) { - writer.accept(CdcTester.newUniqueRow(schema3, prevRows), + writer.accept(CdcTester.newUniqueRow(schema3, prevRows, random), TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); } } @@ -824,20 +835,21 @@ public CqlTable cqlTable(CdcTester tester) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testUpdateStaticColumnOnly(CassandraVersion version) { - qt().forAll(cql3Type(bridge).zip(arbitrary().enumValues(OperationType.class), Pair::of)) - .checkAssert(cql3TypeAndInsertFlag -> { + qt().forAll(cql3Type(bridge).zip(arbitrary().enumValues(OperationType.class), Pair::of), qtRandom()) + .checkAssert((cql3TypeAndInsertFlag, random) -> { CqlField.NativeType cqlType = cql3TypeAndInsertFlag._1; OperationType insertOrUpdate = cql3TypeAndInsertFlag._2; testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withClusteringKey("ck", bridge.uuid()) .withStaticColumn("sc", cqlType)) + .withRandom(random) .clearWriters() .withWriter(((tester, rows, writer) -> { long timestampMicros = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); IntStream.range(0, tester.numRows) .forEach(i -> { - TestSchema.TestRow row = newUniqueRow(tester.schema, rows); + TestSchema.TestRow row = newUniqueRow(tester.schema, rows, random); row = row.copy(1, null); insertOrUpdate.accept(row); writer.accept(row, timestampMicros); @@ -868,24 +880,24 @@ public void testUpdateStaticColumnOnly(CassandraVersion version) public void testUpdatePartialColumns(CassandraVersion version) { Set ttlRowIdx = new HashSet<>(); - Random rnd = new Random(1); - qt().forAll(cql3Type(bridge)) - .checkAssert(type -> { + qt().forAll(cql3Type(bridge), qtRandom()) + .checkAssert((type, random) -> { ttlRowIdx.clear(); testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", type)) + .withRandom(random) .clearWriters() .withAddLastModificationTime(true) .withWriter((tester, rows, writer) -> { long time = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = newUniqueRow(tester.schema, rows, random); // mark c1 as not updated / unset testRow = testRow.copy("c1", CdcBridge.UNSET_MARKER); - if (rnd.nextDouble() > 0.5) + if (random.nextDouble() > 0.5) { testRow.setTTL(TTL); ttlRowIdx.add(testRow.getUUID("pk")); @@ -926,19 +938,20 @@ public void testCellDeletion(CassandraVersion version) { // The test write cell-level tombstones, // i.e. deleting one or more columns in a row, for cdc job to aggregate. - qt().forAll(cql3Type(bridge)) + qt().forAll(cql3Type(bridge), qtRandom()) .checkAssert( - type -> + (type, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", type) .withColumn("c3", bridge.list(type))) + .withRandom(random) .clearWriters() .withWriter((tester, rows, writer) -> { for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); testRow = testRow.copy("c1", CdcBridge.UNSET_MARKER); // mark c1 as not updated / unset testRow = testRow.copy("c2", null); // delete c2 testRow = testRow.copy("c3", null); // delete c3 @@ -968,16 +981,17 @@ public void testCellDeletion(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testCompositePartitionKey(CassandraVersion version) { - qt().forAll(cql3Type(bridge)) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) + qt().forAll(cql3Type(bridge), qtRandom()) + .assuming((type, random) -> type.supportedAsPrimaryKeyColumn()) .checkAssert( - type -> + (type, random) -> testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk1", bridge.uuid()) .withPartitionKey("pk2", type) .withPartitionKey("pk3", bridge.timestamp()) .withColumn("c1", bridge.bigint()) .withColumn("c2", bridge.text())) + .withRandom(random) .withCdcEventChecker((testRows, events) -> { for (CdcEvent event : events) { @@ -1001,19 +1015,20 @@ public void testCompositePartitionKey(CassandraVersion version) public void testUpdateFlag(CassandraVersion version) { qt().withExamples(10) - .forAll(cql3Type(bridge)) - .checkAssert(type -> { + .forAll(cql3Type(bridge), qtRandom()) + .checkAssert((type, random) -> { testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.aInt()) .withColumn("c2", type)) + .withRandom(random) .clearWriters() .withNumRows(1000) .withWriter((tester, rows, writer) -> { int halfway = tester.numRows / 2; for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); testRow = testRow.copy("c1", i); if (i >= halfway) { @@ -1054,12 +1069,13 @@ public void testMultipleWritesToSameKeyInBatch(CassandraVersion version) // The test writes different groups of mutations. // Each group of mutations write to the same key with the different timestamp. // For CDC, it only deduplicate and emit the replicated mutations, i.e. they have the same writetime. - qt().forAll(cql3Type(bridge)) - .checkAssert(type -> { + qt().forAll(cql3Type(bridge), qtRandom()) + .checkAssert((type, random) -> { testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) .withPartitionKey("pk", bridge.uuid()) .withColumn("c1", bridge.bigint()) .withColumn("c2", type)) + .withRandom(random) .clearWriters() .withNumRows(1000) .withExpectedNumRows(2000) @@ -1069,13 +1085,13 @@ public void testMultipleWritesToSameKeyInBatch(CassandraVersion version) long timestamp = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); for (int i = 0; i < tester.numRows; i++) { - writer.accept(CdcTester.newUniqueRow(tester.schema, rows), timestamp++); + writer.accept(CdcTester.newUniqueRow(tester.schema, rows, random), timestamp++); } // overwrite with new mutations at later timestamp for (TestSchema.TestRow row : rows.values()) { - TestSchema.TestRow newUniqueRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow newUniqueRow = CdcTester.newUniqueRow(tester.schema, rows, random); for (CqlField field : tester.cqlTable.valueColumns()) { // update value columns diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CollectionDeletionTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CollectionDeletionTests.java index dcda43418..889744be7 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CollectionDeletionTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/CollectionDeletionTests.java @@ -25,7 +25,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -47,6 +46,7 @@ import static org.apache.cassandra.cdc.test.CdcTester.testWith; import static org.apache.cassandra.spark.CommonTestUtils.cql3Type; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; @@ -100,14 +100,14 @@ private void testElementDeletionInCollection(CassandraBridge bridge, { // key: row# that has deletion; value: the deleted cell key/path in the collection final Map elementDeletionIndices = new HashMap<>(); - final Random rnd = new Random(1); final long minTimestamp = System.currentTimeMillis(); final int numRows = 1000; - qt().forAll(cql3Type(bridge)) - .assuming(CqlField.CqlType::supportedAsMapKey) + qt().forAll(cql3Type(bridge), qtRandom()) + .assuming((type, random) -> type.supportedAsMapKey()) .checkAssert( - type -> testWith(bridge, cdcBridge, directory, schemaBuilder.apply(type)) + (type, random) -> testWith(bridge, cdcBridge, directory, schemaBuilder.apply(type)) .withAddLastModificationTime(true) + .withRandom(random) .clearWriters() .withNumRows(numRows) .withWriter((tester, rows, writer) -> { @@ -117,12 +117,12 @@ private void testElementDeletionInCollection(CassandraBridge bridge, { int ignoredSize = 10; TestSchema.TestRow testRow; - if (rnd.nextDouble() < 0.5) + if (random.nextDouble() < 0.5) { // NOTE: it is a little hacky. For simplicity, all collections in the row // has the SAME entry being deleted. - ByteBuffer key = type.serialize(type.randomValue(ignoredSize)); - testRow = CdcTester.newUniqueRow(tester.schema, rows); + ByteBuffer key = type.serialize(type.randomValue(ignoredSize, random)); + testRow = CdcTester.newUniqueRow(tester.schema, rows, random); for (String name : collectionColumnNames) { Object value = TestUtils.collectionDeleteMutation(bridge.getVersion(), key); @@ -132,7 +132,7 @@ private void testElementDeletionInCollection(CassandraBridge bridge, } else { - testRow = CdcTester.newUniqueRow(tester.schema, rows); + testRow = CdcTester.newUniqueRow(tester.schema, rows, random); } timestamp += 1; writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(timestamp)); diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/MicroBatchIteratorTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/MicroBatchIteratorTests.java index 7b477d2ae..a98719bcc 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/MicroBatchIteratorTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/MicroBatchIteratorTests.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -63,6 +64,7 @@ import static org.apache.cassandra.cdc.CdcTests.ASYNC_EXECUTOR; import static org.apache.cassandra.cdc.CdcTests.logProvider; import static org.apache.cassandra.spark.CommonTestUtils.cql3Type; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -79,9 +81,9 @@ public void testSetDeletion(CassandraVersion version) TestSchema.builder(bridge) .withPartitionKey("a", bridge.uuid()) .withColumn("b", bridge.set(bridge.text())), - (schema, i, rows) -> { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(schema, rows); - String deletedValue = (String) bridge.text().randomValue(4); + (schema, i, rows, random) -> { + TestSchema.TestRow testRow = CdcTester.newUniqueRow(schema, rows, random); + String deletedValue = (String) bridge.text().randomValue(4, random); ByteBuffer key = bridge.text().serialize(deletedValue); testRow = testRow.copy("b", TestUtils.collectionDeleteMutation(version, key)); deletedValues.put(testRow.get(0).toString(), deletedValue); @@ -106,9 +108,9 @@ public void testMapDeletion(CassandraVersion version) TestSchema.builder(bridge) .withPartitionKey("a", bridge.uuid()) .withColumn("b", bridge.map(bridge.text(), bridge.aInt())), - (schema, i, rows) -> { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(schema, rows); - String deletedValue = (String) bridge.text().randomValue(4); + (schema, i, rows, random) -> { + TestSchema.TestRow testRow = CdcTester.newUniqueRow(schema, rows, random); + String deletedValue = (String) bridge.text().randomValue(4, random); ByteBuffer key = bridge.text().serialize(deletedValue); testRow = testRow.copy("b", TestUtils.collectionDeleteMutation(version, key)); deletedValues.put(testRow.get(0).toString(), deletedValue); @@ -134,14 +136,14 @@ public void testRangeTombstone(CassandraVersion version) .withClusteringKey("b", bridge.aInt()) .withClusteringKey("c", bridge.aInt()) .withColumn("d", bridge.text()), - (schema, i, rows) -> { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(schema, rows); - int start = RandomUtils.randomPositiveInt(1024); - int end = start + RandomUtils.randomPositiveInt(100000); + (schema, i, rows, random) -> { + TestSchema.TestRow testRow = CdcTester.newUniqueRow(schema, rows, random); + int start = RandomUtils.randomPositiveInt(random, 1024); + int end = start + RandomUtils.randomPositiveInt(random, 100000); testRow.setRangeTombstones(ImmutableList.of( new RangeTombstoneData( - new RangeTombstoneData.Bound(new Integer[]{start, start + RandomUtils.randomPositiveInt(100)}, true), - new RangeTombstoneData.Bound(new Integer[]{end, end + RandomUtils.randomPositiveInt(100)}, true))) + new RangeTombstoneData.Bound(new Integer[]{start, start + RandomUtils.randomPositiveInt(random, 100)}, true), + new RangeTombstoneData.Bound(new Integer[]{end, end + RandomUtils.randomPositiveInt(random, 100)}, true))) ); rows.put(testRow.get(0).toString(), testRow); return testRow; @@ -182,8 +184,8 @@ public void testRowDelete(CassandraVersion version) .withPartitionKey("b", bridge.aInt()) .withClusteringKey("c", bridge.bigint()) .withColumn("d", bridge.text()), - (schema, i, rows) -> { - TestSchema.TestRow row = schema.randomRow(); + (schema, i, rows, random) -> { + TestSchema.TestRow row = schema.randomRow(random); row.delete(); rows.put(row.getPrimaryHexKey(), row); return row; @@ -212,8 +214,8 @@ public void testInserts(CassandraVersion version) .withPartitionKey("b", bridge.text()) .withClusteringKey("c", bridge.timestamp()) .withColumn("d", bridge.map(bridge.text(), bridge.aInt())), - (schema, i, rows) -> { - TestSchema.TestRow row = schema.randomRow(); + (schema, i, rows, random) -> { + TestSchema.TestRow row = schema.randomRow(random); rows.put(row.getPrimaryHexKey(), row); return row; }, @@ -243,8 +245,8 @@ public void testPartitionDelete(CassandraVersion version) .withPartitionKey("b", bridge.aInt()) .withClusteringKey("c", bridge.bigint()) .withColumn("d", bridge.text()), - (schema, i, rows) -> { - TestSchema.TestRow row = schema.randomPartitionDelete(); + (schema, i, rows, random) -> { + TestSchema.TestRow row = schema.randomPartitionDelete(random); rows.put(row.getPartitionHexKey(), row); // partition delete so just the partition keys return row; }, @@ -265,8 +267,8 @@ public void testPartitionDelete(CassandraVersion version) @MethodSource("org.apache.cassandra.cdc.test.TestVersionSupplier#testVersions") public void testUpdateStaticColumnAndValueColumns(CassandraVersion version) { - qt().forAll(cql3Type(bridge).zip(arbitrary().enumValues(OperationType.class), Pair::of)) - .checkAssert(cql3TypeAndInsertFlag -> { + qt().forAll(cql3Type(bridge).zip(arbitrary().enumValues(OperationType.class), Pair::of), qtRandom()) + .checkAssert((cql3TypeAndInsertFlag, random) -> { CqlField.NativeType cqlType = cql3TypeAndInsertFlag._1; OperationType insertOrUpdate = cql3TypeAndInsertFlag._2; testWith(bridge, cdcBridge, commitLogDir, TestSchema.builder(bridge) @@ -274,12 +276,13 @@ public void testUpdateStaticColumnAndValueColumns(CassandraVersion version) .withClusteringKey("ck", bridge.uuid()) .withStaticColumn("sc", cqlType) .withColumn("c1", cqlType)) + .withRandom(random) .clearWriters() .withWriter(((tester, rows, writer) -> { long timestampMicros = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); IntStream.range(0, tester.numRows) .forEach(i -> { - TestSchema.TestRow row = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow row = CdcTester.newUniqueRow(tester.schema, rows, random); insertOrUpdate.accept(row); writer.accept(row, timestampMicros); }); @@ -321,8 +324,8 @@ public void testUpdate(CassandraVersion version) .withPartitionKey("b", bridge.aInt()) .withClusteringKey("c", bridge.bigint()) .withColumn("d", bridge.text()), - (schema, i, rows) -> { - TestSchema.TestRow row = schema.randomRow(); + (schema, i, rows, random) -> { + TestSchema.TestRow row = schema.randomRow(random); row.fromUpdate(); rows.put(row.getPrimaryHexKey(), row); return row; @@ -364,7 +367,7 @@ public void accept(TestSchema.TestRow row) public interface RowGenerator { - TestSchema.TestRow newRow(TestSchema schema, int i, Map rows); + TestSchema.TestRow newRow(TestSchema schema, int i, Map rows, Random random); } public interface TestVerifier @@ -396,9 +399,10 @@ private void runTest(CassandraBridge bridge, try { Map rows = new HashMap<>(numRows); + Random random = new Random(); for (int i = 0; i < numRows; i++) { - TestSchema.TestRow row = rowGenerator.newRow(schema, i, rows); + TestSchema.TestRow row = rowGenerator.newRow(schema, i, rows, random); cdcBridge.log(TimeProvider.DEFAULT, cqlTable, commitLog, row, nowMicros); } commitLog.sync(); diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/PartitionDeletionTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/PartitionDeletionTests.java index 4fd1f044d..17d08edf1 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/PartitionDeletionTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/PartitionDeletionTests.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -43,6 +42,7 @@ import static org.apache.cassandra.cdc.test.CdcTester.testWith; import static org.apache.cassandra.spark.CommonTestUtils.cql3Type; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; @@ -110,14 +110,14 @@ private void testPartitionDeletion(CassandraBridge bridge, // This kind of output means the entire partition is deleted final Set partitionDeletionIndices = new HashSet<>(); final List> validationPk = new ArrayList<>(); // pk of the partition deletions - final Random rnd = new Random(1); final long minTimestamp = System.currentTimeMillis(); final int numRows = 1000; - qt().forAll(cql3Type(bridge)) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) - .checkAssert(type -> { + qt().forAll(cql3Type(bridge), qtRandom()) + .assuming((type, random) -> type.supportedAsPrimaryKeyColumn()) + .checkAssert((type, random) -> { testWith(bridge, cdcBridge, commitLogDir, schemaBuilder.apply(type)) .withAddLastModificationTime(true) + .withRandom(random) .clearWriters() .withNumRows(numRows) .withWriter((tester, rows, writer) -> { @@ -127,9 +127,9 @@ private void testPartitionDeletion(CassandraBridge bridge, for (int i = 0; i < tester.numRows; i++) { TestSchema.TestRow testRow; - if (rnd.nextDouble() < 0.5) + if (random.nextDouble() < 0.5) { - testRow = CdcTester.newUniquePartitionDeletion(tester.schema, rows); + testRow = CdcTester.newUniquePartitionDeletion(tester.schema, rows, random); List pk = new ArrayList<>(partitionKeys); for (int j = 0; j < partitionKeys; j++) { @@ -140,7 +140,7 @@ private void testPartitionDeletion(CassandraBridge bridge, } else { - testRow = CdcTester.newUniqueRow(tester.schema, rows); + testRow = CdcTester.newUniqueRow(tester.schema, rows, random); } timestamp += 1; writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(timestamp)); diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RangeDeletionTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RangeDeletionTests.java index cba295932..4b0336d25 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RangeDeletionTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RangeDeletionTests.java @@ -23,7 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -45,6 +45,7 @@ import static org.apache.cassandra.cdc.test.CdcTester.testWith; import static org.apache.cassandra.spark.CommonTestUtils.cql3Type; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; @@ -118,15 +119,16 @@ private void testRangeDeletions(CassandraBridge bridge, Map rangeTombstones = new HashMap<>(); long minTimestamp = System.currentTimeMillis(); int numRows = 1000; - qt().forAll(cql3Type(bridge)) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) + qt().forAll(cql3Type(bridge), qtRandom()) + .assuming((type, random) -> type.supportedAsPrimaryKeyColumn()) .checkAssert( - type -> + (type, random) -> testWith(bridge, cdcBridge, commitLogDir, schemaBuilder.apply(type)) .withAddLastModificationTime(true) + .withRandom(random) .clearWriters() .withNumRows(numRows) - .withWriter(rangeDeletionWriter(rangeTombstones, numOfPartitionKeys, numOfClusteringKeys, withOpenEnd, minTimestamp)) + .withWriter(rangeDeletionWriter(rangeTombstones, numOfPartitionKeys, numOfClusteringKeys, withOpenEnd, minTimestamp, random)) .withCdcEventChecker((testRows, events) -> { for (int i = 0; i < events.size(); i++) { @@ -201,7 +203,8 @@ public static CdcWriter rangeDeletionWriter(Map ran int numOfPartitionKeys, int numOfClusteringKeys, boolean withOpenEnd, - long minTimestamp) + long minTimestamp, + Random random) { return (tester, rows, writer) -> { long timestamp = minTimestamp; @@ -209,14 +212,14 @@ public static CdcWriter rangeDeletionWriter(Map ran for (int i = 0; i < tester.numRows; i++) { TestSchema.TestRow testRow; - if (ThreadLocalRandom.current().nextDouble() < 0.5) + if (random.nextDouble() < 0.5) { - testRow = CdcTester.newUniqueRow(tester.schema, rows); + testRow = CdcTester.newUniqueRow(tester.schema, rows, random); Object[] baseBound = testRow.rawValues(numOfPartitionKeys, numOfPartitionKeys + numOfClusteringKeys); // create a new bound that has the last CK value different from the base bound Object[] newBound = new Object[baseBound.length]; System.arraycopy(baseBound, 0, newBound, 0, baseBound.length); - TestSchema.TestRow newRow = CdcTester.newUniqueRow(tester.schema, rows); + TestSchema.TestRow newRow = CdcTester.newUniqueRow(tester.schema, rows, random); int lastCK = newBound.length - 1; newBound[lastCK] = newRow.get(numOfPartitionKeys + numOfClusteringKeys - 1); Object[] open; @@ -242,7 +245,7 @@ public static CdcWriter rangeDeletionWriter(Map ran } else { - testRow = CdcTester.newUniqueRow(tester.schema, rows); + testRow = CdcTester.newUniqueRow(tester.schema, rows, random); } timestamp += 1; writer.accept(testRow, TimeUnit.MILLISECONDS.toMicros(timestamp)); diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RowDeletionTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RowDeletionTests.java index 5053c531d..be519db9a 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RowDeletionTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/RowDeletionTests.java @@ -20,7 +20,6 @@ package org.apache.cassandra.cdc; import java.util.HashSet; -import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -40,6 +39,7 @@ import static org.apache.cassandra.cdc.test.CdcTester.testWith; import static org.apache.cassandra.spark.CommonTestUtils.cql3Type; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; @@ -98,13 +98,13 @@ private void testRowDeletion(CassandraBridge bridge, // i.e. [pk.., ck.., null..]. The bitset should indicate that only the primary keys are present. // This kind of output means the entire row is deleted final Set rowDeletionIndices = new HashSet<>(); - final Random rnd = new Random(1); final long minTimestamp = System.currentTimeMillis(); final int numRows = 1000; - qt().forAll(cql3Type(bridge)) + qt().forAll(cql3Type(bridge), qtRandom()) .checkAssert( - type -> testWith(bridge, cdcBridge, commitLogDir, schemaBuilder.apply(type)) + (type, random) -> testWith(bridge, cdcBridge, commitLogDir, schemaBuilder.apply(type)) .withAddLastModificationTime(true) + .withRandom(random) .clearWriters() .withNumRows(numRows) .withWriter((tester, rows, writer) -> { @@ -112,8 +112,8 @@ private void testRowDeletion(CassandraBridge bridge, long timestamp = minTimestamp; for (int i = 0; i < tester.numRows; i++) { - TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows); - if (rnd.nextDouble() < 0.5) + TestSchema.TestRow testRow = CdcTester.newUniqueRow(tester.schema, rows, random); + if (random.nextDouble() < 0.5) { testRow.delete(); rowDeletionIndices.add(testRow.getUUID("pk")); diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/msg/jdk/CdcMessageTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/msg/jdk/CdcMessageTests.java index 3ba05991a..d019f31e7 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/msg/jdk/CdcMessageTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/msg/jdk/CdcMessageTests.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cdc.msg.jdk; import java.util.Date; +import java.util.Random; import com.google.common.collect.ImmutableList; import org.junit.jupiter.api.Test; @@ -38,10 +39,11 @@ public class CdcMessageTests @Test public void testCdcMessage() { - long colA = (long) BigInt.INSTANCE.randomValue(1024); + Random random = new Random(); + long colA = (long) BigInt.INSTANCE.randomValue(1024, random); java.util.UUID colB = java.util.UUID.randomUUID(); - String colC = Text.INSTANCE.randomValue(1024).toString(); - Date colD = (Date) Timestamp.INSTANCE.randomValue(1024); + String colC = Text.INSTANCE.randomValue(1024, random).toString(); + Date colD = (Date) Timestamp.INSTANCE.randomValue(1024, random); long now = TimeUtils.nowMicros(); CdcMessage msg = new CdcMessage( diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/test/CdcTester.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/test/CdcTester.java index f05c0ae3c..0a0facbaf 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/test/CdcTester.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/cdc/test/CdcTester.java @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -102,6 +103,7 @@ public void reset() Partitioner partitioner = Partitioner.Murmur3Partitioner; CdcOptions cdcOptions; CassandraSource cassandraSource; + public final Random random; CdcTester(CassandraBridge bridge, CdcBridge cdcBridge, @@ -114,7 +116,8 @@ public void reset() BiConsumer, List> eventsChecker, boolean shouldCdcEventWriterFailOnProcessing, CdcOptions cdcOptions, - CassandraSource cassandraSource) + CassandraSource cassandraSource, + Random random) { this.bridge = bridge; this.cdcBridge = cdcBridge; @@ -132,6 +135,7 @@ public void reset() this.cqlTable = schema.buildTable(); this.cdcOptions = cdcOptions; this.cassandraSource = cassandraSource; + this.random = random; } public static Builder builder(CassandraBridge bridge, CdcBridge cdcBridge, TestSchema.Builder schemaBuilder, Path testDir) @@ -153,6 +157,7 @@ public static class Builder private boolean shouldCdcEventWriterFailOnProcessing = false; private CdcOptions cdcOptions; private CassandraSource cassandraSource = CassandraSource.DEFAULT; + private Random random = new Random(); Builder(CassandraBridge bridge, CdcBridge cdcBridge, TestSchema.Builder schemaBuilder, Path testDir) { @@ -165,10 +170,16 @@ public static class Builder this.writers.add((tester, rows, writer) -> { long timestampMicros = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); IntStream.range(0, tester.numRows) - .forEach(i -> writer.accept(newUniqueRow(tester.schema, rows), timestampMicros)); + .forEach(i -> writer.accept(newUniqueRow(tester.schema, rows, tester.random), timestampMicros)); }); } + public Builder withRandom(Random random) + { + this.random = random; + return this; + } + public Builder clearWriters() { this.writers.clear(); @@ -237,7 +248,7 @@ public CdcTester build() } return new CdcTester(bridge, cdcBridge, schemaBuilder.build(), testDir, writers, numRows, expectedNumRows, addLastModificationTime, eventChecker, shouldCdcEventWriterFailOnProcessing, - options, cassandraSource); + options, cassandraSource, random); } public void run() @@ -356,14 +367,14 @@ public static Builder testWith(CassandraBridge bridge, CdcBridge cdcBridge, Path return new Builder(bridge, cdcBridge, schemaBuilder, testDir); } - public static TestSchema.TestRow newUniqueRow(TestSchema schema, Map rows) + public static TestSchema.TestRow newUniqueRow(TestSchema schema, Map rows, Random random) { - return newUniqueRow(schema::randomRow, rows); + return newUniqueRow(() -> schema.randomRow(random), rows); } - public static TestSchema.TestRow newUniquePartitionDeletion(TestSchema schema, Map rows) + public static TestSchema.TestRow newUniquePartitionDeletion(TestSchema schema, Map rows, Random random) { - return newUniqueRow(schema::randomPartitionDelete, rows); + return newUniqueRow(() -> schema.randomPartitionDelete(random), rows); } private static TestSchema.TestRow newUniqueRow(Supplier rowProvider, diff --git a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/db/commitlog/BufferingCommitLogReaderTests.java b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/db/commitlog/BufferingCommitLogReaderTests.java index b273f6ce3..4a395537d 100644 --- a/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/db/commitlog/BufferingCommitLogReaderTests.java +++ b/cassandra-analytics-cdc/src/test/java/org/apache/cassandra/db/commitlog/BufferingCommitLogReaderTests.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -93,13 +94,14 @@ public void testReaderSeek(CassandraVersion version) int numRows = 1000; // write some rows to a CommitLog + Random random = new Random(); Set keys = new HashSet<>(numRows); for (int i = 0; i < numRows; i++) { - TestSchema.TestRow row = schema.randomRow(); + TestSchema.TestRow row = schema.randomRow(random); while (keys.contains(row.getLong("pk"))) { - row = schema.randomRow(); + row = schema.randomRow(random); } keys.add(row.getLong("pk")); cdcBridge.log(TimeProvider.DEFAULT, cqlTable, commitLog, row, TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); diff --git a/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/data/CqlField.java b/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/data/CqlField.java index b228e36e0..d38afa361 100644 --- a/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/data/CqlField.java +++ b/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/data/CqlField.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import com.google.common.annotations.VisibleForTesting; @@ -36,7 +37,6 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import org.apache.cassandra.bridge.CassandraVersion; -import org.apache.cassandra.spark.utils.RandomUtils; import org.jetbrains.annotations.NotNull; @SuppressWarnings({ "WeakerAccess", "unused" }) @@ -170,13 +170,14 @@ default Object deserializeToJavaType(ByteBuffer buffer) @VisibleForTesting int cardinality(int orElse); - default Object randomValue() + @VisibleForTesting + default Object randomValue(Random random) { - return randomValue(RandomUtils.MIN_COLLECTION_SIZE); + return randomValue(16, random); } @VisibleForTesting - Object randomValue(int minCollectionSize); + Object randomValue(int minCollectionSize, Random random); @VisibleForTesting Object convertForCqlWriter(Object value, CassandraVersion version, boolean isCollectionElement); diff --git a/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/utils/RandomUtils.java b/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/utils/RandomUtils.java index 5827b4487..31996941a 100644 --- a/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/utils/RandomUtils.java +++ b/cassandra-analytics-common/src/main/java/org/apache/cassandra/spark/utils/RandomUtils.java @@ -35,36 +35,34 @@ public final class RandomUtils { public static final int MIN_COLLECTION_SIZE = 16; - public static final Random RANDOM = new Random(); - private RandomUtils() { throw new IllegalStateException(getClass() + " is static utility class and shall not be instantiated"); } - public static byte randomByte() + public static byte randomByte(Random random) { - return randomBytes(1)[0]; + return randomBytes(random, 1)[0]; } - public static byte[] randomBytes(int size) + public static byte[] randomBytes(Random random, int size) { byte[] bytes = new byte[size]; - RANDOM.nextBytes(bytes); + random.nextBytes(bytes); return bytes; } - public static ByteBuffer randomByteBuffer(int length) + public static ByteBuffer randomByteBuffer(Random random, int length) { - return ByteBuffer.wrap(randomBytes(length)); + return ByteBuffer.wrap(randomBytes(random, length)); } - public static int randomPositiveInt(int bound) + public static int randomPositiveInt(Random random, int bound) { - return RANDOM.nextInt(bound - 1) + 1; + return random.nextInt(bound - 1) + 1; } - public static int nextInt(int startInclusive, int endExclusive) + public static int nextInt(Random random, int startInclusive, int endExclusive) { if (endExclusive <= startInclusive) { @@ -75,14 +73,14 @@ public static int nextInt(int startInclusive, int endExclusive) throw new IllegalArgumentException("Both range values must be non-negative."); } - return startInclusive + RANDOM.nextInt(endExclusive - startInclusive); + return startInclusive + random.nextInt(endExclusive - startInclusive); } - public static BigInteger randomBigInteger(Partitioner partitioner) + public static BigInteger randomBigInteger(Random random, Partitioner partitioner) { BigInteger range = partitioner.maxToken().subtract(partitioner.minToken()); int length = partitioner.maxToken().bitLength(); - BigInteger result = new BigInteger(length, RandomUtils.RANDOM); + BigInteger result = new BigInteger(length, random); if (result.compareTo(partitioner.minToken()) < 0) { result = result.add(partitioner.minToken()); @@ -94,6 +92,31 @@ public static BigInteger randomBigInteger(Partitioner partitioner) return result; } + /** + * Returns a random Type 4 (random) UUID, built from the given {@link Random} so it is reproducible from a + * fixed seed - unlike {@link UUID#randomUUID()}, which always draws from the JVM-wide {@link java.security.SecureRandom}. + */ + public static UUID randomUuid(Random random) + { + byte[] randomBytes = new byte[16]; + random.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= (byte) 0x80; /* set to IETF variant */ + long mostSigBits = 0; + for (int i = 0; i < 8; i++) + { + mostSigBits = (mostSigBits << 8) | (randomBytes[i] & 0xff); + } + long leastSigBits = 0; + for (int i = 8; i < 16; i++) + { + leastSigBits = (leastSigBits << 8) | (randomBytes[i] & 0xff); + } + return new UUID(mostSigBits, leastSigBits); + } + /** * Returns a random Type 1 (time-based) UUID. *

@@ -102,49 +125,49 @@ public static BigInteger randomBigInteger(Partitioner partitioner) * * @return a random Type 1 (time-based) UUID */ - public static UUID getRandomTimeUUIDForTesting() + public static UUID getRandomTimeUUIDForTesting(Random random) { - UUID uuid = UUID.randomUUID(); + UUID uuid = randomUuid(random); return new UUID(uuid.getMostSignificantBits() ^ 0x0000000000005000L, // Change UUID version from 4 to 1 uuid.getLeastSignificantBits() | 0x0000010000000000L); // Always set multicast bit to 1 } @SuppressWarnings("UnstableApiUsage") - public static InetAddress randomInet() + public static InetAddress randomInet(Random random) { - return InetAddresses.fromInteger(RANDOM.nextInt()); + return InetAddresses.fromInteger(random.nextInt()); } - public static String randomAlphanumeric(int minLengthInclusive, int maxLengthExclusive) + public static String randomAlphanumeric(Random random, int minLengthInclusive, int maxLengthExclusive) { - return randomAlphanumeric(RandomUtils.nextInt(minLengthInclusive, maxLengthExclusive)); + return randomAlphanumeric(random, RandomUtils.nextInt(random, minLengthInclusive, maxLengthExclusive)); } - public static String randomAlphanumeric(Set alreadyExist) + public static String randomAlphanumeric(Random random, Set alreadyExist) { - return randomAlphanumeric(alreadyExist, 32); + return randomAlphanumeric(random, alreadyExist, 32); } - public static String randomAlphanumeric(Set alreadyExist, int length) + public static String randomAlphanumeric(Random random, Set alreadyExist, int length) { - String str = randomAlphanumeric(length); + String str = randomAlphanumeric(random, length); while (alreadyExist.contains(str)) { - str = randomAlphanumeric(length); + str = randomAlphanumeric(random, length); } return str; } - public static String randomAlphanumeric() + public static String randomAlphanumeric(Random random) { - return randomAlphanumeric(32); + return randomAlphanumeric(random, 32); } - public static String randomAlphanumeric(int length) + public static String randomAlphanumeric(Random random, int length) { StringBuilder sb = new StringBuilder(length); IntStream.range(0, length) - .mapToObj(i -> randomAsciiAlphanumeric()) + .mapToObj(i -> randomAsciiAlphanumeric(random)) .forEach(sb::append); return sb.toString(); } @@ -152,9 +175,9 @@ public static String randomAlphanumeric(int length) /** * @return random ascii character between 0x30...0x39 for numbers and 0x41...0x5A for uppercase letters */ - public static char randomAsciiAlphanumeric() + public static char randomAsciiAlphanumeric(Random random) { - int c = RANDOM.nextInt(36); + int c = random.nextInt(36); if (c < 10) { // return ascii number diff --git a/cassandra-analytics-common/src/test/java/org/apache/cassandra/spark/utils/RandomUtilsTest.java b/cassandra-analytics-common/src/test/java/org/apache/cassandra/spark/utils/RandomUtilsTest.java index b0800afa0..2c02aca79 100644 --- a/cassandra-analytics-common/src/test/java/org/apache/cassandra/spark/utils/RandomUtilsTest.java +++ b/cassandra-analytics-common/src/test/java/org/apache/cassandra/spark/utils/RandomUtilsTest.java @@ -19,6 +19,7 @@ package org.apache.cassandra.spark.utils; +import java.util.Random; import java.util.Set; import java.util.stream.Collectors; @@ -33,17 +34,19 @@ public class RandomUtilsTest .mapToObj(e -> (char) e) .collect(Collectors.toSet()); + private final Random random = new Random(); + @Test public void testNextInt() { for (int i = 0; i < 1000; i++) { - assertThat(RandomUtils.nextInt(4, 5)).isEqualTo(4); + assertThat(RandomUtils.nextInt(random, 4, 5)).isEqualTo(4); } for (int i = 0; i < 1000; i++) { - int r = RandomUtils.nextInt(4, 7); + int r = RandomUtils.nextInt(random, 4, 7); assertThat(r >= 4).isTrue(); assertThat(r < 7).isTrue(); } @@ -52,10 +55,10 @@ public void testNextInt() @Test public void testNextIntThrows() { - assertThatThrownBy(() -> RandomUtils.nextInt(-1, 5)).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> RandomUtils.nextInt(-5, -2)).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> RandomUtils.nextInt(5, 5)).isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> RandomUtils.nextInt(10, 5)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RandomUtils.nextInt(random, -1, 5)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RandomUtils.nextInt(random, -5, -2)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RandomUtils.nextInt(random, 5, 5)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RandomUtils.nextInt(random, 10, 5)).isInstanceOf(IllegalArgumentException.class); } @Test @@ -63,7 +66,7 @@ public void testRandomAscii() { for (int i = 0; i < 1000000; i++) { - assertThat(ALPHANUMERIC_CHARS.contains(RandomUtils.randomAsciiAlphanumeric())).isTrue(); + assertThat(ALPHANUMERIC_CHARS.contains(RandomUtils.randomAsciiAlphanumeric(random))).isTrue(); } } @@ -72,8 +75,8 @@ public void testRandomString() { for (int i = 0; i < 1000; i++) { - int len = RandomUtils.nextInt(20, 100); - String str = RandomUtils.randomAlphanumeric(len); + int len = RandomUtils.nextInt(random, 20, 100); + String str = RandomUtils.randomAlphanumeric(random, len); assertThat(str.length()).isEqualTo(len); for (int j = 0; j < str.length(); j++) { diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/bridge/CompressionTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/bridge/CompressionTests.java index f6f91bd6c..1d1fc248a 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/bridge/CompressionTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/bridge/CompressionTests.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Random; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -40,7 +41,7 @@ public void testCompressRandom(CassandraVersion version) throws IOException { bridge = CassandraBridgeFactory.get(version); // Test with random data - not highly compressible - testCompression(RandomUtils.randomBytes(4096)); + testCompression(RandomUtils.randomBytes(new Random(), 4096)); } @ParameterizedTest() diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/KryoSerializationTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/KryoSerializationTests.java index 5a6c44953..638898d5f 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/KryoSerializationTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/KryoSerializationTests.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.UUID; import com.google.common.collect.ImmutableList; @@ -110,7 +111,7 @@ public void testCqlField(CassandraBridge bridge) CqlField field = new CqlField(isPartitionKey, isClusteringKey && !isPartitionKey, false, - RandomUtils.randomAlphanumeric(5, 20), + RandomUtils.randomAlphanumeric(new Random(), 5, 20), cqlType, position); Output out = serialize(bridge.getVersion(), field); @@ -135,7 +136,7 @@ public void testCqlFieldSet(CassandraBridge bridge) CqlField field = new CqlField(isPartitionKey, isClusteringKey && !isPartitionKey, false, - RandomUtils.randomAlphanumeric(5, 20), + RandomUtils.randomAlphanumeric(new Random(), 5, 20), setType, position); Output out = serialize(bridge.getVersion(), field); @@ -160,7 +161,7 @@ public void testCqlFieldList(CassandraBridge bridge) CqlField field = new CqlField(isPartitionKey, isClusteringKey && !isPartitionKey, false, - RandomUtils.randomAlphanumeric(5, 20), + RandomUtils.randomAlphanumeric(new Random(), 5, 20), listType, position); Output out = serialize(bridge.getVersion(), field); @@ -186,7 +187,7 @@ public void testCqlFieldVector(CassandraBridge bridge) CqlField field = new CqlField(isPartitionKey, isClusteringKey && !isPartitionKey, false, - RandomUtils.randomAlphanumeric(5, 20), + RandomUtils.randomAlphanumeric(new Random(), 5, 20), vectorType, position); Output out = serialize(bridge.getVersion(), field); @@ -211,7 +212,7 @@ public void testCqlFieldMap(CassandraBridge bridge) CqlField field = new CqlField(isPartitionKey, isClusteringKey && !isPartitionKey, false, - RandomUtils.randomAlphanumeric(5, 20), + RandomUtils.randomAlphanumeric(new Random(), 5, 20), mapType, 2); Output out = serialize(bridge.getVersion(), field); @@ -236,7 +237,7 @@ public void testCqlUdt(CassandraBridge bridge) .withField("a", type1) .withField("b", type2) .build(); - CqlField field = new CqlField(false, false, false, RandomUtils.randomAlphanumeric(5, 20), udt, 2); + CqlField field = new CqlField(false, false, false, RandomUtils.randomAlphanumeric(new Random(), 5, 20), udt, 2); Output out = serialize(bridge.getVersion(), field); CqlField deserialized = deserialize(bridge.getVersion(), out, CqlField.class); assertThat(deserialized).isEqualTo(field); @@ -261,7 +262,7 @@ public void testCqlTuple(CassandraBridge bridge) bridge.set(bridge.text()), bridge.bigint(), bridge.map(type2, bridge.timeuuid())); - CqlField field = new CqlField(false, false, false, RandomUtils.randomAlphanumeric(5, 20), tuple, 2); + CqlField field = new CqlField(false, false, false, RandomUtils.randomAlphanumeric(new Random(), 5, 20), tuple, 2); Output out = serialize(bridge.getVersion(), field); CqlField deserialized = deserialize(bridge.getVersion(), out, CqlField.class); assertThat(deserialized).isEqualTo(field); diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/TestUtils.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/TestUtils.java index e2ca6a45f..9b4c659d7 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/TestUtils.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/TestUtils.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -371,7 +372,7 @@ public static String randomLowEntropyString() public static byte[] randomLowEntropyData() { - return randomLowEntropyData(RandomUtils.randomPositiveInt(16384 - 512) + 512); + return randomLowEntropyData(RandomUtils.randomPositiveInt(new Random(), 16384 - 512) + 512); } public static byte[] randomLowEntropyData(int size) diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/Tester.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/Tester.java index fb3b568d3..6e025cd0b 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/Tester.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/Tester.java @@ -27,6 +27,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -52,6 +53,7 @@ import org.quicktheories.core.Gen; import static org.apache.cassandra.bridge.CassandraBridgeFactory.getSparkSql; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -342,13 +344,13 @@ public void run(CassandraVersion... versions) private void run() { - qt().forAll(versions(), numSSTables()) + qt().forAll(versions(), numSSTables(), qtRandom()) .checkAssert(this::run); } private void run(CassandraVersion... versions) { - qt().forAll(arbitrary().pick(Arrays.asList(versions)), numSSTables()) + qt().forAll(arbitrary().pick(Arrays.asList(versions)), numSSTables(), qtRandom()) .checkAssert(this::run); } @@ -363,7 +365,7 @@ private Gen numSSTables() } // CHECKSTYLE IGNORE: Long method - private void run(CassandraVersion version, int numSSTables) + private void run(CassandraVersion version, int numSSTables, Random random) { TestUtils.runTest(version, (partitioner, directory, bridge) -> { String keyspace = "keyspace_" + UUID.randomUUID().toString().replaceAll("-", ""); @@ -381,7 +383,7 @@ private void run(CassandraVersion version, int numSSTables) TestSchema.TestRow testRow; do { - testRow = schema.randomRow(nullifyValueColumn); + testRow = schema.randomRow(nullifyValueColumn, random); } while (rows.containsKey(testRow.getPrimaryHexKey())); // Don't write duplicate rows @@ -508,9 +510,9 @@ private void run(CassandraVersion version, int numSSTables) }); } - public static TestSchema.TestRow newUniqueRow(TestSchema schema, Map rows) + public static TestSchema.TestRow newUniqueRow(TestSchema schema, Map rows, Random random) { - return newUniqueRow(schema::randomRow, rows); + return newUniqueRow(() -> schema.randomRow(random), rows); } private static TestSchema.TestRow newUniqueRow(Supplier rowProvider, diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/bulkwriter/benchmarks/ByteOperationsBenchmark.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/bulkwriter/benchmarks/ByteOperationsBenchmark.java index c565104cf..c12bf8c22 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/bulkwriter/benchmarks/ByteOperationsBenchmark.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/bulkwriter/benchmarks/ByteOperationsBenchmark.java @@ -33,6 +33,7 @@ import org.openjdk.jmh.annotations.Warmup; import java.nio.ByteBuffer; +import java.util.Random; @Warmup(iterations = 2) @Measurement(iterations = 3) @@ -51,7 +52,7 @@ public class ByteOperationsBenchmark @Setup(Level.Trial) public void setup() { - byte[] bytes = RandomUtils.randomBytes(numBytes); + byte[] bytes = RandomUtils.randomBytes(new Random(), numBytes); bytes1 = ByteBuffer.wrap(bytes.clone()); bytes2 = ByteBuffer.wrap(bytes.clone()); } diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/JDKSerializationTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/JDKSerializationTests.java index fdcd7b9d4..d96dbaf13 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/JDKSerializationTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/JDKSerializationTests.java @@ -25,6 +25,7 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; +import java.util.Random; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -129,7 +130,7 @@ public void testPartitionedDataLayer(CassandraBridge bridge) public void testCqlFieldSet(CassandraBridge bridge) { CqlField.CqlSet setType = bridge.set(bridge.text()); - CqlField field = new CqlField(true, false, false, RandomUtils.randomAlphanumeric(5, 20), setType, 10); + CqlField field = new CqlField(true, false, false, RandomUtils.randomAlphanumeric(new Random(), 5, 20), setType, 10); byte[] bytes = bridge.javaSerialize(field); CqlField deserialized = bridge.javaDeserialize(bytes, CqlField.class); assertThat(deserialized).isEqualTo(field); diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/TokenPartitionerTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/TokenPartitionerTests.java index 9d623181e..7ba34503f 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/TokenPartitionerTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/data/partitioner/TokenPartitionerTests.java @@ -23,6 +23,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Map; +import java.util.Random; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -33,6 +34,7 @@ import org.apache.cassandra.spark.TestUtils; import org.apache.cassandra.spark.utils.RandomUtils; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -46,11 +48,12 @@ public void testTokenPartitioner() { qt().forAll(TestUtils.partitioners(), arbitrary().pick(Arrays.asList(1, 3, 6, 12, 104, 208, 416)), - arbitrary().pick(Arrays.asList(1, 2, 4, 16, 128, 1024))) + arbitrary().pick(Arrays.asList(1, 2, 4, 16, 128, 1024)), + qtRandom()) .checkAssert(this::runTest); } - private void runTest(Partitioner partitioner, int numInstances, int numCores) + private void runTest(Partitioner partitioner, int numInstances, int numCores, Random random) { TokenPartitioner tokenPartitioner = new TokenPartitioner(TestUtils.createRing(partitioner, numInstances), 1, numCores); if (numInstances == 1 && numCores == 1) @@ -64,7 +67,7 @@ private void runTest(Partitioner partitioner, int numInstances, int numCores) // Generate some random tokens and verify they only exist in a single token partition Map tokens = IntStream.range(0, NUM_TOKEN_TESTS) - .mapToObj(token -> RandomUtils.randomBigInteger(partitioner)) + .mapToObj(token -> RandomUtils.randomBigInteger(random, partitioner)) .collect(Collectors.toMap(Function.identity(), token -> 0)); for (int partition = 0; partition < tokenPartitioner.numPartitions(); partition++) diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/CompactionTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/CompactionTests.java index afa7d6539..a9d69a900 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/CompactionTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/CompactionTests.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; @@ -34,7 +35,6 @@ import org.apache.cassandra.bridge.CassandraBridge; import org.apache.cassandra.spark.Tester; -import org.apache.cassandra.spark.utils.RandomUtils; import org.apache.cassandra.spark.utils.test.TestSchema; import org.apache.spark.sql.Row; @@ -49,6 +49,7 @@ public void testMultipleSSTablesCompaction(CassandraBridge bridge) { AtomicLong startTotal = new AtomicLong(0); AtomicLong newTotal = new AtomicLong(0); + Random random = new Random(); Map column1 = new HashMap<>(Tester.DEFAULT_NUM_ROWS); Map column2 = new HashMap<>(Tester.DEFAULT_NUM_ROWS); Tester.builder(TestSchema.builder(bridge) @@ -60,7 +61,7 @@ public void testMultipleSSTablesCompaction(CassandraBridge bridge) for (int row = 0; row < Tester.DEFAULT_NUM_ROWS; row++) { UUID pk = UUID.randomUUID(); - long c1 = RandomUtils.RANDOM.nextInt(10_000_000); + long c1 = random.nextInt(10_000_000); String c2 = UUID.randomUUID().toString(); startTotal.addAndGet(c1); column1.put(pk, c1); @@ -72,7 +73,7 @@ public void testMultipleSSTablesCompaction(CassandraBridge bridge) .withSSTableWriter(writer -> { for (UUID pk : column1.keySet()) { - long newBalance = (long) RandomUtils.RANDOM.nextInt(10_000_000) + column1.get(pk); + long newBalance = (long) random.nextInt(10_000_000) + column1.get(pk); assertThat(newBalance).isGreaterThan(column1.get(pk)); newTotal.addAndGet(newBalance); column1.put(pk, newBalance); @@ -160,6 +161,7 @@ public void testCompaction(CassandraBridge bridge) public void testSingleClusteringKey(CassandraBridge bridge) { AtomicLong total = new AtomicLong(0); + Random random = new Random(); Map testSum = new HashMap<>(); Set clusteringKeys = ImmutableSet.of(0, 1, 2, 3); for (int clusteringKey : clusteringKeys) @@ -179,7 +181,7 @@ public void testSingleClusteringKey(CassandraBridge bridge) for (int clusteringKey : clusteringKeys) { UUID accountId = UUID.randomUUID(); - long balance = RandomUtils.RANDOM.nextInt(10_000_000); + long balance = random.nextInt(10_000_000); total.addAndGet(balance); String name = UUID.randomUUID().toString().substring(0, 8); testSum.get(clusteringKey).add(balance); diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/DataTypeTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/DataTypeTests.java index e9fa11850..c2bb07ccd 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/DataTypeTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/DataTypeTests.java @@ -23,6 +23,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -37,7 +38,6 @@ import org.apache.cassandra.spark.TestUtils; import org.apache.cassandra.spark.Tester; import org.apache.cassandra.spark.data.CqlField; -import org.apache.cassandra.spark.utils.RandomUtils; import org.apache.cassandra.spark.utils.test.TestSchema; import org.apache.spark.sql.Row; import org.quicktheories.core.Gen; @@ -838,6 +838,7 @@ public void testUdtTupleInnerNulls(CassandraBridge bridge) int numRows = 50; int midPoint = numRows / 2; + Random random = new Random(); Map>> udtSetValues = new LinkedHashMap<>(numRows); Map tupleValues = new LinkedHashMap<>(numRows); for (int tupleIndex = 0; tupleIndex < numRows; tupleIndex++) @@ -851,9 +852,9 @@ public void testUdtTupleInnerNulls(CassandraBridge bridge) udt.put("b", udtIndex < midPoint ? UUID.randomUUID().toString() : null); udtSet.add(udt); } - Object[] tuple = new Object[]{RandomUtils.RANDOM.nextLong(), + Object[] tuple = new Object[]{random.nextLong(), tupleIndex < midPoint ? UUID.randomUUID().toString() : null, - RandomUtils.RANDOM.nextInt()}; + random.nextInt()}; udtSetValues.put(pk, udtSet); tupleValues.put(pk, tuple); diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/SchemaTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/SchemaTests.java index f2d452adb..7ed972d7a 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/SchemaTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/SchemaTests.java @@ -22,6 +22,7 @@ import java.math.BigInteger; import java.util.HashMap; import java.util.Map; +import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -41,7 +42,6 @@ import org.apache.cassandra.spark.TestUtils; import org.apache.cassandra.spark.Tester; import org.apache.cassandra.spark.data.CqlField; -import org.apache.cassandra.spark.utils.RandomUtils; import org.apache.cassandra.spark.utils.test.TestSchema; import org.apache.spark.sql.Row; import org.apache.spark.sql.functions; @@ -292,6 +292,7 @@ public void testMultipleSSTableCompacted(CassandraVersion version) // In earlier versions of Spark the long value would just overflow and the aggregated // value was incorrect. AtomicReference total = new AtomicReference<>(BigInteger.ZERO); + Random random = new Random(); Map rows = new HashMap<>(Tester.DEFAULT_NUM_ROWS); Tester.builder(schemaBuilder) // Don't write random data @@ -302,7 +303,7 @@ public void testMultipleSSTableCompacted(CassandraVersion version) { TestSchema schema = schemaBuilder.build(); schema.setCassandraVersion(version); - TestSchema.TestRow testRow = schema.randomRow(); + TestSchema.TestRow testRow = schema.randomRow(random); rows.put(testRow.getUUID("a"), testRow); writer.write(testRow.allValues()); } @@ -313,7 +314,7 @@ public void testMultipleSSTableCompacted(CassandraVersion version) for (TestSchema.TestRow testRow : ImmutableSet.copyOf(rows.values())) { // Update rows with new values - TestSchema.TestRow newTestRow = testRow.copy("e", RandomUtils.RANDOM.nextLong()) + TestSchema.TestRow newTestRow = testRow.copy("e", random.nextLong()) .copy("d", UUID.randomUUID().toString().substring(0, 10)); rows.put(testRow.getUUID("a"), newTestRow); writer.write(newTestRow.allValues()); @@ -323,7 +324,7 @@ public void testMultipleSSTableCompacted(CassandraVersion version) for (TestSchema.TestRow testRow : ImmutableSet.copyOf(rows.values())) { // Update rows with new values - this should be the final values seen by Spark - TestSchema.TestRow newTestRow = testRow.copy("e", RandomUtils.RANDOM.nextLong()) + TestSchema.TestRow newTestRow = testRow.copy("e", random.nextLong()) .copy("d", UUID.randomUUID().toString().substring(0, 10)); rows.put(testRow.getUUID("a"), newTestRow); total.updateAndGet(t -> t.add(BigInteger.valueOf(newTestRow.getLong("e")))); @@ -363,6 +364,7 @@ public void testUdtsWithNulls(CassandraBridge bridge) .withField("c", bridge.text()) .build(); Map> values = new HashMap<>(Tester.DEFAULT_NUM_ROWS); + Random random = new Random(); Tester.builder(keyspace -> TestSchema.builder(bridge) .withKeyspace(keyspace) @@ -377,13 +379,13 @@ public void testUdtsWithNulls(CassandraBridge bridge) for (long pk = 0; pk < Tester.DEFAULT_NUM_ROWS; pk++) { Map value = ImmutableMap.of( - pk < midPoint ? "a" : "b", bridge.text().randomValue().toString(), - "c", bridge.text().randomValue().toString()); + pk < midPoint ? "a" : "b", bridge.text().randomValue(random).toString(), + "c", bridge.text().randomValue(random).toString()); values.put(pk, value); writer.write(pk, bridge.toUserTypeValue(type, value), - bridge.text().randomValue(), - bridge.timestamp().randomValue(), - bridge.aInt().randomValue()); + bridge.text().randomValue(random), + bridge.timestamp().randomValue(random), + bridge.aInt().randomValue(random)); } }) .withCheck(dataset -> { diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/TombstoneTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/TombstoneTests.java index 2d98ad349..43c3fc574 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/TombstoneTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/endtoend/TombstoneTests.java @@ -26,10 +26,10 @@ import org.apache.cassandra.bridge.CassandraBridge; import org.apache.cassandra.spark.TestUtils; import org.apache.cassandra.spark.Tester; -import org.apache.cassandra.spark.utils.RandomUtils; import org.apache.cassandra.spark.utils.test.TestSchema; import org.apache.spark.sql.Row; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.characters; @@ -45,10 +45,10 @@ public void testPartitionTombstoneInt(CassandraBridge bridge) int numRows = 100; int numColumns = 10; qt().withExamples(20) - .forAll(integers().between(0, numRows - 2)) - .checkAssert(deleteRangeStart -> { + .forAll(integers().between(0, numRows - 2), qtRandom()) + .checkAssert((deleteRangeStart, random) -> { assert 0 <= deleteRangeStart && deleteRangeStart < numRows; - int deleteRangeEnd = deleteRangeStart + RandomUtils.RANDOM.nextInt(numRows - deleteRangeStart - 1) + 1; + int deleteRangeEnd = deleteRangeStart + random.nextInt(numRows - deleteRangeStart - 1) + 1; assert deleteRangeStart < deleteRangeEnd && deleteRangeEnd < numRows; Tester.builder(TestSchema.basicBuilder(bridge) @@ -138,10 +138,10 @@ public void testRangeTombstoneInt(CassandraBridge bridge) int numRows = 10; int numColumns = 128; qt().withExamples(10) - .forAll(integers().between(0, numColumns - 1)) - .checkAssert(startBound -> { + .forAll(integers().between(0, numColumns - 1), qtRandom()) + .checkAssert((startBound, random) -> { assertThat(startBound).isLessThan(numColumns); - int endBound = startBound + RandomUtils.RANDOM.nextInt(numColumns - startBound); + int endBound = startBound + random.nextInt(numColumns - startBound); assertThat(endBound).isBetween(startBound, numColumns); int numTombstones = endBound - startBound; @@ -189,10 +189,10 @@ public void testRangeTombstoneString(CassandraBridge bridge) int numRows = 10; int numColumns = 128; qt().withExamples(10) - .forAll(characters().ascii()) - .checkAssert(startBound -> { + .forAll(characters().ascii(), qtRandom()) + .checkAssert((startBound, random) -> { assertThat((int) startBound).isLessThanOrEqualTo(numColumns); - char endBound = (char) (startBound + RandomUtils.RANDOM.nextInt(numColumns - startBound)); + char endBound = (char) (startBound + random.nextInt(numColumns - startBound)); assertThat(endBound).isBetween(startBound, (char) numColumns); int numTombstones = endBound - startBound; diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/CassandraBridgeUtilTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/CassandraBridgeUtilTests.java index b9cb8449f..6a10bcf9b 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/CassandraBridgeUtilTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/CassandraBridgeUtilTests.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -63,6 +64,8 @@ public class CassandraBridgeUtilTests { + private static final Random RANDOM = new Random(); + @TempDir private static Path tempPath; @@ -72,7 +75,7 @@ public void testLastRepairTime() runTest((partitioner, bridge, schema, testDir) -> { writeSSTable(partitioner, bridge, schema, testDir, (writer) -> IntStream.range(0, DEFAULT_NUM_ROWS) - .forEach(i -> writer.write(randomAlphanumeric(), randomAlphanumeric(), randomAlphanumeric())) + .forEach(i -> writer.write(randomAlphanumeric(RANDOM), randomAlphanumeric(RANDOM), randomAlphanumeric(RANDOM))) ); try @@ -92,10 +95,10 @@ public void testBuildPartitionKey() runTest((partitioner, bridge, schema, testDir) -> { ByteBuffer pk1 = bridge.encodePartitionKey(partitioner, "ks", schema.createStatement, Collections.singletonList("a")); assertThat(ByteBufferUtils.string(pk1)).isEqualTo("a"); - String str1 = randomAlphanumeric(); + String str1 = randomAlphanumeric(RANDOM); ByteBuffer pk2 = bridge.encodePartitionKey(partitioner, "ks", schema.createStatement, Collections.singletonList(str1)); assertThat(ByteBufferUtils.string(pk2)).isEqualTo(str1); - List keys = Arrays.asList(randomAlphanumeric(), randomAlphanumeric(), randomAlphanumeric()); + List keys = Arrays.asList(randomAlphanumeric(RANDOM), randomAlphanumeric(RANDOM), randomAlphanumeric(RANDOM)); TestSchema threePkSchema = TestSchema.builder(bridge) .withPartitionKey("a", bridge.text()) @@ -116,7 +119,7 @@ public void testOverlaps() { runTest((partitioner, bridge, schema, testDir) -> { // write SSTable - List keys = IntStream.range(0, 3).mapToObj(i -> randomAlphanumeric()).collect(Collectors.toList()); + List keys = IntStream.range(0, 3).mapToObj(i -> randomAlphanumeric(RANDOM)).collect(Collectors.toList()); List buffers = bridge.encodePartitionKeys( partitioner, schema.keyspace, @@ -132,7 +135,7 @@ public void testOverlaps() .collect(Collectors.toList()); assertThat(TestSSTable.allIn(testDir)).isEmpty(); writeSSTable(partitioner, bridge, schema, testDir, - (writer) -> keys.forEach(key -> writer.write(key, randomAlphanumeric(), randomAlphanumeric()))); + (writer) -> keys.forEach(key -> writer.write(key, randomAlphanumeric(RANDOM), randomAlphanumeric(RANDOM)))); assertThat(TestSSTable.allIn(testDir)).hasSize(1); TestSSTable ssTable = (TestSSTable) TestSSTable.firstIn(testDir); @@ -179,7 +182,7 @@ public void testContains() { runTest((partitioner, bridge, schema, testDir) -> { // write SSTable - Set keys = IntStream.range(0, 25).mapToObj(i -> randomAlphanumeric()).collect(Collectors.toSet()); + Set keys = IntStream.range(0, 25).mapToObj(i -> randomAlphanumeric(RANDOM)).collect(Collectors.toSet()); List buffers = bridge.encodePartitionKeys( partitioner, schema.keyspace, @@ -190,7 +193,7 @@ public void testContains() ); assertThat(TestSSTable.allIn(testDir)).isEmpty(); writeSSTable(partitioner, bridge, schema, testDir, - (writer) -> keys.forEach(key -> writer.write(key, randomAlphanumeric(), randomAlphanumeric()))); + (writer) -> keys.forEach(key -> writer.write(key, randomAlphanumeric(RANDOM), randomAlphanumeric(RANDOM)))); assertThat(TestSSTable.allIn(testDir)).hasSize(1); TestSSTable ssTable = (TestSSTable) TestSSTable.firstIn(testDir); @@ -202,7 +205,7 @@ public void testContains() assertThat(result.stream().allMatch(boolValue -> boolValue)).isTrue(); // random keys should return some negatives for keys not contained in the SSTable - List otherKeys = IntStream.range(0, DEFAULT_NUM_ROWS).mapToObj(i -> randomAlphanumeric(keys)).collect(Collectors.toList()); + List otherKeys = IntStream.range(0, DEFAULT_NUM_ROWS).mapToObj(i -> randomAlphanumeric(RANDOM, keys)).collect(Collectors.toList()); List randomBuffers = bridge.encodePartitionKeys( partitioner, schema.keyspace, @@ -248,8 +251,8 @@ public void testReadKeys() runTest( (partitioner, bridge, schema, testDir) -> { Map expected = IntStream.range(0, DEFAULT_NUM_ROWS) - .mapToObj(i -> randomAlphanumeric(32)) - .collect(Collectors.toMap(Function.identity(), i -> randomAlphanumeric(32))); + .mapToObj(i -> randomAlphanumeric(RANDOM, 32)) + .collect(Collectors.toMap(Function.identity(), i -> randomAlphanumeric(RANDOM, 32))); writeSSTable(partitioner, bridge, schema, testDir, (writer) -> expected.forEach((key, value) -> writer.write(key, value, value))); List all = TestSSTable.allIn(testDir); diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/DataTypeSerializationTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/DataTypeSerializationTests.java index b24bfc58a..8d0771974 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/DataTypeSerializationTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/reader/DataTypeSerializationTests.java @@ -54,6 +54,7 @@ import org.apache.spark.unsafe.types.UTF8String; import static org.apache.cassandra.bridge.CassandraBridgeFactory.getSparkSql; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.apache.cassandra.spark.TestUtils.runTest; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; @@ -114,11 +115,11 @@ public void testBoolean() @Test public void testTimeUUID() { - qt().forAll(TestUtils.bridges()).checkAssert(bridge -> { - assertThat(toTimeUUID(bridge, RandomUtils.getRandomTimeUUIDForTesting())).isInstanceOf(UTF8String.class); + qt().forAll(TestUtils.bridges(), qtRandom()).checkAssert((bridge, random) -> { + assertThat(toTimeUUID(bridge, RandomUtils.getRandomTimeUUIDForTesting(random))).isInstanceOf(UTF8String.class); for (int test = 0; test < MAX_TESTS; test++) { - UUID expected = RandomUtils.getRandomTimeUUIDForTesting(); + UUID expected = RandomUtils.getRandomTimeUUIDForTesting(random); assertThat(toTimeUUID(bridge, expected).toString()).isEqualTo(expected.toString()); } }); @@ -257,11 +258,11 @@ public void testVarchar() @Test public void testInet() { - qt().forAll(TestUtils.bridges()).checkAssert(bridge -> { - assertThat(toInet(bridge, RandomUtils.randomInet())).isInstanceOf(byte[].class); + qt().forAll(TestUtils.bridges(), qtRandom()).checkAssert((bridge, random) -> { + assertThat(toInet(bridge, RandomUtils.randomInet(random))).isInstanceOf(byte[].class); for (int test = 0; test < MAX_TESTS; test++) { - InetAddress expected = RandomUtils.randomInet(); + InetAddress expected = RandomUtils.randomInet(random); assertThat((byte[]) toInet(bridge, expected)).isEqualTo(expected.getAddress()); } }); @@ -320,12 +321,12 @@ public void testTimestamp() @Test public void testBlob() { - qt().forAll(TestUtils.bridges()).checkAssert(bridge -> { - assertThat(toBlob(bridge, ByteBuffer.wrap(RandomUtils.randomBytes(5)))).isInstanceOf(byte[].class); + qt().forAll(TestUtils.bridges(), qtRandom()).checkAssert((bridge, random) -> { + assertThat(toBlob(bridge, ByteBuffer.wrap(RandomUtils.randomBytes(random, 5)))).isInstanceOf(byte[].class); for (int test = 0; test < MAX_TESTS; test++) { - int size = RandomUtils.RANDOM.nextInt(1024); - byte[] expected = RandomUtils.randomBytes(size); + int size = random.nextInt(1024); + byte[] expected = RandomUtils.randomBytes(random, size); assertThat((byte[]) toBlob(bridge, ByteBuffer.wrap(expected))).isEqualTo(expected); } }); @@ -355,11 +356,11 @@ public void testSmallInt() @Test public void testTinyInt() { - qt().forAll(TestUtils.bridges()).checkAssert(bridge -> { - assertThat(toTinyInt(bridge, RandomUtils.randomByte())).isInstanceOf(Byte.class); + qt().forAll(TestUtils.bridges(), qtRandom()).checkAssert((bridge, random) -> { + assertThat(toTinyInt(bridge, RandomUtils.randomByte(random))).isInstanceOf(Byte.class); for (int test = 0; test < MAX_TESTS; test++) { - byte expected = RandomUtils.randomByte(); + byte expected = RandomUtils.randomByte(random); assertThat(toTinyInt(bridge, expected)).isEqualTo(expected); } }); @@ -369,7 +370,7 @@ public void testTinyInt() public void testSerialization() { // CassandraBridge.serialize is mostly used for unit tests - qt().forAll(TestUtils.bridges()).checkAssert(bridge -> { + qt().forAll(TestUtils.bridges(), qtRandom()).checkAssert((bridge, random) -> { // BLOB, VARINT assertThat(toAscii(bridge, "ABC").toString()).isEqualTo("ABC"); assertThat(toBigInt(bridge, 500L)).isEqualTo(500L); @@ -401,7 +402,7 @@ public void testSerialization() assertThat(toTime(bridge, 5002839L)).isEqualTo(5002839L); Date now = new Date(); assertThat(toTimestamp(bridge, now)).isEqualTo(now.getTime() * 1000L); - UUID timeUuid = RandomUtils.getRandomTimeUUIDForTesting(); + UUID timeUuid = RandomUtils.getRandomTimeUUIDForTesting(random); assertThat(UUID.fromString(toTimeUUID(bridge, timeUuid).toString())).isEqualTo(timeUuid); assertThat(toTinyInt(bridge, (byte) 100)).isEqualTo((byte) 100); UUID uuid = UUID.randomUUID(); @@ -415,11 +416,11 @@ public void testSerialization() public void testList() { runTest((partitioner, directory, bridge) -> - qt().forAll(TestUtils.cql3Type(bridge)).checkAssert(type -> { + qt().forAll(TestUtils.cql3Type(bridge), qtRandom()).checkAssert((type, random) -> { CqlField.CqlList list = bridge.list(type); SparkType sparkType = getSparkSql(bridge).toSparkType(type); List expected = IntStream.range(0, 128) - .mapToObj(index -> type.randomValue()) + .mapToObj(index -> type.randomValue(random)) .collect(Collectors.toList()); ByteBuffer buffer = list.serialize(expected); List actual = Arrays.asList(((ArrayData) list.deserializeToType(getSparkSql(bridge), buffer)).array()); @@ -435,11 +436,11 @@ public void testList() public void testSet() { runTest((partitioner, directory, bridge) -> - qt().forAll(TestUtils.cql3Type(bridge)).assuming(CqlField.CqlType::supportedAsSetElement).checkAssert(type -> { + qt().forAll(TestUtils.cql3Type(bridge), qtRandom()).assuming((type, random) -> type.supportedAsSetElement()).checkAssert((type, random) -> { CqlField.CqlSet set = bridge.set(type); SparkType sparkType = getSparkSql(bridge).toSparkType(type); Set expected = IntStream.range(0, 128) - .mapToObj(integer -> type.randomValue()) + .mapToObj(integer -> type.randomValue(random)) .collect(Collectors.toSet()); ByteBuffer buffer = set.serialize(expected); Set actual = new HashSet<>(Arrays.asList(((ArrayData) set.deserializeToType(getSparkSql(bridge), buffer)).array())); @@ -455,9 +456,9 @@ public void testSet() public void testMap() { runTest((partitioner, directory, bridge) -> - qt().forAll(TestUtils.cql3Type(bridge), TestUtils.cql3Type(bridge)) - .assuming((keyType, valueType) -> keyType.supportedAsMapKey()) - .checkAssert((keyType, valueType) -> { + qt().forAll(TestUtils.cql3Type(bridge), TestUtils.cql3Type(bridge), qtRandom()) + .assuming((keyType, valueType, random) -> keyType.supportedAsMapKey()) + .checkAssert((keyType, valueType, random) -> { CqlField.CqlMap map = bridge.map(keyType, valueType); SparkType keySparkType = getSparkSql(bridge).toSparkType(keyType); SparkType valueSparkType = getSparkSql(bridge).toSparkType(valueType); @@ -469,9 +470,9 @@ public void testMap() Object key = null; while (key == null || expected.containsKey(key)) { - key = keyType.randomValue(); + key = keyType.randomValue(random); } - expected.put(key, valueType.randomValue()); + expected.put(key, valueType.randomValue(random)); } ByteBuffer buffer = map.serialize(expected); ArrayBasedMapData mapData = ((ArrayBasedMapData) map.deserializeToType(getSparkSql(bridge), buffer)); @@ -497,13 +498,13 @@ public void testMap() public void testUdts() { runTest((partitioner, directory, bridge) -> - qt().forAll(TestUtils.cql3Type(bridge), TestUtils.cql3Type(bridge)).checkAssert((firstType, secondType) -> { + qt().forAll(TestUtils.cql3Type(bridge), TestUtils.cql3Type(bridge), qtRandom()).checkAssert((firstType, secondType, random) -> { CqlField.CqlUdt udt = bridge.udt("keyspace", "testudt") .withField("a", firstType) .withField("b", bridge.ascii()) .withField("c", secondType) .build(); - Map expected = (Map) udt.randomValue(); + Map expected = (Map) udt.randomValue(random); assert expected != null; ByteBuffer buffer = udt.serializeUdt(expected); Map actual = udt.deserializeUdt(getSparkSql(bridge), buffer, false); @@ -520,14 +521,14 @@ public void testUdts() public void testTuples() { runTest((partitioner, directory, bridge) -> - qt().forAll(TestUtils.cql3Type(bridge), TestUtils.cql3Type(bridge)).checkAssert((firstType, secondType) -> { + qt().forAll(TestUtils.cql3Type(bridge), TestUtils.cql3Type(bridge), qtRandom()).checkAssert((firstType, secondType, random) -> { CqlField.CqlTuple tuple = bridge.tuple(firstType, bridge.ascii(), secondType, bridge.timestamp(), bridge.uuid(), bridge.varchar()); - Object[] expected = (Object[]) tuple.randomValue(); + Object[] expected = (Object[]) tuple.randomValue(random); assert expected != null; ByteBuffer buffer = tuple.serializeTuple(expected); GenericInternalRow row = (GenericInternalRow) getSparkSql(bridge).convert(tuple, tuple.deserializeTuple(buffer, false), false); diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/sparksql/SparkRowIteratorTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/sparksql/SparkRowIteratorTests.java index e13aeb529..337ebe858 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/sparksql/SparkRowIteratorTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/sparksql/SparkRowIteratorTests.java @@ -23,6 +23,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.List; +import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -147,7 +148,7 @@ public void testTuple(CassandraBridge bridge) private static void runTest(CassandraVersion version, TestSchema schema) { - runTest(version, schema, schema.randomRows(NUM_ROWS)); + runTest(version, schema, schema.randomRows(NUM_ROWS, new Random())); } private static void runTest(CassandraVersion version, TestSchema schema, TestSchema.TestRow[] testRows) diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamHttpTest.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamHttpTest.java index 8e2280a71..f72805bd3 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamHttpTest.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamHttpTest.java @@ -29,6 +29,7 @@ import java.security.MessageDigest; import java.util.Arrays; import java.util.List; +import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -267,12 +268,13 @@ private void runHttpTest(long size, Long maxBufferSize, Long chunkBufferSize) { Path path = Files.createTempFile(directory, null, null); MessageDigest digest = DigestUtils.getMd5Digest(); + Random random = new Random(); try (BufferedOutputStream out = new BufferedOutputStream(Files.newOutputStream(path))) { long remaining = size; while (remaining > 0) { - byte[] bytes = RandomUtils.randomBytes((int) Math.min(remaining, BufferingInputStreamTests.DEFAULT_CHUNK_SIZE)); + byte[] bytes = RandomUtils.randomBytes(random, (int) Math.min(remaining, BufferingInputStreamTests.DEFAULT_CHUNK_SIZE)); out.write(bytes); digest.update(bytes); remaining -= bytes.length; diff --git a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamTests.java b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamTests.java index b7c305241..b81901d7e 100644 --- a/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamTests.java +++ b/cassandra-analytics-core/src/test/java/org/apache/cassandra/spark/utils/BufferingInputStreamTests.java @@ -24,6 +24,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -57,6 +58,7 @@ */ public class BufferingInputStreamTests { + private static final Random RANDOM = new Random(); private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1); private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(4, new ThreadFactoryBuilder().setNameFormat("sstable-tests-%d") @@ -360,7 +362,7 @@ public void testUnalignedEndReading() throws IOException @Override public void request(long start, long end, StreamConsumer consumer) { - byte[] bytes = RandomUtils.randomBytes((int) (end - start + 1)); + byte[] bytes = RandomUtils.randomBytes(RANDOM, (int) (end - start + 1)); StreamBuffer buffer = StreamBuffer.wrap(bytes); returnedBuffers.add(bytes); consumer.onRead(buffer); @@ -435,7 +437,7 @@ private static StreamBuffer randomBuffer() private static StreamBuffer randomBuffer(int size) { - return StreamBuffer.wrap(RandomUtils.randomBytes(size)); + return StreamBuffer.wrap(RandomUtils.randomBytes(RANDOM, size)); } @SuppressWarnings("StatementWithEmptyBody") @@ -465,6 +467,6 @@ private static void writeBuffers(StreamConsumer consumer, ImmutableList values; + + FixedSequenceSource(long... values) + { + this.values = new ArrayDeque<>(); + for (long value : values) + { + this.values.add(value); + } + } + + @Override + public long next(Constraint constraints) + { + return values.poll(); + } + + @Override + public DetatchedRandomnessSource detach() + { + throw new UnsupportedOperationException(); + } + + @Override + public void registerFailedAssumption() + { + } + + int remaining() + { + return values.size(); + } + } +} diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/MixedSSTableVersionTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/MixedSSTableVersionTest.java index b1c0758f8..bf8c88ecd 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/MixedSSTableVersionTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/MixedSSTableVersionTest.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.UUID; @@ -167,7 +168,7 @@ private Dataset writeDataAndFlush() private List generateRandomData() { - int rowCount = RandomUtils.nextInt(3, 10); + int rowCount = RandomUtils.nextInt(new Random(), 3, 10); List data = new ArrayList<>(rowCount); for (int i = 0; i < rowCount; i++) { diff --git a/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/CommonTestUtils.java b/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/CommonTestUtils.java index de5167a09..b6af05218 100644 --- a/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/CommonTestUtils.java +++ b/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/CommonTestUtils.java @@ -19,8 +19,11 @@ package org.apache.cassandra.spark; +import java.util.Random; + import org.apache.cassandra.bridge.CassandraBridge; import org.apache.cassandra.spark.data.CqlField; +import org.apache.cassandra.spark.utils.test.QTRandom; import org.quicktheories.core.Gen; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -37,4 +40,13 @@ public static Gen cql3Type(CassandraBridge bridge) { return arbitrary().pick(bridge.supportedTypes()); } + + /** + * A {@link Random} whose entropy is drawn from QuickTheories' own seeded PRNG, so that any randomness + * consumed via the returned instance is reproducible from the same QT seed as the rest of the property. + */ + public static Gen qtRandom() + { + return QTRandom::new; + } } diff --git a/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/QTRandom.java b/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/QTRandom.java new file mode 100644 index 000000000..8e42558d5 --- /dev/null +++ b/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/QTRandom.java @@ -0,0 +1,40 @@ +/* + * 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.spark.utils.test; + +import java.util.Random; + +import org.quicktheories.core.RandomnessSource; +import org.quicktheories.impl.Constraint; + +/** + * A {@link Random} seeded from QuickTheories' PRNG, so tests using it are reproducible from QT's seed. + * Draws one seed value from the {@link RandomnessSource} and behaves as an ordinary {@link Random} + * afterwards - QT records every draw from a {@link RandomnessSource} for shrinking, so forwarding all + * calls to it would grow unbounded on data-heavy tests. + */ +public final class QTRandom extends Random +{ + public QTRandom(RandomnessSource source) + { + super(source.next(Constraint.between(Long.MIN_VALUE, Long.MAX_VALUE))); + } +} + diff --git a/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/TestSchema.java b/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/TestSchema.java index 505ada883..a0533553c 100644 --- a/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/TestSchema.java +++ b/cassandra-bridge/src/testFixtures/java/org/apache/cassandra/spark/utils/test/TestSchema.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -545,32 +546,32 @@ public void writeTombstoneSSTable(Path directory, } @SuppressWarnings("SameParameterValue") - public TestRow[] randomRows(int numRows) + public TestRow[] randomRows(int numRows, Random random) { TestSchema.TestRow[] testRows = new TestSchema.TestRow[numRows]; for (int testRow = 0; testRow < testRows.length; testRow++) { - testRows[testRow] = randomRow(); + testRows[testRow] = randomRow(random); } return testRows; } - public TestRow randomPartitionDelete() + public TestRow randomPartitionDelete(Random random) { - return randomRow(field -> !field.isPartitionKey()); + return randomRow(field -> !field.isPartitionKey(), random); } - public TestRow randomRow() + public TestRow randomRow(Random random) { - return randomRow(false); + return randomRow(false, random); } - public TestRow randomRow(boolean nullifyValueColumn) + public TestRow randomRow(boolean nullifyValueColumn, Random random) { - return randomRow(field -> nullifyValueColumn && field.isValueColumn()); + return randomRow(field -> nullifyValueColumn && field.isValueColumn(), random); } - private TestRow randomRow(Predicate nullifiedFields) + private TestRow randomRow(Predicate nullifiedFields, Random random) { final Object[] values = new Object[allFields.size()]; for (final CqlField field : allFields) @@ -583,11 +584,11 @@ private TestRow randomRow(Predicate nullifiedFields) { if (field.type().getClass().getSimpleName().equals("Blob") && blobSize != null) { - values[field.position()] = RandomUtils.randomByteBuffer(blobSize); + values[field.position()] = RandomUtils.randomByteBuffer(random, blobSize); } else { - values[field.position()] = field.type().randomValue(minCollectionSize); + values[field.position()] = field.type().randomValue(minCollectionSize, random); } } } diff --git a/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java b/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java index 68e012bd8..14e74bae8 100644 --- a/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java +++ b/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Random; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; @@ -49,7 +50,7 @@ private TestUtils() public static byte[] randomLowEntropyData() { - return randomLowEntropyData(RandomUtils.randomPositiveInt(16384 - 512) + 512); + return randomLowEntropyData(RandomUtils.randomPositiveInt(new Random(), 16384 - 512) + 512); } public static byte[] randomLowEntropyData(int size) diff --git a/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java b/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java index 6a0eebf0d..48789410a 100644 --- a/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java +++ b/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java @@ -29,7 +29,6 @@ import org.apache.cassandra.bridge.CassandraBridgeImplementation; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.CompositeType; -import org.apache.cassandra.spark.data.CqlField; import org.apache.cassandra.spark.data.CqlTable; import org.apache.cassandra.spark.data.CqlType; import org.apache.cassandra.spark.data.converter.SparkSqlTypeConverter; @@ -37,6 +36,7 @@ import org.apache.cassandra.spark.utils.test.TestSchema; import static org.assertj.core.api.Assertions.assertThat; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -49,16 +49,16 @@ public class PartitionKeyTests @SuppressWarnings("static-access") public void testBuildPartitionKey() { - qt().forAll(arbitrary().pick(BRIDGE.supportedTypes())) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) - .checkAssert(partitionKeyType -> { + qt().forAll(arbitrary().pick(BRIDGE.supportedTypes()), qtRandom()) + .assuming((partitionKeyType, random) -> partitionKeyType.supportedAsPrimaryKeyColumn()) + .checkAssert((partitionKeyType, random) -> { CqlTable table = TestSchema.builder(BRIDGE) .withPartitionKey("a", partitionKeyType) .withClusteringKey("b", BRIDGE.aInt()) .withColumn("c", BRIDGE.aInt()) .build() .buildTable(); - Object value = partitionKeyType.randomValue(100); + Object value = partitionKeyType.randomValue(100, random); String string = ((CqlType) partitionKeyType).serializer().toString(value); ByteBuffer buffer = BRIDGE.buildPartitionKey(table, Collections.singletonList(string)); Object cassandraValue = partitionKeyType.deserializeToJavaType(buffer); @@ -76,9 +76,9 @@ public void testBuildPartitionKey() @SuppressWarnings("static-access") public void testBuildCompositePartitionKey() { - qt().forAll(arbitrary().pick(BRIDGE.supportedTypes())) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) - .checkAssert(partitionKeyType -> { + qt().forAll(arbitrary().pick(BRIDGE.supportedTypes()), qtRandom()) + .assuming((partitionKeyType, random) -> partitionKeyType.supportedAsPrimaryKeyColumn()) + .checkAssert((partitionKeyType, random) -> { CqlTable table = TestSchema.builder(BRIDGE) .withPartitionKey("a", BRIDGE.aInt()) .withPartitionKey("b", partitionKeyType) @@ -90,10 +90,10 @@ public void testBuildCompositePartitionKey() List> partitionKeyColumnTypes = BRIDGE.partitionKeyColumnTypes(table); CompositeType compositeType = CompositeType.getInstance(partitionKeyColumnTypes); - int columnA = (int) BRIDGE.aInt().randomValue(1024); - Object columnB = partitionKeyType.randomValue(1024); + int columnA = (int) BRIDGE.aInt().randomValue(1024, random); + Object columnB = partitionKeyType.randomValue(1024, random); String columnBString = ((CqlType) partitionKeyType).serializer().toString(columnB); - String columnC = (String) BRIDGE.text().randomValue(1024); + String columnC = (String) BRIDGE.text().randomValue(1024, random); ByteBuffer buffer = BRIDGE.buildPartitionKey(table, Arrays.asList(Integer.toString(columnA), columnBString, columnC)); ByteBuffer[] buffers = compositeType.split(buffer); diff --git a/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java b/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java index 18821a2b8..d38f6533d 100644 --- a/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java +++ b/cassandra-five-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java @@ -85,6 +85,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.apache.cassandra.spark.TestUtils.SSTABLE_FORMATS; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; @@ -971,8 +972,8 @@ public void skippedRepairedSSTable(SSTable ssTable, long repairedAt) @Test public void testPartitionKeyFilter() { - qt().forAll(arbitrary().enumValues(Partitioner.class)) - .checkAssert(partitioner -> { + qt().forAll(arbitrary().enumValues(Partitioner.class), qtRandom()) + .checkAssert((partitioner, random) -> { try (TemporaryDirectory directory = new TemporaryDirectory()) { TestSchema schema = TestSchema.builder(BRIDGE) @@ -983,7 +984,7 @@ public void testPartitionKeyFilter() .build(); CqlTable cqlTable = schema.buildTable(); int numSSTables = 24; - String partitionKeyStr = (String) BRIDGE.text().randomValue(1024); + String partitionKeyStr = (String) BRIDGE.text().randomValue(1024, random); AbstractMap.SimpleEntry partitionKey = BRIDGE.getPartitionKey(cqlTable, partitioner, Collections.singletonList(partitionKeyStr)); PartitionKeyFilter partitionKeyFilter = PartitionKeyFilter.create(partitionKey.getKey(), @@ -1003,8 +1004,8 @@ public void testPartitionKeyFilter() // Write partition key in last SSTable only for (int column = 0; column < COLUMNS; column++) { - expectedC[column] = (int) BRIDGE.aInt().randomValue(1024); - expectedD[column] = (String) BRIDGE.text().randomValue(1024); + expectedC[column] = (int) BRIDGE.aInt().randomValue(1024, random); + expectedD[column] = (String) BRIDGE.text().randomValue(1024, random); writer.write(partitionKeyStr, column, expectedC[column], expectedD[column]); } } @@ -1016,12 +1017,12 @@ public void testPartitionKeyFilter() String key = null; while (key == null || key.equals(partitionKeyStr)) { - key = (String) BRIDGE.text().randomValue(1024); + key = (String) BRIDGE.text().randomValue(1024, random); } writer.write(key, row, - BRIDGE.aInt().randomValue(1024), - BRIDGE.text().randomValue(1024)); + BRIDGE.aInt().randomValue(1024, random), + BRIDGE.text().randomValue(1024, random)); } } }); diff --git a/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlVector.java b/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlVector.java index cef8a1b8a..fd0abb79d 100644 --- a/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlVector.java +++ b/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlVector.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Objects; +import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -110,10 +111,10 @@ protected void setInnerValueInternal(SettableByIndexData udtValue, int positi } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { return IntStream.range(0, dimensions) - .mapToObj(element -> type().randomValue(minCollectionSize)) + .mapToObj(element -> type().randomValue(minCollectionSize, random)) .collect(Collectors.toList()); } diff --git a/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TimeUUID.java b/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TimeUUID.java index 24758c2f1..71d587902 100644 --- a/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TimeUUID.java +++ b/cassandra-five-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TimeUUID.java @@ -20,6 +20,7 @@ package org.apache.cassandra.spark.data.types; import java.nio.ByteBuffer; +import java.util.Random; import java.util.function.Function; import org.apache.cassandra.db.marshal.ValueAccessor; @@ -31,8 +32,10 @@ public class TimeUUID extends AbstractTimeUUID public static final TimeUUID INSTANCE = new TimeUUID(); @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { + // Cassandra's own monotonic time-based generator is not pluggable with an external Random source, + // so this remains unseeded; see the "explicitly out of scope" note for QT reproducibility. return org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUID().asUUID(); } diff --git a/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java b/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java index 6f5850327..0e3df0e59 100644 --- a/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java +++ b/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/TestUtils.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Random; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; @@ -39,7 +40,7 @@ private TestUtils() public static byte[] randomLowEntropyData() { - return randomLowEntropyData(RandomUtils.randomPositiveInt(16384 - 512) + 512); + return randomLowEntropyData(RandomUtils.randomPositiveInt(new Random(), 16384 - 512) + 512); } public static byte[] randomLowEntropyData(int size) diff --git a/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java b/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java index 6a0eebf0d..434a30e3a 100644 --- a/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java +++ b/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/PartitionKeyTests.java @@ -29,13 +29,13 @@ import org.apache.cassandra.bridge.CassandraBridgeImplementation; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.CompositeType; -import org.apache.cassandra.spark.data.CqlField; import org.apache.cassandra.spark.data.CqlTable; import org.apache.cassandra.spark.data.CqlType; import org.apache.cassandra.spark.data.converter.SparkSqlTypeConverter; import org.apache.cassandra.spark.utils.ComparisonUtils; import org.apache.cassandra.spark.utils.test.TestSchema; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -49,16 +49,16 @@ public class PartitionKeyTests @SuppressWarnings("static-access") public void testBuildPartitionKey() { - qt().forAll(arbitrary().pick(BRIDGE.supportedTypes())) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) - .checkAssert(partitionKeyType -> { + qt().forAll(arbitrary().pick(BRIDGE.supportedTypes()), qtRandom()) + .assuming((partitionKeyType, random) -> partitionKeyType.supportedAsPrimaryKeyColumn()) + .checkAssert((partitionKeyType, random) -> { CqlTable table = TestSchema.builder(BRIDGE) .withPartitionKey("a", partitionKeyType) .withClusteringKey("b", BRIDGE.aInt()) .withColumn("c", BRIDGE.aInt()) .build() .buildTable(); - Object value = partitionKeyType.randomValue(100); + Object value = partitionKeyType.randomValue(100, random); String string = ((CqlType) partitionKeyType).serializer().toString(value); ByteBuffer buffer = BRIDGE.buildPartitionKey(table, Collections.singletonList(string)); Object cassandraValue = partitionKeyType.deserializeToJavaType(buffer); @@ -76,9 +76,9 @@ public void testBuildPartitionKey() @SuppressWarnings("static-access") public void testBuildCompositePartitionKey() { - qt().forAll(arbitrary().pick(BRIDGE.supportedTypes())) - .assuming(CqlField.CqlType::supportedAsPrimaryKeyColumn) - .checkAssert(partitionKeyType -> { + qt().forAll(arbitrary().pick(BRIDGE.supportedTypes()), qtRandom()) + .assuming((partitionKeyType, random) -> partitionKeyType.supportedAsPrimaryKeyColumn()) + .checkAssert((partitionKeyType, random) -> { CqlTable table = TestSchema.builder(BRIDGE) .withPartitionKey("a", BRIDGE.aInt()) .withPartitionKey("b", partitionKeyType) @@ -90,10 +90,10 @@ public void testBuildCompositePartitionKey() List> partitionKeyColumnTypes = BRIDGE.partitionKeyColumnTypes(table); CompositeType compositeType = CompositeType.getInstance(partitionKeyColumnTypes); - int columnA = (int) BRIDGE.aInt().randomValue(1024); - Object columnB = partitionKeyType.randomValue(1024); + int columnA = (int) BRIDGE.aInt().randomValue(1024, random); + Object columnB = partitionKeyType.randomValue(1024, random); String columnBString = ((CqlType) partitionKeyType).serializer().toString(columnB); - String columnC = (String) BRIDGE.text().randomValue(1024); + String columnC = (String) BRIDGE.text().randomValue(1024, random); ByteBuffer buffer = BRIDGE.buildPartitionKey(table, Arrays.asList(Integer.toString(columnA), columnBString, columnC)); ByteBuffer[] buffers = compositeType.split(buffer); diff --git a/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java b/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java index 1f8c7ef69..400159125 100644 --- a/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java +++ b/cassandra-four-zero-bridge/src/test/java/org/apache/cassandra/spark/reader/SSTableReaderTests.java @@ -83,6 +83,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static org.apache.cassandra.spark.CommonTestUtils.qtRandom; import static org.assertj.core.api.Assertions.assertThat; import static org.quicktheories.QuickTheory.qt; import static org.quicktheories.generators.SourceDSL.arbitrary; @@ -945,8 +946,8 @@ public void skippedRepairedSSTable(SSTable ssTable, long repairedAt) @Test public void testPartitionKeyFilter() { - qt().forAll(arbitrary().enumValues(Partitioner.class)) - .checkAssert(partitioner -> { + qt().forAll(arbitrary().enumValues(Partitioner.class), qtRandom()) + .checkAssert((partitioner, random) -> { try (TemporaryDirectory directory = new TemporaryDirectory()) { TestSchema schema = TestSchema.builder(BRIDGE) @@ -957,7 +958,7 @@ public void testPartitionKeyFilter() .build(); CqlTable cqlTable = schema.buildTable(); int numSSTables = 24; - String partitionKeyStr = (String) BRIDGE.text().randomValue(1024); + String partitionKeyStr = (String) BRIDGE.text().randomValue(1024, random); AbstractMap.SimpleEntry partitionKey = BRIDGE.getPartitionKey(cqlTable, partitioner, Collections.singletonList(partitionKeyStr)); PartitionKeyFilter partitionKeyFilter = PartitionKeyFilter.create(partitionKey.getKey(), @@ -977,8 +978,8 @@ public void testPartitionKeyFilter() // Write partition key in last SSTable only for (int column = 0; column < COLUMNS; column++) { - expectedC[column] = (int) BRIDGE.aInt().randomValue(1024); - expectedD[column] = (String) BRIDGE.text().randomValue(1024); + expectedC[column] = (int) BRIDGE.aInt().randomValue(1024, random); + expectedD[column] = (String) BRIDGE.text().randomValue(1024, random); writer.write(partitionKeyStr, column, expectedC[column], expectedD[column]); } } @@ -990,12 +991,12 @@ public void testPartitionKeyFilter() String key = null; while (key == null || key.equals(partitionKeyStr)) { - key = (String) BRIDGE.text().randomValue(1024); + key = (String) BRIDGE.text().randomValue(1024, random); } writer.write(key, row, - BRIDGE.aInt().randomValue(1024), - BRIDGE.text().randomValue(1024)); + BRIDGE.aInt().randomValue(1024, random), + BRIDGE.text().randomValue(1024, random)); } } }); diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/AbstractCqlType.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/AbstractCqlType.java index 008d34cc8..09fdbbe9f 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/AbstractCqlType.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/AbstractCqlType.java @@ -20,6 +20,7 @@ package org.apache.cassandra.spark.data; import java.nio.ByteBuffer; +import java.util.Random; import java.util.concurrent.TimeUnit; import com.google.common.annotations.VisibleForTesting; @@ -72,7 +73,7 @@ public ByteBuffer serialize(Object value) } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { throw CqlField.notImplemented(this); } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlList.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlList.java index 43619e6f7..eedb8dd79 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlList.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlList.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.List; +import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -35,7 +36,6 @@ import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.spark.data.CqlField; import org.apache.cassandra.spark.data.CqlType; -import org.apache.cassandra.spark.utils.RandomUtils; import org.apache.cassandra.utils.UUIDGen; import static org.apache.cassandra.spark.data.CqlField.NO_TTL; @@ -79,10 +79,10 @@ protected void setInnerValueInternal(SettableByIndexData udtValue, int positi } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return IntStream.range(0, RandomUtils.RANDOM.nextInt(16) + minCollectionSize) - .mapToObj(element -> type().randomValue(minCollectionSize)) + return IntStream.range(0, random.nextInt(16) + minCollectionSize) + .mapToObj(element -> type().randomValue(minCollectionSize, random)) .collect(Collectors.toList()); } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlTuple.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlTuple.java index d6beb06ae..4b33f813d 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlTuple.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/AbstractCqlTuple.java @@ -22,6 +22,7 @@ import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.List; +import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -138,9 +139,9 @@ protected void setInnerValueInternal(SettableByIndexData udtValue, int positi } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return types().stream().map(type -> type.randomValue(minCollectionSize)).toArray(); + return types().stream().map(type -> type.randomValue(minCollectionSize, random)).toArray(); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlFrozen.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlFrozen.java index dd14ee32b..88bf17c9f 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlFrozen.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlFrozen.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.Objects; +import java.util.Random; import java.util.Set; import com.esotericsoftware.kryo.io.Output; @@ -117,9 +118,9 @@ protected void setInnerValueInternal(SettableByIndexData udtValue, int positi } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return inner.randomValue(minCollectionSize); + return inner.randomValue(minCollectionSize, random); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlMap.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlMap.java index e9003b09f..e737ddd72 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlMap.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlMap.java @@ -20,6 +20,7 @@ package org.apache.cassandra.spark.data.complex; import java.util.Map; +import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -32,7 +33,6 @@ import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.spark.data.CqlField; import org.apache.cassandra.spark.data.CqlType; -import org.apache.cassandra.spark.utils.RandomUtils; import org.apache.cassandra.utils.Pair; import org.apache.cassandra.db.rows.BufferCell; import org.apache.cassandra.db.rows.CellPath; @@ -84,11 +84,11 @@ public String name() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return IntStream.range(0, RandomUtils.RANDOM.nextInt(16) + minCollectionSize) - .mapToObj(entry -> Pair.create(keyType().randomValue(minCollectionSize), - valueType().randomValue(minCollectionSize))) + return IntStream.range(0, random.nextInt(16) + minCollectionSize) + .mapToObj(entry -> Pair.create(keyType().randomValue(minCollectionSize, random), + valueType().randomValue(minCollectionSize, random))) .collect(Collectors.toMap(Pair::left, Pair::right, (first, second) -> first)); } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlSet.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlSet.java index bbe504ae2..a9dc2e235 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlSet.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlSet.java @@ -21,6 +21,7 @@ import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.stream.Collectors; @@ -74,9 +75,9 @@ public String name() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return new HashSet<>(((List) super.randomValue(minCollectionSize))); + return new HashSet<>(((List) super.randomValue(minCollectionSize, random))); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlUdt.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlUdt.java index 31a8187d1..de03f8bdd 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlUdt.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/complex/CqlUdt.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -86,10 +87,10 @@ public Set udts() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { return fields().stream() - .collect(Collectors.toMap(CqlField::name, field -> Objects.requireNonNull(field.type().randomValue(minCollectionSize)))); + .collect(Collectors.toMap(CqlField::name, field -> Objects.requireNonNull(field.type().randomValue(minCollectionSize, random)))); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/AbstractTimeUUID.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/AbstractTimeUUID.java index 7beee7fcc..88fcc0d9a 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/AbstractTimeUUID.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/AbstractTimeUUID.java @@ -19,6 +19,8 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.DataType; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.TimeUUIDType; @@ -39,8 +41,10 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { + // Cassandra's own monotonic time-based generator is not pluggable with an external Random source, + // so this remains unseeded; see the "explicitly out of scope" note for QT reproducibility. return UUIDGen.getTimeUUID(); } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Blob.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Blob.java index 785861cd1..0bab4839f 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Blob.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Blob.java @@ -20,6 +20,7 @@ package org.apache.cassandra.spark.data.types; import java.nio.ByteBuffer; +import java.util.Random; import org.apache.cassandra.cql3.functions.types.DataType; import org.apache.cassandra.cql3.functions.types.SettableByIndexData; @@ -44,9 +45,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.randomByteBuffer(RandomUtils.randomPositiveInt(256)); + return RandomUtils.randomByteBuffer(random, RandomUtils.randomPositiveInt(random, 256)); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Boolean.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Boolean.java index 5d30fa04b..1eaaff7ce 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Boolean.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Boolean.java @@ -19,11 +19,12 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.BooleanType; import org.apache.cassandra.spark.data.NativeType; -import org.apache.cassandra.spark.utils.RandomUtils; public class Boolean extends NativeType { @@ -48,9 +49,9 @@ public int cardinality(int orElse) } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.RANDOM.nextBoolean(); + return random.nextBoolean(); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Date.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Date.java index 912d7e1cd..e9ff7ef51 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Date.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Date.java @@ -19,6 +19,8 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.bridge.CassandraVersion; import org.apache.cassandra.cql3.functions.types.LocalDate; import org.apache.cassandra.cql3.functions.types.SettableByIndexData; @@ -44,9 +46,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.randomPositiveInt(30_000); + return RandomUtils.randomPositiveInt(random, 30_000); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Decimal.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Decimal.java index b58090fdc..694d86fa8 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Decimal.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Decimal.java @@ -21,13 +21,13 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Random; import org.apache.cassandra.bridge.BigNumberConfig; import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.DecimalType; import org.apache.cassandra.spark.data.NativeType; -import org.apache.cassandra.spark.utils.RandomUtils; public class Decimal extends NativeType { @@ -46,9 +46,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - BigInteger unscaledVal = new BigInteger(BigNumberConfig.DEFAULT.bigDecimalPrecision(), RandomUtils.RANDOM); + BigInteger unscaledVal = new BigInteger(BigNumberConfig.DEFAULT.bigDecimalPrecision(), random); int scale = BigNumberConfig.DEFAULT.bigDecimalScale(); return new BigDecimal(unscaledVal, scale); } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Double.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Double.java index 22c1f4e6b..2c9590283 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Double.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Double.java @@ -19,11 +19,12 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.DoubleType; import org.apache.cassandra.spark.data.NativeType; -import org.apache.cassandra.spark.utils.RandomUtils; public class Double extends NativeType { @@ -42,9 +43,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.RANDOM.nextDouble(); + return random.nextDouble(); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Duration.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Duration.java index 264f8fa76..9fc9ce568 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Duration.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Duration.java @@ -20,6 +20,7 @@ package org.apache.cassandra.spark.data.types; import java.nio.ByteBuffer; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -110,11 +111,11 @@ else if (value instanceof org.apache.cassandra.cql3.Duration) } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return new InternalDuration(RandomUtils.randomPositiveInt(100), - RandomUtils.randomPositiveInt(100), - TimeUnit.MICROSECONDS.toNanos(RandomUtils.randomPositiveInt(1000000))); + return new InternalDuration(RandomUtils.randomPositiveInt(random, 100), + RandomUtils.randomPositiveInt(random, 100), + TimeUnit.MICROSECONDS.toNanos(RandomUtils.randomPositiveInt(random, 1000000))); } /** diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Empty.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Empty.java index ea2db8fa7..032d0dff9 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Empty.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Empty.java @@ -19,6 +19,8 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.EmptyType; @@ -53,7 +55,7 @@ public int cardinality(int orElse) } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { return null; } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Float.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Float.java index 8a7f635fd..09439347e 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Float.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Float.java @@ -19,11 +19,12 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.FloatType; import org.apache.cassandra.spark.data.NativeType; -import org.apache.cassandra.spark.utils.RandomUtils; public class Float extends NativeType { @@ -42,9 +43,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.RANDOM.nextFloat(); + return random.nextFloat(); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Inet.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Inet.java index c306cf04e..d06e6abee 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Inet.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Inet.java @@ -20,6 +20,7 @@ package org.apache.cassandra.spark.data.types; import java.net.InetAddress; +import java.util.Random; import com.google.common.net.InetAddresses; @@ -27,7 +28,6 @@ import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.InetAddressType; -import org.apache.cassandra.spark.utils.RandomUtils; public class Inet extends BinaryBased { @@ -47,9 +47,9 @@ public AbstractType dataType() @Override @SuppressWarnings("UnstableApiUsage") - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return InetAddresses.fromInteger(RandomUtils.RANDOM.nextInt()); + return InetAddresses.fromInteger(random.nextInt()); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Int.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Int.java index 41df2d134..e0733ca17 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Int.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Int.java @@ -19,11 +19,12 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.spark.data.NativeType; -import org.apache.cassandra.spark.utils.RandomUtils; public class Int extends NativeType { @@ -42,9 +43,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.RANDOM.nextInt(); + return random.nextInt(); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/LongBased.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/LongBased.java index 1a5db24a8..ea0a8a3af 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/LongBased.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/LongBased.java @@ -19,14 +19,16 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.spark.data.NativeType; import org.apache.cassandra.spark.utils.RandomUtils; public abstract class LongBased extends NativeType { @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return (long) RandomUtils.randomPositiveInt(5_000_000); // Keep within bound to avoid overflows + return (long) RandomUtils.randomPositiveInt(random, 5_000_000); // Keep within bound to avoid overflows } } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/SmallInt.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/SmallInt.java index 0cc01e77f..a5db0e5a0 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/SmallInt.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/SmallInt.java @@ -19,11 +19,12 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.ShortType; import org.apache.cassandra.spark.data.NativeType; -import org.apache.cassandra.spark.utils.RandomUtils; public class SmallInt extends NativeType { @@ -42,9 +43,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return (short) RandomUtils.RANDOM.nextInt(Short.MAX_VALUE + 1); + return (short) random.nextInt(Short.MAX_VALUE + 1); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/StringBased.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/StringBased.java index 3388f8239..f5fa13a84 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/StringBased.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/StringBased.java @@ -19,6 +19,8 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.spark.data.NativeType; import org.apache.cassandra.spark.utils.RandomUtils; @@ -26,9 +28,9 @@ public abstract class StringBased extends NativeType { @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.randomAlphanumeric(RandomUtils.randomPositiveInt(32)); + return RandomUtils.randomAlphanumeric(random, RandomUtils.randomPositiveInt(random, 32)); } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Timestamp.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Timestamp.java index 08754d22d..93a476eab 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Timestamp.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/Timestamp.java @@ -19,6 +19,8 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.TimestampType; @@ -47,7 +49,7 @@ protected void setInnerValueInternal(SettableByIndexData udtValue, int positi } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { return new java.util.Date(); } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TinyInt.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TinyInt.java index db67d99ec..b819ecca6 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TinyInt.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/TinyInt.java @@ -19,6 +19,8 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.ByteType; @@ -42,9 +44,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return RandomUtils.randomBytes(1)[0]; + return RandomUtils.randomBytes(random, 1)[0]; } @Override diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/UUID.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/UUID.java index 97cd4755b..c40ef8f1d 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/UUID.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/UUID.java @@ -19,10 +19,13 @@ package org.apache.cassandra.spark.data.types; +import java.util.Random; + import org.apache.cassandra.cql3.functions.types.DataType; import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.UUIDType; +import org.apache.cassandra.spark.utils.RandomUtils; public class UUID extends StringBased { @@ -53,8 +56,8 @@ protected void setInnerValueInternal(SettableByIndexData udtValue, int positi } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return java.util.UUID.randomUUID(); + return RandomUtils.randomUuid(random); } } diff --git a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/VarInt.java b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/VarInt.java index 43adcc027..76230a48e 100644 --- a/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/VarInt.java +++ b/cassandra-four-zero-types/src/main/java/org/apache/cassandra/spark/data/types/VarInt.java @@ -20,12 +20,12 @@ package org.apache.cassandra.spark.data.types; import java.math.BigInteger; +import java.util.Random; import org.apache.cassandra.bridge.BigNumberConfig; import org.apache.cassandra.cql3.functions.types.SettableByIndexData; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.IntegerType; -import org.apache.cassandra.spark.utils.RandomUtils; public class VarInt extends Decimal { @@ -44,9 +44,9 @@ public AbstractType dataType() } @Override - public Object randomValue(int minCollectionSize) + public Object randomValue(int minCollectionSize, Random random) { - return new BigInteger(BigNumberConfig.DEFAULT.bigIntegerPrecision(), RandomUtils.RANDOM); + return new BigInteger(BigNumberConfig.DEFAULT.bigIntegerPrecision(), random); } @Override