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 d38a90df2b6 KAFKA-17672 Run quarantined tests separately (#17329)
d38a90df2b6 is described below

commit d38a90df2b68daf7b96f1300e2fca308dc5498cc
Author: David Arthur <[email protected]>
AuthorDate: Sun Oct 6 02:09:24 2024 -0400

    KAFKA-17672 Run quarantined tests separately (#17329)
    
    Introduce new quarantinedTest that excludes tests tagged with "flaky". Also 
introduce two new build parameters "maxQuarantineTestRetries" and 
"maxQuarantineTestRetryFailures".
    
    Reviewers: Chia-Ping Tsai <[email protected]>
---
 .github/workflows/build.yml                        |  9 ++--
 .github/workflows/deflake.yml                      |  4 +-
 README.md                                          | 24 ++++++---
 build.gradle                                       | 63 +++++++++++++++++++++-
 .../integration/OffsetsApiIntegrationTest.java     |  2 +
 .../transaction/ProducerIdManagerTest.scala        |  2 +
 .../org/apache/kafka/common/test/api/Flaky.java    | 40 ++++++++++++++
 7 files changed, 130 insertions(+), 14 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2dc3b5c6793..3b0feba6158 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -130,18 +130,19 @@ jobs:
           timeout ${TIMEOUT_MINUTES}m ./gradlew --build-cache --continue 
--no-scan \
           -PtestLoggingEvents=started,passed,skipped,failed \
           -PmaxParallelForks=2 \
-          -PmaxTestRetries=1 -PmaxTestRetryFailures=10 \
+          -PmaxTestRetries=1 -PmaxTestRetryFailures=3 \
+          -PmaxQuarantineTestRetries=3 -PmaxQuarantineTestRetryFailures=0 \
           -PcommitId=xxxxxxxxxxxxxxxx \
-          test
+          quarantinedTest test
           exitcode="$?"
           echo "exitcode=$exitcode" >> $GITHUB_OUTPUT
-      - name: Archive JUnit reports
+      - name: Archive JUnit HTML reports
         uses: actions/upload-artifact@v4
         id: junit-upload-artifact
         with:
           name: junit-reports-${{ matrix.java }}
           path: |
-            **/build/reports/tests/test/*
+            **/build/reports/tests/*
           compression-level: 9
           if-no-files-found: ignore
       - name: Archive Thread Dumps
diff --git a/.github/workflows/deflake.yml b/.github/workflows/deflake.yml
index d41cacdf96c..85e4ce959f0 100644
--- a/.github/workflows/deflake.yml
+++ b/.github/workflows/deflake.yml
@@ -60,7 +60,9 @@ jobs:
           -PtestLoggingEvents=started,passed,skipped,failed \
           -PignoreFailures=true -PmaxParallelForks=2 \
           -Pkafka.cluster.test.repeat=${{ inputs.test-repeat }} \
-          ${{ inputs.test-module }}:test --tests ${{ inputs.test-pattern }}
+          -PmaxTestRetries=${{ inputs.test-repeat }} -PmaxTestRetryFailures=0 \
+          -PmaxQuarantineTestRetries=${{ inputs.test-repeat }} 
-PmaxQuarantineTestRetryFailures=0 \
+          ${{ inputs.test-module }}:test ${{ inputs.test-module 
}}:quarantinedTest --tests ${{ inputs.test-pattern }}
           exitcode="$?"
           echo "exitcode=$exitcode" >> $GITHUB_OUTPUT
       - name: Archive JUnit reports
diff --git a/README.md b/README.md
index 7bacfee75d8..6e90cac5c52 100644
--- a/README.md
+++ b/README.md
@@ -34,14 +34,16 @@ Follow instructions in https://kafka.apache.org/quickstart
     ./gradlew docsJar # builds both (if applicable) javadoc and scaladoc jars 
for each module
 
 ### Run unit/integration tests ###
-    ./gradlew test # runs both unit and integration tests
+    ./gradlew test  # runs both unit and integration tests
     ./gradlew unitTest
     ./gradlew integrationTest
+    ./gradlew quarantinedTest  # runs the quarantined tests
+
     
 ### Force re-running tests without code change ###
-    ./gradlew test --rerun
-    ./gradlew unitTest --rerun
-    ./gradlew integrationTest --rerun
+    ./gradlew test --rerun-tasks
+    ./gradlew unitTest --rerun-tasks
+    ./gradlew integrationTest --rerun-tasks
 
 ### Running a particular unit/integration test ###
     ./gradlew clients:test --tests RequestResponseTest
@@ -64,11 +66,17 @@ to `log4j.logger.org.apache.kafka=INFO` and then run:
 And you should see `INFO` level logs in the file under the 
`clients/build/test-results/test` directory.
 
 ### Specifying test retries ###
-By default, each failed test is retried once up to a maximum of five retries 
per test run. Tests are retried at the end of the test task. Adjust these 
parameters in the following way:
+By default, each failed test is retried once up to a maximum of three total 
retries per test run. 
+Tests are retried at the end of the test task. Adjust these parameters in the 
following way:
 
-    ./gradlew test -PmaxTestRetries=1 -PmaxTestRetryFailures=5
-    
-See [Test Retry Gradle 
Plugin](https://github.com/gradle/test-retry-gradle-plugin) for more details.
+    ./gradlew test -PmaxTestRetries=1 -PmaxTestRetryFailures=3
+
+Additionally, quarantined tests are automatically retried three times up to a 
total of
+20 retries per run. This is controlled by similar parameters.
+
+    ./gradlew test -PmaxQuarantineTestRetries=3 
-PmaxQuarantineTestRetryFailures=20
+
+See [Test Retry Gradle 
Plugin](https://github.com/gradle/test-retry-gradle-plugin) for and 
[build.yml](.github/workflows/build.yml) more details.
 
 ### Generating test coverage reports ###
 Generate coverage reports for the whole project:
diff --git a/build.gradle b/build.gradle
index ce47cc538d6..ec55a370a7b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -85,6 +85,9 @@ ext {
   userMaxTestRetries = project.hasProperty('maxTestRetries') ? 
maxTestRetries.toInteger() : 0
   userMaxTestRetryFailures = project.hasProperty('maxTestRetryFailures') ? 
maxTestRetryFailures.toInteger() : 0
 
+  userMaxQuarantineTestRetries = 
project.hasProperty('maxQuarantineTestRetries') ? 
maxQuarantineTestRetries.toInteger() : 0
+  userMaxQuarantineTestRetryFailures = 
project.hasProperty('maxQuarantineTestRetryFailures') ? 
maxQuarantineTestRetryFailures.toInteger() : 0
+
   skipSigning = project.hasProperty('skipSigning') && skipSigning.toBoolean()
   shouldSign = !skipSigning && !version.endsWith("SNAPSHOT")
 
@@ -501,6 +504,7 @@ subprojects {
 
     useJUnitPlatform {
       includeEngines 'junit-jupiter'
+      excludeTags 'flaky'
     }
 
     develocity {
@@ -524,7 +528,7 @@ subprojects {
       if (ext.isGithubActions) {
         def dest = 
rootProject.layout.buildDirectory.dir("junit-xml/${project.name}").get().asFile
         println "Copy JUnit XML for ${project.name} to $dest"
-        ant.copy(todir: "$dest") {
+        ant.copy(todir: "$dest/test") {
           ant.fileset(dir: "${test.reports.junitXml.entryPoint}")
         }
 
@@ -537,6 +541,62 @@ subprojects {
     }
   }
 
+  task quarantinedTest(type: Test, dependsOn: compileJava) {
+    ext {
+      isGithubActions = System.getenv('GITHUB_ACTIONS') != null
+    }
+
+    // Disable caching and up-to-date for this task. We always want 
quarantined tests
+    // to run and never want to cache their results. Since we do this, we can 
avoid
+    // explicitly failing the build like we do in "test" with ext.hadFailure.
+    outputs.upToDateWhen { false }
+    outputs.cacheIf { false }
+
+    maxParallelForks = maxTestForks
+    ignoreFailures = userIgnoreFailures
+
+    maxHeapSize = defaultMaxHeapSize
+    jvmArgs = defaultJvmArgs
+
+    // KAFKA-17433 Used by deflake.yml github action to repeat individual tests
+    systemProperty("kafka.cluster.test.repeat", 
project.findProperty("kafka.cluster.test.repeat"))
+
+    testLogging {
+      events = userTestLoggingEvents ?: testLoggingEvents
+      showStandardStreams = userShowStandardStreams ?: testShowStandardStreams
+      exceptionFormat = testExceptionFormat
+      displayGranularity = 0
+    }
+    logTestStdout.rehydrate(delegate, owner, this)()
+
+    useJUnitPlatform {
+      includeEngines 'junit-jupiter'
+      includeTags 'flaky'
+    }
+
+    develocity {
+      testRetry {
+        maxRetries = userMaxQuarantineTestRetries
+        maxFailures = userMaxQuarantineTestRetryFailures
+      }
+    }
+
+    // This closure will copy JUnit XML files out of the sub-project's build 
directory and into
+    // a top-level build/junit-xml directory. This is necessary to avoid 
reporting on tests which
+    // were not run, but instead were restored via FROM-CACHE. See KAFKA-17479 
for more details.
+    doLast {
+      if (ext.isGithubActions) {
+        def dest = 
rootProject.layout.buildDirectory.dir("junit-xml/${project.name}").get().asFile
+        println "Copy JUnit XML for ${project.name} to $dest"
+        ant.copy(todir: "$dest/quarantinedTest", failonerror: "false") {
+          ant.fileset(dir: "${quarantinedTest.reports.junitXml.entryPoint}") {
+            ant.include(name: "**/*.xml")
+          }
+        }
+      }
+    }
+  }
+
   task integrationTest(type: Test, dependsOn: compileJava) {
     maxParallelForks = maxTestForks
     ignoreFailures = userIgnoreFailures
@@ -3507,6 +3567,7 @@ project(':connect:runtime') {
     testImplementation project(':storage')
     testImplementation project(':connect:test-plugins')
     testImplementation project(':server-common').sourceSets.test.output
+    testImplementation project(':test-common:test-common-api')
 
     testImplementation libs.junitJupiter
     testImplementation libs.mockitoCore
diff --git 
a/connect/runtime/src/test/java/org/apache/kafka/connect/integration/OffsetsApiIntegrationTest.java
 
b/connect/runtime/src/test/java/org/apache/kafka/connect/integration/OffsetsApiIntegrationTest.java
index 0ac514039f0..0f013129c75 100644
--- 
a/connect/runtime/src/test/java/org/apache/kafka/connect/integration/OffsetsApiIntegrationTest.java
+++ 
b/connect/runtime/src/test/java/org/apache/kafka/connect/integration/OffsetsApiIntegrationTest.java
@@ -19,6 +19,7 @@ package org.apache.kafka.connect.integration;
 import org.apache.kafka.clients.CommonClientConfigs;
 import org.apache.kafka.clients.admin.Admin;
 import org.apache.kafka.clients.admin.ConsumerGroupListing;
+import org.apache.kafka.common.test.api.Flaky;
 import org.apache.kafka.connect.runtime.ConnectorConfig;
 import org.apache.kafka.connect.runtime.SourceConnectorConfig;
 import org.apache.kafka.connect.runtime.distributed.DistributedConfig;
@@ -331,6 +332,7 @@ public class OffsetsApiIntegrationTest {
         alterAndVerifySinkConnectorOffsets(baseSinkConnectorConfigs(), 
connect.kafka());
     }
 
+    @Flaky("KAFKA-15914")
     @Test
     public void testAlterSinkConnectorOffsetsOverriddenConsumerGroupId() 
throws Exception {
         Map<String, String> connectorConfigs = baseSinkConnectorConfigs();
diff --git 
a/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
 
b/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
index eef0b31e415..f74d0d55c90 100644
--- 
a/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
+++ 
b/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
@@ -24,6 +24,7 @@ import 
org.apache.kafka.common.errors.CoordinatorLoadInProgressException
 import org.apache.kafka.common.message.AllocateProducerIdsResponseData
 import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.requests.AllocateProducerIdsResponse
+import org.apache.kafka.common.test.api.Flaky
 import org.apache.kafka.common.utils.{MockTime, Time}
 import org.apache.kafka.server.NodeToControllerChannelManager
 import org.apache.kafka.server.common.ProducerIdsBlock
@@ -40,6 +41,7 @@ import java.util.concurrent.{ConcurrentLinkedQueue, 
CountDownLatch, Executors, T
 import scala.collection.mutable
 import scala.util.{Failure, Success, Try}
 
+@Flaky("KAFKA-17654")
 class ProducerIdManagerTest {
 
   var brokerToController: NodeToControllerChannelManager = 
mock(classOf[NodeToControllerChannelManager])
diff --git 
a/test-common/test-common-api/src/main/java/org/apache/kafka/common/test/api/Flaky.java
 
b/test-common/test-common-api/src/main/java/org/apache/kafka/common/test/api/Flaky.java
new file mode 100644
index 00000000000..c277ad5b8dc
--- /dev/null
+++ 
b/test-common/test-common-api/src/main/java/org/apache/kafka/common/test/api/Flaky.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.kafka.common.test.api;
+
+import org.junit.jupiter.api.Tag;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Tag("flaky")
+public @interface Flaky {
+    /**
+     * Required reference to a KAFKA Jira ticket.
+     */
+    String value();
+
+    /**
+     * Optional comment describing the reason for quarantined.
+     */
+    String comment() default "";
+}

Reply via email to