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

Jackie-Jiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 06c8f7e2ac2 Upgrade Apache parent to 40 and migrate to native Surefire 
3.6 (#19633)
06c8f7e2ac2 is described below

commit 06c8f7e2ac2da850591434d9cc6de0548e2ba8e4
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Wed Sep 23 11:33:52 2026 -0700

    Upgrade Apache parent to 40 and migrate to native Surefire 3.6 (#19633)
---
 .mvn/jvm.config                                    |   3 +-
 pinot-common/pom.xml                               |  26 ++-
 .../pinot/common/utils/NativeTestNGSuiteTest.java  | 230 +++++++++++++++++++++
 .../pinot/common/utils/OrderedTestNGSuite.java     |  49 +++++
 pinot-controller/pom.xml                           |  85 ++++----
 .../pinot/controller/ControllerTestSetup.java      |   2 +-
 .../controller/suites/ControllerStatefulSuite.java |  36 ++++
 .../suites/ControllerStatelessSuite.java           |  36 ++++
 pinot-controller/testng-statefull.xml              |  39 ----
 pinot-controller/testng-stateless.xml              |  41 ----
 ...centileSmartTDigestAggregationFunctionTest.java |  49 -----
 ...DigestHighThresholdAggregationFunctionTest.java |  31 +++
 ...igestSmallThresholdAggregationFunctionTest.java |  66 ++++++
 pinot-integration-tests/pom.xml                    |  65 +++---
 .../tests/suites/CustomClusterSuite.java           |  34 +++
 .../integration/tests/suites/KinesisSuite.java     |  41 ++++
 .../tests/suites/MultiNodesOfflineSuite.java       |  38 ++++
 .../tests/suites/SharedHybridSuite.java            |  43 ++++
 .../tests/suites/SharedKafkaRealtimeSuite.java     |  45 ++++
 .../custom-cluster-integration-test-suite.xml      |  28 ---
 .../resources/kinesis-integration-test-suite.xml   |  29 ---
 .../multi-nodes-offline-integration-test-suite.xml |  36 ----
 .../shared-hybrid-integration-test-suite.xml       |  31 ---
 ...hared-kafka-realtime-integration-test-suite.xml |  33 ---
 pom.xml                                            |  41 ++--
 25 files changed, 778 insertions(+), 379 deletions(-)

diff --git a/.mvn/jvm.config b/.mvn/jvm.config
index d71cbc89466..981650a9b82 100644
--- a/.mvn/jvm.config
+++ b/.mvn/jvm.config
@@ -1,3 +1,4 @@
+--add-opens=java.base/java.net=ALL-UNNAMED
 --add-opens=java.base/java.nio=ALL-UNNAMED
 --add-opens=java.base/sun.nio.ch=ALL-UNNAMED
 --add-opens=java.base/java.lang=ALL-UNNAMED
@@ -5,4 +6,4 @@
 --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
 --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED
 -Dio.netty.tryReflectionSetAccessible=true
--Dio.grpc.netty.shaded.io.netty.tryReflectionSetAccessible=true
\ No newline at end of file
+-Dio.grpc.netty.shaded.io.netty.tryReflectionSetAccessible=true
diff --git a/pinot-common/pom.xml b/pinot-common/pom.xml
index 539625d7f2e..bcd233c5698 100644
--- a/pinot-common/pom.xml
+++ b/pinot-common/pom.xml
@@ -62,12 +62,6 @@
           <!-- Keep this module's historical fork reuse, but honor the root 
parallel-fork knob. -->
           <forkCount>${unit.test.fork.count}</forkCount>
           <reuseForks>true</reuseForks>
-          <properties>
-            <property>
-              <name>usedefaultlisteners</name>
-              <value>false</value>
-            </property>
-          </properties>
         </configuration>
       </plugin>
 
@@ -372,6 +366,26 @@
       <artifactId>assertj-core</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-suite</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-launcher</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.support</groupId>
+      <artifactId>testng-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <profiles>
     <profile>
diff --git 
a/pinot-common/src/test/java/org/apache/pinot/common/utils/NativeTestNGSuiteTest.java
 
b/pinot-common/src/test/java/org/apache/pinot/common/utils/NativeTestNGSuiteTest.java
new file mode 100644
index 00000000000..7e1b756fd4b
--- /dev/null
+++ 
b/pinot-common/src/test/java/org/apache/pinot/common/utils/NativeTestNGSuiteTest.java
@@ -0,0 +1,230 @@
+/**
+ * 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.pinot.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
+import org.junit.platform.launcher.core.LauncherFactory;
+import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
+import org.junit.platform.launcher.listeners.TestExecutionSummary;
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.SelectMethod;
+import org.junit.platform.suite.api.Suite;
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.DataProvider;
+
+import static 
org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.junit.platform.launcher.EngineFilter.includeEngines;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+
+/// Verifies the native JUnit Platform suites used to group TestNG tests 
without XML suite files.
+/// Not thread-safe: the fixtures share lifecycle events and must execute 
sequentially.
+public class NativeTestNGSuiteTest {
+  private static final List<String> EVENTS = new ArrayList<>();
+
+  @BeforeEach
+  public void resetEvents() {
+    EVENTS.clear();
+  }
+
+  @Test
+  public void testSharedLifecycleAndClassOrder() {
+    TestExecutionSummary summary = execute(SharedSuite.class);
+    assertEquals(summary.getTotalFailureCount(), 0L, 
summary.getFailures().toString());
+    assertEquals(summary.getTestsSucceededCount(), 4L);
+    assertEquals(EVENTS, List.of("start", "first", "second", "third", "last", 
"finish"));
+  }
+
+  @Test
+  public void testReversedClassOrder() {
+    TestExecutionSummary summary = execute(ReversedSuite.class);
+    assertEquals(summary.getTotalFailureCount(), 0L, 
summary.getFailures().toString());
+    assertEquals(summary.getTestsSucceededCount(), 4L);
+    assertEquals(EVENTS, List.of("start", "last", "third", "second", "first", 
"finish"));
+  }
+
+  @Test
+  public void testExcludedGroups() {
+    TestExecutionSummary summary = execute(StatefulSuite.class);
+    assertEquals(summary.getTotalFailureCount(), 0L, 
summary.getFailures().toString());
+    assertEquals(summary.getTestsSucceededCount(), 1L);
+    assertEquals(EVENTS, List.of("start", "stateful", "finish"));
+  }
+
+  @Test
+  public void testIncludedGroupsAndRepeatedDataProviderInvocations() {
+    TestExecutionSummary summary = execute(StatelessSuite.class);
+    assertEquals(summary.getTotalFailureCount(), 0L, 
summary.getFailures().toString());
+    assertEquals(summary.getTestsSucceededCount(), 4L);
+    assertEquals(EVENTS, List.of("start", "data-1", "data-2", "data-1", 
"data-2", "finish"));
+  }
+
+  @Test
+  public void testMethodSelection() {
+    TestExecutionSummary summary = execute(MethodSuite.class);
+    assertEquals(summary.getTotalFailureCount(), 0L, 
summary.getFailures().toString());
+    assertEquals(summary.getTestsSucceededCount(), 1L);
+    assertEquals(EVENTS, List.of("start", "stateful", "finish"));
+  }
+
+  @Test
+  public void testFailurePropagationAndTeardown() {
+    TestExecutionSummary summary = execute(FailingSuite.class);
+    assertEquals(summary.getTestsFailedCount(), 1L);
+    assertEquals(summary.getTotalFailureCount(), 1L);
+    
assertTrue(summary.getFailures().get(0).getException().getMessage().contains("expected
 fixture failure"));
+    assertEquals(EVENTS, List.of("start", "finish"));
+  }
+
+  private static TestExecutionSummary execute(Class<?> suite) {
+    SummaryGeneratingListener listener = new SummaryGeneratingListener();
+    LauncherFactory.create().execute(LauncherDiscoveryRequestBuilder.request()
+        
.selectors(selectClass(suite)).filters(includeEngines("junit-platform-suite")).build(),
 listener);
+    return listener.getSummary();
+  }
+
+  /// Stateless suite declaration that preserves the shared lifecycle and 
class order.
+  @Suite
+  @IncludeEngines("testng")
+  @SelectClasses({ZFirstFixture.class, ASecondFixture.class, 
MThirdFixture.class, BLastFixture.class})
+  @ConfigurationParameter(key = "testng.listeners",
+      value = 
"org.apache.pinot.common.utils.NativeTestNGSuiteTest$SharedSuite")
+  public static class SharedSuite extends OrderedTestNGSuite {
+  }
+
+  /// Reverses the same four fixtures so incidental HashSet iteration cannot 
satisfy both order assertions.
+  @Suite
+  @IncludeEngines("testng")
+  @SelectClasses({BLastFixture.class, MThirdFixture.class, 
ASecondFixture.class, ZFirstFixture.class})
+  @ConfigurationParameter(key = "testng.listeners",
+      value = 
"org.apache.pinot.common.utils.NativeTestNGSuiteTest$ReversedSuite")
+  public static class ReversedSuite extends OrderedTestNGSuite {
+  }
+
+  /// Stateless suite declaration excluding the stateless group.
+  @Suite
+  @IncludeEngines("testng")
+  @SelectClasses(GroupedFixture.class)
+  @ConfigurationParameter(key = "testng.excludedGroups", value = "stateless")
+  public static class StatefulSuite {
+  }
+
+  /// Stateless suite declaration including only the stateless group.
+  @Suite
+  @IncludeEngines("testng")
+  @SelectClasses(GroupedFixture.class)
+  @ConfigurationParameter(key = "testng.groups", value = "stateless")
+  public static class StatelessSuite {
+  }
+
+  /// Stateless suite declaration selecting one method from a class.
+  @Suite
+  @IncludeEngines("testng")
+  @SelectMethod(type = GroupedFixture.class, name = "stateful")
+  public static class MethodSuite {
+  }
+
+  /// Stateless suite declaration containing an intentional failure.
+  @Suite
+  @IncludeEngines("testng")
+  @SelectClasses(FailingFixture.class)
+  public static class FailingSuite {
+  }
+
+  /// Shared lifecycle fixture; accesses the enclosing test's sequential event 
log.
+  public static class SharedFixture {
+    @BeforeSuite(alwaysRun = true)
+    public void start() {
+      assertTrue(EVENTS.isEmpty(), "Suite setup must run exactly once");
+      EVENTS.add("start");
+    }
+
+    @AfterSuite(alwaysRun = true)
+    public void finish() {
+      EVENTS.add("finish");
+    }
+  }
+
+  /// First sequential fixture sharing its suite's lifecycle.
+  public static class ZFirstFixture extends SharedFixture {
+    @org.testng.annotations.Test
+    public void first() {
+      EVENTS.add("first");
+    }
+  }
+
+  /// Second sequential fixture sharing its suite's lifecycle.
+  public static class ASecondFixture extends SharedFixture {
+    @org.testng.annotations.Test
+    public void second() {
+      EVENTS.add("second");
+    }
+  }
+
+  /// Sequential fixture verifying a third nonalphabetical class selection.
+  public static class MThirdFixture extends SharedFixture {
+    @org.testng.annotations.Test
+    public void third() {
+      EVENTS.add("third");
+    }
+  }
+
+  /// Sequential fixture that must execute last, like the Kafka shutdown 
scenario.
+  public static class BLastFixture extends SharedFixture {
+    @org.testng.annotations.Test
+    public void last() {
+      EVENTS.add("last");
+    }
+  }
+
+  /// Sequential fixture with distinct groups and repeated parameterized 
invocations.
+  public static class GroupedFixture extends SharedFixture {
+    @org.testng.annotations.Test
+    public void stateful() {
+      EVENTS.add("stateful");
+    }
+
+    @org.testng.annotations.Test(groups = "stateless", dataProvider = 
"values", invocationCount = 2)
+    public void stateless(int value) {
+      EVENTS.add("data-" + value);
+    }
+
+    @DataProvider
+    public Object[][] values() {
+      return new Object[][]{{1}, {2}};
+    }
+  }
+
+  /// Sequential fixture verifying that native test failures reach the 
platform launcher.
+  public static class FailingFixture extends SharedFixture {
+    @org.testng.annotations.Test
+    public void failure() {
+      fail("expected fixture failure");
+    }
+  }
+}
diff --git 
a/pinot-common/src/test/java/org/apache/pinot/common/utils/OrderedTestNGSuite.java
 
b/pinot-common/src/test/java/org/apache/pinot/common/utils/OrderedTestNGSuite.java
new file mode 100644
index 00000000000..517e6e2cc59
--- /dev/null
+++ 
b/pinot-common/src/test/java/org/apache/pinot/common/utils/OrderedTestNGSuite.java
@@ -0,0 +1,49 @@
+/**
+ * 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.pinot.common.utils;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import org.junit.platform.suite.api.SelectClasses;
+import org.testng.IAlterSuiteListener;
+import org.testng.xml.XmlClass;
+import org.testng.xml.XmlSuite;
+import org.testng.xml.XmlTest;
+
+
+/// Restores the declared class order after the TestNG engine converts class 
selectors to method selectors.
+/// TestNG collects those methods' classes in a HashSet, so preserveOrder 
alone cannot retain the selection order.
+/// Stateless and thread-safe; register the concrete @SelectClasses suite as a 
testng.listeners parameter.
+public abstract class OrderedTestNGSuite implements IAlterSuiteListener {
+  @Override
+  public final void alter(List<XmlSuite> suites) {
+    List<Class<?>> classOrder = 
Arrays.asList(getClass().getAnnotation(SelectClasses.class).value());
+    for (XmlSuite suite : suites) {
+      for (XmlTest test : suite.getTests()) {
+        List<XmlClass> classes = test.getXmlClasses();
+        classes.sort(Comparator.comparingInt(xmlClass -> 
classOrder.indexOf(xmlClass.getSupportClass())));
+        for (int i = 0; i < classes.size(); i++) {
+          classes.get(i).setIndex(i);
+        }
+        test.setPreserveOrder(true);
+      }
+    }
+  }
+}
diff --git a/pinot-controller/pom.xml b/pinot-controller/pom.xml
index d89cf983459..63047319a25 100644
--- a/pinot-controller/pom.xml
+++ b/pinot-controller/pom.xml
@@ -108,6 +108,11 @@
       <artifactId>mockito-core</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-suite</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <build>
     <resources>
@@ -168,49 +173,51 @@
           </execution>
         </executions>
       </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <!-- The stateful and stateless TestNG suites run in separate forks 
but Surefire names
-               both reports TEST-TestSuite.xml. Keep their reports in 
fork-specific subdirectories
-               so one suite cannot overwrite the other. -->
-          
<reportsDirectory>${project.build.directory}/surefire-reports/$${surefire.forkNumber}</reportsDirectory>
-          <!-- Surefire collapses repeated TestNG invocations onto one 
class/method key when it
-               computes the XML suite count. Use TestNG's per-class JUnit 
reporter for this module;
-               it reports every invocation and does not write the shared HTML 
assets. -->
-          <disableXmlReport>true</disableXmlReport>
-          <properties combine.children="append">
-            <property>
-              <name>reporter</name>
-              <value>org.testng.reporters.JUnitReportReporter</value>
-            </property>
-          </properties>
-          <suiteXmlFiles>
-            <suiteXmlFile>testng-statefull.xml</suiteXmlFile>
-            <suiteXmlFile>testng-stateless.xml</suiteXmlFile>
-          </suiteXmlFiles>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-report-plugin</artifactId>
-        <version>${surefire.version}</version>
-        <configuration>
-          <!-- CI uses three possible fork indices, while Controller has only 
two TestNG suite test
-               sets, so at most two directories are populated. List all three; 
the report plugin
-               ignores missing directories. -->
-          <reportsDirectories>
-            
<reportsDirectory>${project.build.directory}/surefire-reports/1/junitreports</reportsDirectory>
-            
<reportsDirectory>${project.build.directory}/surefire-reports/2/junitreports</reportsDirectory>
-            
<reportsDirectory>${project.build.directory}/surefire-reports/3/junitreports</reportsDirectory>
-          </reportsDirectories>
-        </configuration>
-      </plugin>
     </plugins>
   </build>
 
   <profiles>
+    <!-- Explicit -Dtest selections bypass the grouped suites, as they did 
with suiteXmlFiles. -->
+    <profile>
+      <id>controller-test-suites</id>
+      <activation>
+        <property>
+          <name>!test</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>default-test</id>
+                <configuration>
+                  <includes>
+                    <include>**/suites/ControllerStatefulSuite.java</include>
+                  </includes>
+                  <reportNameSuffix>stateful</reportNameSuffix>
+                </configuration>
+              </execution>
+              <execution>
+                <id>stateless-tests</id>
+                <phase>test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>**/suites/ControllerStatelessSuite.java</include>
+                  </includes>
+                  <reportNameSuffix>stateless</reportNameSuffix>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
     <profile>
       <id>bin-dist</id>
       <properties>
diff --git 
a/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerTestSetup.java
 
b/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerTestSetup.java
index 340ebdb9cfe..a36a81dcbd9 100644
--- 
a/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerTestSetup.java
+++ 
b/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerTestSetup.java
@@ -23,7 +23,7 @@ import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
 
 
-/// All test cases in [org.apache.pinot.controller] package are run as part a 
TestNG group (see testng\_\*.xml).
+/// Tests in [org.apache.pinot.controller] run as a TestNG group selected by 
the controller suites.
 /// This helps to setup (see [#setUpGroup()] and tear down (see 
[#tearDownGroup()] the shared state before
 /// and after all tests are run. Each test case class should implement a 
@BeforeClass method, which would call
 /// @link ControllerTest.getInstance()#validate()} method to validate shared 
state. Each test case class should also
diff --git 
a/pinot-controller/src/test/java/org/apache/pinot/controller/suites/ControllerStatefulSuite.java
 
b/pinot-controller/src/test/java/org/apache/pinot/controller/suites/ControllerStatefulSuite.java
new file mode 100644
index 00000000000..583178a9fe0
--- /dev/null
+++ 
b/pinot-controller/src/test/java/org/apache/pinot/controller/suites/ControllerStatefulSuite.java
@@ -0,0 +1,36 @@
+/**
+ * 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.pinot.controller.suites;
+
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeClassNamePatterns;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the stateful controller tests as one TestNG group.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@SelectPackages("org.apache.pinot.controller")
+@IncludeClassNamePatterns(".*")
+@ConfigurationParameter(key = "testng.excludedGroups", value = "stateless")
+public class ControllerStatefulSuite {
+}
diff --git 
a/pinot-controller/src/test/java/org/apache/pinot/controller/suites/ControllerStatelessSuite.java
 
b/pinot-controller/src/test/java/org/apache/pinot/controller/suites/ControllerStatelessSuite.java
new file mode 100644
index 00000000000..5d8137108c5
--- /dev/null
+++ 
b/pinot-controller/src/test/java/org/apache/pinot/controller/suites/ControllerStatelessSuite.java
@@ -0,0 +1,36 @@
+/**
+ * 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.pinot.controller.suites;
+
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeClassNamePatterns;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the stateless controller tests as one TestNG group.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@SelectPackages("org.apache.pinot.controller")
+@IncludeClassNamePatterns(".*")
+@ConfigurationParameter(key = "testng.groups", value = "stateless")
+public class ControllerStatelessSuite {
+}
diff --git a/pinot-controller/testng-statefull.xml 
b/pinot-controller/testng-statefull.xml
deleted file mode 100644
index fc61e096a0d..00000000000
--- a/pinot-controller/testng-statefull.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"; >
-<!--
-
-    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.
-
--->
-<suite name="testng.suite.controller.statefull">
-  <!--
-      Test cases listed here share state with each other (see 
ControllerTestSetup.java). This allows
-      the test suite to run faster becuase individual test cases don't have to 
waste time setting up
-      and destroying their own individual state.
-  -->
-  <test name="testng.suite.controller.statefull">
-    <groups>
-      <run>
-        <exclude name="stateless"/>
-      </run>
-    </groups>
-    <packages>
-      <package name="org.apache.pinot.controller.*"/>
-    </packages>
-  </test>
-</suite>
diff --git a/pinot-controller/testng-stateless.xml 
b/pinot-controller/testng-stateless.xml
deleted file mode 100644
index ce6e14e4e69..00000000000
--- a/pinot-controller/testng-stateless.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"; >
-<!--
-
-    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.
-
--->
-<suite name="testng.suite.controller.stateless">
-  <!--
-      These test cases take extra time to run because they have to spend time 
setting up and destroying
-      their individual state. When possible add new test cases to the 
testng-stateful.xml suite, so that
-      time is not wasted setting up and destroying state and test cases can 
run faster. Add test cases
-      here only if your test cases can not use the shared state that is setup 
in the testng-stateful.xml
-      suite.
-  -->
-  <test name="testng.suite.controller.stateless">
-    <groups>
-      <run>
-        <include name="stateless"/>
-      </run>
-    </groups>
-    <packages>
-      <package name="org.apache.pinot.controller.*"/>
-    </packages>
-  </test>
-</suite>
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestAggregationFunctionTest.java
index 256ef347a29..c1f67b24f3a 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestAggregationFunctionTest.java
@@ -292,53 +292,4 @@ public class PercentileSmartTDigestAggregationFunctionTest 
{
     assertEquals(digest.quantile(0.5), (expectedSize - 1.0) / 2.0, 1.0);
     assertEquals(digest.quantile(1.0), expectedSize - 1.0);
   }
-
-  public static class WithHighThreshold extends 
AbstractPercentileAggregationFunctionTest {
-    @Override
-    public String callStr(String column, int percent) {
-      return "PERCENTILESMARTTDIGEST(" + column + ", " + percent + ", 
'THRESHOLD=10000')";
-    }
-  }
-
-  public static class WithSmallThreshold extends 
AbstractPercentileAggregationFunctionTest {
-    @Override
-    public String callStr(String column, int percent) {
-      return "PERCENTILESMARTTDIGEST(" + column + ", " + percent + ", 
'THRESHOLD=1')";
-    }
-
-    @Override
-    String expectedAggrWithNull10(Scenario scenario) {
-      return "1.0";
-    }
-
-    @Override
-    String expectedAggrWithNull30(Scenario scenario) {
-      return "3.0";
-    }
-
-    @Override
-    String expectedAggrWithNull50(Scenario scenario) {
-      return "5.0";
-    }
-
-    @Override
-    String expectedAggrWithNull70(Scenario scenario) {
-      return "7.0";
-    }
-
-    @Override
-    String expectedAggrWithoutNull55(Scenario scenario) {
-      return "0.0";
-    }
-
-    @Override
-    String expectedAggrWithoutNull75(Scenario scenario) {
-      return "4.0";
-    }
-
-    @Override
-    String expectedAggrWithoutNull90(Scenario scenario) {
-      return "7.0";
-    }
-  }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestHighThresholdAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestHighThresholdAggregationFunctionTest.java
new file mode 100644
index 00000000000..6fb293196ca
--- /dev/null
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestHighThresholdAggregationFunctionTest.java
@@ -0,0 +1,31 @@
+/**
+ * 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.pinot.core.query.aggregation.function;
+
+
+/// Tests exact percentile aggregation below the smart t-digest conversion 
threshold.
+/// Inherits per-class segment state and runs test methods sequentially.
+public class PercentileSmartTDigestHighThresholdAggregationFunctionTest
+    extends AbstractPercentileAggregationFunctionTest {
+  @Override
+  public String callStr(String column, int percent) {
+    return "PERCENTILESMARTTDIGEST(" + column + ", " + percent + ", 
'THRESHOLD=10000')";
+  }
+}
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestSmallThresholdAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestSmallThresholdAggregationFunctionTest.java
new file mode 100644
index 00000000000..4409e7a9827
--- /dev/null
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestSmallThresholdAggregationFunctionTest.java
@@ -0,0 +1,66 @@
+/**
+ * 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.pinot.core.query.aggregation.function;
+
+
+/// Tests percentile aggregation after the smart t-digest conversion threshold 
is reached.
+/// Inherits per-class segment state and runs test methods sequentially.
+public class PercentileSmartTDigestSmallThresholdAggregationFunctionTest
+    extends AbstractPercentileAggregationFunctionTest {
+  @Override
+  public String callStr(String column, int percent) {
+    return "PERCENTILESMARTTDIGEST(" + column + ", " + percent + ", 
'THRESHOLD=1')";
+  }
+
+  @Override
+  String expectedAggrWithNull10(Scenario scenario) {
+    return "1.0";
+  }
+
+  @Override
+  String expectedAggrWithNull30(Scenario scenario) {
+    return "3.0";
+  }
+
+  @Override
+  String expectedAggrWithNull50(Scenario scenario) {
+    return "5.0";
+  }
+
+  @Override
+  String expectedAggrWithNull70(Scenario scenario) {
+    return "7.0";
+  }
+
+  @Override
+  String expectedAggrWithoutNull55(Scenario scenario) {
+    return "0.0";
+  }
+
+  @Override
+  String expectedAggrWithoutNull75(Scenario scenario) {
+    return "4.0";
+  }
+
+  @Override
+  String expectedAggrWithoutNull90(Scenario scenario) {
+    return "7.0";
+  }
+}
diff --git a/pinot-integration-tests/pom.xml b/pinot-integration-tests/pom.xml
index 8c26aaca0bd..bdfb27f58cc 100644
--- a/pinot-integration-tests/pom.xml
+++ b/pinot-integration-tests/pom.xml
@@ -161,9 +161,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/shared-kafka-realtime-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/SharedKafkaRealtimeSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
               <execution>
@@ -178,9 +178,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/multi-nodes-offline-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/MultiNodesOfflineSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
               <execution>
@@ -195,9 +195,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/shared-hybrid-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/SharedHybridSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
             </executions>
@@ -296,9 +296,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/shared-kafka-realtime-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/SharedKafkaRealtimeSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
               <execution>
@@ -313,9 +313,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/kinesis-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/KinesisSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
             </executions>
@@ -369,9 +369,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/multi-nodes-offline-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/MultiNodesOfflineSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
               <execution>
@@ -386,9 +386,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/custom-cluster-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/CustomClusterSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
               <execution>
@@ -403,9 +403,9 @@
                   <excludes combine.self="override">
                     <exclude>none</exclude>
                   </excludes>
-                  <suiteXmlFiles>
-                    
<suiteXmlFile>src/test/resources/shared-hybrid-integration-test-suite.xml</suiteXmlFile>
-                  </suiteXmlFiles>
+                  <includes combine.self="override">
+                    <include>**/suites/SharedHybridSuite.java</include>
+                  </includes>
                 </configuration>
               </execution>
             </executions>
@@ -498,9 +498,9 @@
             <artifactId>maven-surefire-plugin</artifactId>
             <configuration>
               <skipTests>false</skipTests>
-              <suiteXmlFiles>
-                
<suiteXmlFile>src/test/resources/custom-cluster-integration-test-suite.xml</suiteXmlFile>
-              </suiteXmlFiles>
+              <includes combine.self="override">
+                <include>**/suites/CustomClusterSuite.java</include>
+              </includes>
             </configuration>
           </plugin>
         </plugins>
@@ -521,9 +521,9 @@
             <artifactId>maven-surefire-plugin</artifactId>
             <configuration>
               <skipTests>false</skipTests>
-              <suiteXmlFiles>
-                
<suiteXmlFile>src/test/resources/kinesis-integration-test-suite.xml</suiteXmlFile>
-              </suiteXmlFiles>
+              <includes combine.self="override">
+                <include>**/suites/KinesisSuite.java</include>
+              </includes>
             </configuration>
           </plugin>
         </plugins>
@@ -701,5 +701,10 @@
       <artifactId>pinot-batch-ingestion-spark-3</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-suite</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/CustomClusterSuite.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/CustomClusterSuite.java
new file mode 100644
index 00000000000..1f4fc87548a
--- /dev/null
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/CustomClusterSuite.java
@@ -0,0 +1,34 @@
+/**
+ * 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.pinot.integration.tests.suites;
+
+import org.junit.platform.suite.api.IncludeClassNamePatterns;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the custom-data scenarios with one shared cluster.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@SelectPackages("org.apache.pinot.integration.tests.custom")
+@IncludeClassNamePatterns(".*")
+public class CustomClusterSuite {
+}
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/KinesisSuite.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/KinesisSuite.java
new file mode 100644
index 00000000000..17d4b843d93
--- /dev/null
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/KinesisSuite.java
@@ -0,0 +1,41 @@
+/**
+ * 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.pinot.integration.tests.suites;
+
+import org.apache.pinot.common.utils.OrderedTestNGSuite;
+import 
org.apache.pinot.integration.tests.realtime.ingestion.KinesisShardChangeTest;
+import 
org.apache.pinot.integration.tests.realtime.ingestion.RealtimeKinesisIntegrationTest;
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the Kinesis scenarios with one shared cluster.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@ConfigurationParameter(key = "testng.listeners",
+    value = "org.apache.pinot.integration.tests.suites.KinesisSuite")
+@SelectClasses({
+    RealtimeKinesisIntegrationTest.class,
+    KinesisShardChangeTest.class
+})
+public class KinesisSuite extends OrderedTestNGSuite {
+}
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/MultiNodesOfflineSuite.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/MultiNodesOfflineSuite.java
new file mode 100644
index 00000000000..9e86c46eaa9
--- /dev/null
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/MultiNodesOfflineSuite.java
@@ -0,0 +1,38 @@
+/**
+ * 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.pinot.integration.tests.suites;
+
+import 
org.apache.pinot.integration.tests.MultiNodesOfflineClusterIntegrationTest;
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectMethod;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the focused multi-node offline scenarios with one shared cluster.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@ConfigurationParameter(key = "testng.preserveOrder", value = "true")
+@SelectMethod(type = MultiNodesOfflineClusterIntegrationTest.class, name = 
"testUpdateBrokerResource")
+@SelectMethod(type = MultiNodesOfflineClusterIntegrationTest.class, name = 
"testServerHardFailure")
+@SelectMethod(type = MultiNodesOfflineClusterIntegrationTest.class, name = 
"testServerReturnFinalResult")
+@SelectMethod(type = MultiNodesOfflineClusterIntegrationTest.class, name = 
"testConstantExpressionQuery")
+public class MultiNodesOfflineSuite {
+}
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/SharedHybridSuite.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/SharedHybridSuite.java
new file mode 100644
index 00000000000..60e5ae9f011
--- /dev/null
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/SharedHybridSuite.java
@@ -0,0 +1,43 @@
+/**
+ * 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.pinot.integration.tests.suites;
+
+import org.apache.pinot.common.utils.OrderedTestNGSuite;
+import 
org.apache.pinot.integration.tests.DateTimeFieldSpecHybridClusterIntegrationTest;
+import org.apache.pinot.integration.tests.HybridClusterIntegrationTest;
+import org.apache.pinot.integration.tests.IngestionConfigHybridIntegrationTest;
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the hybrid scenarios with one shared cluster.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@ConfigurationParameter(key = "testng.listeners",
+    value = "org.apache.pinot.integration.tests.suites.SharedHybridSuite")
+@SelectClasses({
+    HybridClusterIntegrationTest.class,
+    DateTimeFieldSpecHybridClusterIntegrationTest.class,
+    IngestionConfigHybridIntegrationTest.class
+})
+public class SharedHybridSuite extends OrderedTestNGSuite {
+}
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/SharedKafkaRealtimeSuite.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/SharedKafkaRealtimeSuite.java
new file mode 100644
index 00000000000..b0d4a8da063
--- /dev/null
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/suites/SharedKafkaRealtimeSuite.java
@@ -0,0 +1,45 @@
+/**
+ * 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.pinot.integration.tests.suites;
+
+import org.apache.pinot.common.utils.OrderedTestNGSuite;
+import 
org.apache.pinot.integration.tests.ExactlyOnceKafkaRealtimeClusterIntegrationTest;
+import 
org.apache.pinot.integration.tests.KafkaConfluentSchemaRegistryAvroMessageDecoderRealtimeClusterIntegrationTest;
+import 
org.apache.pinot.integration.tests.KafkaConsumingSegmentToBeMovedSummaryIntegrationTest;
+import org.apache.pinot.integration.tests.LLCRealtimeClusterIntegrationTest;
+import org.junit.platform.suite.api.ConfigurationParameter;
+import org.junit.platform.suite.api.IncludeEngines;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+
+/// Runs the Kafka scenarios with one shared cluster.
+/// Stateless suite definition; the selected tests run sequentially in one 
fork.
+@Suite
+@IncludeEngines("testng")
+@ConfigurationParameter(key = "testng.listeners",
+    value = 
"org.apache.pinot.integration.tests.suites.SharedKafkaRealtimeSuite")
+@SelectClasses({
+    LLCRealtimeClusterIntegrationTest.class,
+    ExactlyOnceKafkaRealtimeClusterIntegrationTest.class,
+    
KafkaConfluentSchemaRegistryAvroMessageDecoderRealtimeClusterIntegrationTest.class,
+    KafkaConsumingSegmentToBeMovedSummaryIntegrationTest.class
+})
+public class SharedKafkaRealtimeSuite extends OrderedTestNGSuite {
+}
diff --git 
a/pinot-integration-tests/src/test/resources/custom-cluster-integration-test-suite.xml
 
b/pinot-integration-tests/src/test/resources/custom-cluster-integration-test-suite.xml
deleted file mode 100644
index f00fbd9ad2b..00000000000
--- 
a/pinot-integration-tests/src/test/resources/custom-cluster-integration-test-suite.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-
-    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.
-
--->
-<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"; >
-<suite name="CustomDataQueryClusterIntegrationTestSuite">
-  <test name="CustomDataQueryClusterIntegrationTest">
-    <packages>
-      <package name="org.apache.pinot.integration.tests.custom"/>
-    </packages>
-  </test>
-</suite>
diff --git 
a/pinot-integration-tests/src/test/resources/kinesis-integration-test-suite.xml 
b/pinot-integration-tests/src/test/resources/kinesis-integration-test-suite.xml
deleted file mode 100644
index 91b54fe88fe..00000000000
--- 
a/pinot-integration-tests/src/test/resources/kinesis-integration-test-suite.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-
-    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.
-
--->
-<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"; >
-<suite name="KinesisIngestionIntegrationTestSuite" parallel="none">
-  <test name="KinesisIngestionIntegrationTests" preserve-order="true">
-    <classes>
-      <class 
name="org.apache.pinot.integration.tests.realtime.ingestion.RealtimeKinesisIntegrationTest"/>
-      <class 
name="org.apache.pinot.integration.tests.realtime.ingestion.KinesisShardChangeTest"/>
-    </classes>
-  </test>
-</suite>
diff --git 
a/pinot-integration-tests/src/test/resources/multi-nodes-offline-integration-test-suite.xml
 
b/pinot-integration-tests/src/test/resources/multi-nodes-offline-integration-test-suite.xml
deleted file mode 100644
index 90d19249cde..00000000000
--- 
a/pinot-integration-tests/src/test/resources/multi-nodes-offline-integration-test-suite.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
-
-    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.
-
--->
-<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"; >
-<suite name="MultiNodesOfflineClusterIntegrationTestSuite" parallel="false">
-  <test name="MultiNodesOfflineClusterIntegrationTests" preserve-order="true">
-    <classes>
-      <class 
name="org.apache.pinot.integration.tests.MultiNodesOfflineClusterIntegrationTest">
-        <methods>
-          <include name="testUpdateBrokerResource"/>
-          <include name="testServerHardFailure"/>
-          <include name="testServerReturnFinalResult"/>
-          <!-- Leaves the multi-stage engine enabled, so keep it last. -->
-          <include name="testConstantExpressionQuery"/>
-        </methods>
-      </class>
-    </classes>
-  </test>
-</suite>
diff --git 
a/pinot-integration-tests/src/test/resources/shared-hybrid-integration-test-suite.xml
 
b/pinot-integration-tests/src/test/resources/shared-hybrid-integration-test-suite.xml
deleted file mode 100644
index 35ebe8ecb41..00000000000
--- 
a/pinot-integration-tests/src/test/resources/shared-hybrid-integration-test-suite.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-
-    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.
-
--->
-<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"; >
-<suite name="SharedHybridIntegrationTestSuite" parallel="false">
-  <test name="SharedHybridIntegrationTests" preserve-order="true">
-    <classes>
-      <!-- Canonical hybrid query and API coverage runs once; the following 
classes are focused scenarios. -->
-      <class 
name="org.apache.pinot.integration.tests.HybridClusterIntegrationTest"/>
-      <class 
name="org.apache.pinot.integration.tests.DateTimeFieldSpecHybridClusterIntegrationTest"/>
-      <class 
name="org.apache.pinot.integration.tests.IngestionConfigHybridIntegrationTest"/>
-    </classes>
-  </test>
-</suite>
diff --git 
a/pinot-integration-tests/src/test/resources/shared-kafka-realtime-integration-test-suite.xml
 
b/pinot-integration-tests/src/test/resources/shared-kafka-realtime-integration-test-suite.xml
deleted file mode 100644
index 405c2b7e67c..00000000000
--- 
a/pinot-integration-tests/src/test/resources/shared-kafka-realtime-integration-test-suite.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-
-    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.
-
--->
-<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"; >
-<suite name="SharedKafkaRealtimeIntegrationTestSuite" parallel="false">
-  <test name="SharedKafkaRealtimeIntegrationTests" preserve-order="true">
-    <classes>
-      <!-- Canonical query/LLC coverage runs once; the following classes 
contain only focused stream assertions. -->
-      <class 
name="org.apache.pinot.integration.tests.LLCRealtimeClusterIntegrationTest"/>
-      <class 
name="org.apache.pinot.integration.tests.ExactlyOnceKafkaRealtimeClusterIntegrationTest"/>
-      <class 
name="org.apache.pinot.integration.tests.KafkaConfluentSchemaRegistryAvroMessageDecoderRealtimeClusterIntegrationTest"/>
-      <!-- Adds a server and stops Kafka, so it must remain last. -->
-      <class 
name="org.apache.pinot.integration.tests.KafkaConsumingSegmentToBeMovedSummaryIntegrationTest"/>
-    </classes>
-  </test>
-</suite>
diff --git a/pom.xml b/pom.xml
index 2a28e7067ff..dd20c6f1584 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.apache</groupId>
     <artifactId>apache</artifactId>
-    <version>39</version>
+    <version>40</version>
     <relativePath />
   </parent>
 
@@ -358,6 +358,8 @@
     <!-- Test Libraries -->
     <testng.version>7.12.0</testng.version>
     <junit-jupiter-api.version>5.14.4</junit-jupiter-api.version>
+    <junit-platform.version>1.14.4</junit-platform.version>
+    <testng-engine.version>1.1.0</testng-engine.version>
     <mockito-core.version>5.23.0</mockito-core.version>
     <equalsverifier.version>3.19.4</equalsverifier.version>
     <testcontainers.version>2.0.5</testcontainers.version>
@@ -2092,6 +2094,24 @@
         <type>pom</type>
         <scope>import</scope>
       </dependency>
+      <dependency>
+        <groupId>org.junit.platform</groupId>
+        <artifactId>junit-platform-suite</artifactId>
+        <version>${junit-platform.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.junit.platform</groupId>
+        <artifactId>junit-platform-launcher</artifactId>
+        <version>${junit-platform.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.junit.support</groupId>
+        <artifactId>testng-engine</artifactId>
+        <version>${testng-engine.version}</version>
+        <scope>test</scope>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 
@@ -2243,14 +2263,6 @@
               -XX:+EnableDynamicAgentLoading
             </argLine>
           </configuration>
-          <!-- Explicitly select the test provider, instead of relying on the 
classpath -->
-          <dependencies>
-            <dependency>
-              <groupId>org.apache.maven.surefire</groupId>
-              <artifactId>surefire-testng</artifactId>
-              <version>${surefire.version}</version>
-            </dependency>
-          </dependencies>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -2574,13 +2586,10 @@
                green-washed. -->
           
<rerunFailingTestsCount>${unit.test.rerun.count}</rerunFailingTestsCount>
           <properties>
-            <!-- Surefire is the default report writer; modules can explicitly 
opt into an isolated
-                 reporter. TestNG's native HTML/XML reporters write shared 
files and assets, which
-                 race when multiple forks use one module. -->
-            <property>
-              <name>usedefaultlisteners</name>
-              <value>false</value>
-            </property>
+            <!-- Use Surefire's reports; TestNG's default reporters share 
files across parallel forks. -->
+            <configurationParameters>
+              testng.useDefaultListeners=false
+            </configurationParameters>
           </properties>
           <systemPropertyVariables>
             <!-- Expose the 1-based fork index (1 even at forkCount=1, 1..N 
under parallel forks)


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

Reply via email to