This is an automated email from the ASF dual-hosted git repository.
chia7712 pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push:
new 2699fc40748 KAFKA-20553 Remove clients-integration-tests dependency on
core test output (#22884)
2699fc40748 is described below
commit 2699fc40748da5ce9dc4b6aac41687fbc603ab1d
Author: majialong <[email protected]>
AuthorDate: Fri Jul 24 18:03:41 2026 +0800
KAFKA-20553 Remove clients-integration-tests dependency on core test output
(#22884)
This PR removes the `clients:clients-integration-tests` dependency on
the core test output by replacing usages of `kafka.utils.TestUtils` in
`ProducerCompressionTest` and `ProducerIdExpirationTest`.
Reviewers: Chia-Ping Tsai <[email protected]>
---
build.gradle | 1 -
.../org/apache/kafka/clients/ClientsTestUtils.java | 11 +-
.../clients/producer/ProducerCompressionTest.java | 24 ++++-
.../clients/producer/ProducerIdExpirationTest.java | 114 +++++++++++++++------
.../test/scala/unit/kafka/utils/TestUtils.scala | 4 -
5 files changed, 113 insertions(+), 41 deletions(-)
diff --git a/build.gradle b/build.gradle
index 69e82b5c55b..6a4f33106b5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2175,7 +2175,6 @@ project(':clients:clients-integration-tests') {
testImplementation project(':server')
testImplementation project(':storage')
testImplementation testFixtures(project(':storage'))
- testImplementation project(':core').sourceSets.test.output
testImplementation testFixtures(project(':clients'))
implementation project(':server-common')
testImplementation testFixtures(project(':server-common'))
diff --git
a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/ClientsTestUtils.java
b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/ClientsTestUtils.java
index 5635f6e36b7..bef08022d19 100644
---
a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/ClientsTestUtils.java
+++
b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/ClientsTestUtils.java
@@ -75,6 +75,15 @@ public class ClientsTestUtils {
Consumer<K, V> consumer,
int numRecords,
int maxPollRecords
+ ) throws InterruptedException {
+ return consumeRecords(consumer, numRecords, maxPollRecords, 60000);
+ }
+
+ public static <K, V> List<ConsumerRecord<K, V>> consumeRecords(
+ Consumer<K, V> consumer,
+ int numRecords,
+ int maxPollRecords,
+ long maxWaitMs
) throws InterruptedException {
List<ConsumerRecord<K, V>> consumedRecords = new ArrayList<>();
TestUtils.waitForCondition(() -> {
@@ -82,7 +91,7 @@ public class ClientsTestUtils {
records.forEach(consumedRecords::add);
assertTrue(records.count() <= maxPollRecords);
return consumedRecords.size() >= numRecords;
- }, 60000, "Timed out before consuming expected " + numRecords + "
records.");
+ }, maxWaitMs, "Timed out before consuming expected " + numRecords + "
records.");
return consumedRecords;
}
diff --git
a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerCompressionTest.java
b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerCompressionTest.java
index d8d3ebe3d86..9a14d2b56cc 100644
---
a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerCompressionTest.java
+++
b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerCompressionTest.java
@@ -20,12 +20,14 @@ package org.apache.kafka.clients.producer;
import org.apache.kafka.clients.consumer.CloseOptions;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.header.internals.RecordHeader;
import org.apache.kafka.common.header.internals.RecordHeaders;
import org.apache.kafka.common.record.internal.CompressionType;
import org.apache.kafka.common.test.ClusterInstance;
+import org.apache.kafka.common.test.api.ClusterConfigProperty;
import org.apache.kafka.common.test.api.ClusterTest;
import org.apache.kafka.common.test.api.ClusterTestDefaults;
import org.apache.kafka.common.test.api.Type;
@@ -43,13 +45,19 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import static kafka.utils.TestUtils.consumeRecords;
+import static org.apache.kafka.clients.ClientsTestUtils.consumeRecords;
+import static
org.apache.kafka.coordinator.group.GroupCoordinatorConfig.OFFSETS_TOPIC_REPLICATION_FACTOR_CONFIG;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;
-@ClusterTestDefaults(types = {Type.KRAFT})
+@ClusterTestDefaults(
+ types = {Type.KRAFT},
+ serverProperties = {
+ @ClusterConfigProperty(key = OFFSETS_TOPIC_REPLICATION_FACTOR_CONFIG,
value = "1")
+ }
+)
public class ProducerCompressionTest {
private final String topicName = "topic";
@@ -114,13 +122,20 @@ public class ProducerCompressionTest {
}
private void verifyConsumerRecords(Consumer<byte[], byte[]> consumer,
List<String> messages, long now,
- Header[] headerArr, int partition,
String topic, String compression) {
+ Header[] headerArr, int partition,
String topic, String compression) throws InterruptedException {
TopicPartition tp = new TopicPartition(topic, partition);
consumer.assign(List.of(tp));
consumer.seek(tp, 0);
AtomicInteger num = new AtomicInteger(0);
AtomicInteger flag = new AtomicInteger(0);
- consumeRecords(consumer, numRecords * 3,
TestUtils.DEFAULT_MAX_WAIT_MS).foreach(record -> {
+ List<ConsumerRecord<byte[], byte[]>> records = consumeRecords(
+ consumer,
+ numRecords * 3,
+ Integer.MAX_VALUE,
+ TestUtils.DEFAULT_MAX_WAIT_MS
+ );
+ assertEquals(numRecords * 3, records.size(), "Consumed more records
than expected");
+ records.forEach(record -> {
String messageValue = messages.get(num.get());
long offset = num.get() * 3L + flag.get();
if (flag.get() == 0) {
@@ -149,7 +164,6 @@ public class ProducerCompressionTest {
fail();
}
flagLoop(num, flag);
- return null;
});
}
diff --git
a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerIdExpirationTest.java
b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerIdExpirationTest.java
index 94bc50b4955..cdaa44cda4f 100644
---
a/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerIdExpirationTest.java
+++
b/clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerIdExpirationTest.java
@@ -17,7 +17,6 @@
package org.apache.kafka.clients.producer;
import kafka.server.KafkaBroker;
-import kafka.utils.TestUtils;
import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.clients.admin.AlterConfigOp;
@@ -25,18 +24,22 @@ import org.apache.kafka.clients.admin.ConfigEntry;
import org.apache.kafka.clients.admin.ProducerState;
import org.apache.kafka.clients.consumer.CloseOptions;
import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.KafkaException;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.config.ConfigResource;
import org.apache.kafka.common.errors.InvalidPidMappingException;
import org.apache.kafka.common.errors.TransactionalIdNotFoundException;
+import org.apache.kafka.common.header.internals.RecordHeader;
import org.apache.kafka.common.test.ClusterInstance;
import org.apache.kafka.common.test.api.ClusterConfigProperty;
import org.apache.kafka.common.test.api.ClusterTest;
import org.apache.kafka.common.test.api.ClusterTestDefaults;
+import org.apache.kafka.test.NoRetryException;
import org.opentest4j.AssertionFailedError;
+import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
@@ -46,7 +49,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import static kafka.utils.TestUtils.consumeRecords;
+import static org.apache.kafka.clients.ClientsTestUtils.consumeRecords;
import static
org.apache.kafka.clients.consumer.ConsumerConfig.ISOLATION_LEVEL_CONFIG;
import static
org.apache.kafka.clients.producer.ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG;
import static
org.apache.kafka.coordinator.group.GroupCoordinatorConfig.GROUP_INITIAL_REBALANCE_DELAY_MS_CONFIG;
@@ -64,9 +67,12 @@ import static
org.apache.kafka.server.config.ServerConfigs.CONTROLLED_SHUTDOWN_E
import static
org.apache.kafka.server.config.ServerLogConfigs.AUTO_CREATE_TOPICS_ENABLE_CONFIG;
import static org.apache.kafka.test.TestUtils.DEFAULT_MAX_WAIT_MS;
import static org.apache.kafka.test.TestUtils.assertFutureThrows;
+import static org.apache.kafka.test.TestUtils.waitForCondition;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
@ClusterTestDefaults(
brokers = 3,
@@ -94,6 +100,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
}
)
public class ProducerIdExpirationTest {
+ private static final String HEADER_KEY = "transactionStatus";
+ private static final byte[] COMMITTED_VALUE =
"committed".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] ABORTED_VALUE =
"aborted".getBytes(StandardCharsets.UTF_8);
+
private final String topic1 = "topic1";
private final int numPartitions = 1;
private final short replicationFactor = 3;
@@ -130,19 +140,22 @@ public class ProducerIdExpirationTest {
// Start and then abort a transaction to allow the producer ID to
expire.
producer.beginTransaction();
-
producer.send(TestUtils.producerRecordWithExpectedTransactionStatus(topic1, 0,
"2", "2", false));
+ producer.send(producerRecordWithExpectedTransactionStatus(topic1, 0,
"2", "2", false));
producer.flush();
Consumer<byte[], byte[]> consumer =
cluster.consumer(Map.of(ISOLATION_LEVEL_CONFIG, "read_committed"));
try (Admin admin = cluster.admin()) {
// Ensure producer IDs are added.
- TestUtils.waitUntilTrue(() -> {
+ waitForCondition(() -> {
try {
return producerStates(admin).size() == 1;
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new NoRetryException(e);
+ } catch (ExecutionException e) {
+ throw new NoRetryException(e);
}
- }, () -> "Producer IDs were not added.", DEFAULT_MAX_WAIT_MS, 100);
+ }, DEFAULT_MAX_WAIT_MS, 100L, () -> "Producer IDs were not
added.");
producer.abortTransaction();
@@ -156,9 +169,8 @@ public class ProducerIdExpirationTest {
// due to the expired transactional ID, resulting in a fatal error.
producer.beginTransaction();
Future<RecordMetadata> failedFuture =
-
producer.send(TestUtils.producerRecordWithExpectedTransactionStatus(topic1, 0,
"1", "1", false));
- TestUtils.waitUntilTrue(failedFuture::isDone, () -> "Producer
future never completed.",
- DEFAULT_MAX_WAIT_MS, 100);
+
producer.send(producerRecordWithExpectedTransactionStatus(topic1, 0, "1", "1",
false));
+ waitForCondition(failedFuture::isDone, DEFAULT_MAX_WAIT_MS, 100L,
() -> "Producer future never completed.");
assertFutureThrows(InvalidPidMappingException.class, failedFuture);
// Assert that aborting the transaction throws a KafkaException
due to the fatal error.
@@ -170,8 +182,8 @@ public class ProducerIdExpirationTest {
producer.initTransactions();
producer.beginTransaction();
-
producer.send(TestUtils.producerRecordWithExpectedTransactionStatus(topic1, 0,
"4", "4", true));
-
producer.send(TestUtils.producerRecordWithExpectedTransactionStatus(topic1, 0,
"3", "3", true));
+ producer.send(producerRecordWithExpectedTransactionStatus(topic1,
0, "4", "4", true));
+ producer.send(producerRecordWithExpectedTransactionStatus(topic1,
0, "3", "3", true));
// Producer IDs should be retained.
assertFalse(producerStates(admin).isEmpty());
@@ -180,7 +192,16 @@ public class ProducerIdExpirationTest {
// Check we can still consume the transaction.
consumer.subscribe(List.of(topic1));
- consumeRecords(consumer, 2,
DEFAULT_MAX_WAIT_MS).foreach(TestUtils::assertCommittedAndGetValue);
+ List<ConsumerRecord<byte[], byte[]>> records = consumeRecords(
+ consumer,
+ 2,
+ Integer.MAX_VALUE,
+ DEFAULT_MAX_WAIT_MS
+ );
+ assertEquals(
+ List.of("4", "3"),
+
records.stream().map(ProducerIdExpirationTest::assertCommittedAndGetValue).toList()
+ );
} finally {
producer.close();
consumer.close(CloseOptions.timeout(Duration.ofSeconds(1)));
@@ -204,10 +225,14 @@ public class ProducerIdExpirationTest {
// Update the producer ID expiration ms to a very high value.
admin.incrementalAlterConfigs(producerIdExpirationConfig("100000"));
- cluster.brokers().values().forEach(broker ->
- TestUtils.waitUntilTrue(() ->
broker.logManager().producerStateManagerConfig().producerIdExpirationMs() ==
100000,
- () -> "Configuration was not updated.",
DEFAULT_MAX_WAIT_MS, 100)
- );
+ for (KafkaBroker broker : cluster.brokers().values()) {
+ waitForCondition(
+ () ->
broker.logManager().producerStateManagerConfig().producerIdExpirationMs() ==
100000,
+ DEFAULT_MAX_WAIT_MS,
+ 100L,
+ () -> "Configuration was not updated."
+ );
+ }
// Send more records to send producer ID back to brokers.
producer.send(new ProducerRecord<>(topic1, 0, null,
"key".getBytes(), "value".getBytes()));
producer.flush();
@@ -226,10 +251,14 @@ public class ProducerIdExpirationTest {
kafkaBroker.awaitShutdown();
kafkaBroker.startup();
cluster.waitForReadyBrokers();
- cluster.brokers().values().forEach(broker ->
- TestUtils.waitUntilTrue(() ->
broker.logManager().producerStateManagerConfig().producerIdExpirationMs() ==
100,
- () -> "Configuration was not updated.",
DEFAULT_MAX_WAIT_MS, 100)
- );
+ for (KafkaBroker broker : cluster.brokers().values()) {
+ waitForCondition(
+ () ->
broker.logManager().producerStateManagerConfig().producerIdExpirationMs() ==
100,
+ DEFAULT_MAX_WAIT_MS,
+ 100L,
+ () -> "Configuration was not updated."
+ );
+ }
// Ensure producer ID expires quickly again.
waitProducerIdExpire(admin);
@@ -237,18 +266,21 @@ public class ProducerIdExpirationTest {
}
- private void waitProducerIdExpire(Admin admin) {
+ private void waitProducerIdExpire(Admin admin) throws InterruptedException
{
waitProducerIdExpire(admin, DEFAULT_MAX_WAIT_MS);
}
- private void waitProducerIdExpire(Admin admin, long timeout) {
- TestUtils.waitUntilTrue(() -> {
+ private void waitProducerIdExpire(Admin admin, long timeout) throws
InterruptedException {
+ waitForCondition(() -> {
try {
return producerStates(admin).isEmpty();
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new NoRetryException(e);
+ } catch (ExecutionException e) {
+ throw new NoRetryException(e);
}
- }, () -> "Producer ID expired.", timeout, 100);
+ }, timeout, 100L, () -> "Producer ID expired.");
}
private Map<ConfigResource, Collection<AlterConfigOp>>
producerIdExpirationConfig(String configValue) {
@@ -257,8 +289,8 @@ public class ProducerIdExpirationTest {
}
- private void waitUntilTransactionalStateExpires(Admin admin) {
- TestUtils.waitUntilTrue(() -> {
+ private void waitUntilTransactionalStateExpires(Admin admin) throws
InterruptedException {
+ waitForCondition(() -> {
boolean removedTransactionState = false;
try {
admin.describeTransactions(List.of(transactionalId))
@@ -268,7 +300,29 @@ public class ProducerIdExpirationTest {
removedTransactionState = e.getCause() instanceof
TransactionalIdNotFoundException;
}
return removedTransactionState;
- }, () -> "Transaction state never expired.", DEFAULT_MAX_WAIT_MS, 100);
+ }, DEFAULT_MAX_WAIT_MS, 100L, () -> "Transaction state never
expired.");
+ }
+
+ private static ProducerRecord<byte[], byte[]>
producerRecordWithExpectedTransactionStatus(
+ String topic,
+ Integer partition,
+ String key,
+ String value,
+ boolean willBeCommitted
+ ) {
+ var header = new RecordHeader(HEADER_KEY, willBeCommitted ?
COMMITTED_VALUE : ABORTED_VALUE);
+ return new ProducerRecord<>(topic, partition,
+ key.getBytes(StandardCharsets.UTF_8),
+ value.getBytes(StandardCharsets.UTF_8),
+ Collections.singleton(header));
+ }
+
+ private static String assertCommittedAndGetValue(ConsumerRecord<byte[],
byte[]> record) {
+ var headers = record.headers().headers(HEADER_KEY).iterator();
+ assertTrue(headers.hasNext(), "Expected transaction status header");
+ var header = headers.next();
+ assertArrayEquals(COMMITTED_VALUE, header.value(), "Expected committed
status");
+ return new String(record.value(), StandardCharsets.UTF_8);
}
private Map<String, Object> transactionalProducerConfig() {
diff --git a/core/src/test/scala/unit/kafka/utils/TestUtils.scala
b/core/src/test/scala/unit/kafka/utils/TestUtils.scala
index bc41c2ed2ae..c4e35f74705 100755
--- a/core/src/test/scala/unit/kafka/utils/TestUtils.scala
+++ b/core/src/test/scala/unit/kafka/utils/TestUtils.scala
@@ -1116,10 +1116,6 @@ object TestUtils extends Logging {
new ProducerRecord[Array[Byte], Array[Byte]](topic, partition, key, value,
util.Set.of(header))
}
- def producerRecordWithExpectedTransactionStatus(topic: String, partition:
Integer, key: String, value: String, willBeCommitted: Boolean):
ProducerRecord[Array[Byte], Array[Byte]] = {
- producerRecordWithExpectedTransactionStatus(topic, partition,
asBytes(key), asBytes(value), willBeCommitted)
- }
-
// Collect the current positions for all partition in the consumers current
assignment.
def consumerPositions(consumer: Consumer[Array[Byte], Array[Byte]]) :
Map[TopicPartition, OffsetAndMetadata] = {
val offsetsToCommit = new mutable.HashMap[TopicPartition,
OffsetAndMetadata]()