oldProps = ctx.getStore(NAMESPACE).remove(ctx.getUniqueId(), List.class);
+
+ if (oldProps == null) {
+ return; // Nothing to do.
+ }
+
+ // Bring back the old properties in the reverse order
+ Collections.reverse(oldProps);
+
+ for (Property prop : oldProps) {
+ if (prop.val == null)
+ System.clearProperty(prop.key);
+ else
+ System.setProperty(prop.key, prop.val);
+ }
+ }
+
+ /** Property. */
+ private static class Property {
+ /** Property key. */
+ final String key;
+
+ /** Property value. */
+ @Nullable
+ final String val;
+
+ /**
+ * Constructor.
+ *
+ * @param key Property key.
+ * @param val Property value.
+ */
+ Property(String key, @Nullable String val) {
+ this.key = key;
+ this.val = val;
+ }
+ }
+}
+
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testframework/junit/SystemPropertiesList.java b/modules/calcite/src/test/java/org/apache/ignite/testframework/junit/SystemPropertiesList.java
new file mode 100644
index 0000000000000..170f5d7d4e5a8
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/testframework/junit/SystemPropertiesList.java
@@ -0,0 +1,35 @@
+/*
+ * 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.ignite.testframework.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@link Repeatable} for the {@link WithSystemProperty}.
Not intended for direct usage. Use multiple {@link WithSystemProperty}
+ * annotation instead.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface SystemPropertiesList {
+ /** Array of underlying annotations. */
+ WithSystemProperty[] value();
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testframework/junit/WithSystemProperty.java b/modules/calcite/src/test/java/org/apache/ignite/testframework/junit/WithSystemProperty.java
new file mode 100644
index 0000000000000..0c117acb4a327
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/testframework/junit/WithSystemProperty.java
@@ -0,0 +1,110 @@
+/*
+ * 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.ignite.testframework.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+/**
+ * Annotation that defines a scope with specific system property configured.
+ *
+ * Might be used on class level or on method level. Multiple annotations might be applied to the same class/method.
+ *
+ * In short, these two approaches are basically equivalent:
+ *
+ * Short:
+ * {@literal @}WithSystemProperty(key = "name", value = "val")
+ * public class SomeTest {
+ * }
+ * Long:
+ * public class SomeTest {
+ * private static Object oldVal;
+ *
+ * {@literal @}BeforeClass
+ * public static void beforeClass() {
+ * oldVal = System.getProperty("name");
+ *
+ * System.setProperty("name", "val");
+ * }
+ *
+ * {@literal @}AfterClass
+ * public static void afterClass() {
+ * if (oldVal == null)
+ * System.clearProperty("name");
+ * else
+ * System.setProperty("name", oldVal);
+ * }
+ * }
+ *
+ * Same applies to methods with the difference that annotation translates into something like {@link BeforeEach} and {@link AfterEach}.
+ *
+ *
public class SomeTest {
+ * {@literal @}Test
+ * {@literal @}WithSystemProperty(key = "name", value = "val")
+ * public void test() {
+ * // ...
+ * }
+ * }
+ *
+ * is equivalent to:
+ * public class SomeTest {
+ * {@literal @}Test
+ * public void test() {
+ * Object oldVal = System.getProperty("name");
+ *
+ * try {
+ * // ...
+ * }
+ * finally {
+ * if (oldVal == null)
+ * System.clearProperty("name");
+ * else
+ * System.setProperty("name", oldVal);
+ * }
+ * }
+ * }
+ *
+ * For class level annotation it applies system properties for the whole class hierarchy (ignoring interfaces, there's no linearization
+ * implemented). More specific classes have higher priority and set their properties last. It all starts with {@link Object} which, of
+ * course, is not annotated.
+ *
+ * Test methods do not inherit their annotations from overridden methods of super class.
+ *
+ * If more than one annotation is presented on class/method then they will be applied in the same order as they appear in code. It is
+ * achieved with the help of {@link Repeatable} feature of Java annotations - {@link SystemPropertiesList} is automatically generated in
+ * such cases. For that reason it is not recommended using {@link SystemPropertiesList} directly.
+ *
+ * @see System#setProperty(String, String)
+ * @see SystemPropertiesExtension
+ * @see SystemPropertiesList
+ */
+@Repeatable(SystemPropertiesList.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface WithSystemProperty {
+ /** The name of the system property. */
+ String key();
+
+ /** The value of the system property. */
+ String value();
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/ExecutionTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/ExecutionTestSuite.java
index 6b01981684987..2c62be0aff780 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/ExecutionTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/ExecutionTestSuite.java
@@ -37,14 +37,14 @@
import org.apache.ignite.internal.processors.query.calcite.exec.rel.TimeCalculationExecutionTest;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.UncollectExecutionTest;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.WindowExecutionTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite execution tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
ExecutionTest.class,
ContinuousExecutionTest.class,
MergeJoinExecutionTest.class,
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
index d1417c7d124dc..2de7fb9938346 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
@@ -27,14 +27,14 @@
import org.apache.ignite.internal.processors.tx.SqlTransactionsIsolationTest;
import org.apache.ignite.internal.processors.tx.SqlTransactionsSavepointTest;
import org.apache.ignite.internal.processors.tx.SqlTransactionsUnsupportedModesTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
UtilTestSuite.class,
ParserCodegenResourcesTest.class,
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite2.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite2.java
index 68002b4563535..abbcf2f185766 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite2.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite2.java
@@ -17,14 +17,16 @@
package org.apache.ignite.testsuites;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.apache.ignite.internal.processors.query.calcite.integration.TestSuiteDeclarationArchTest;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
+ TestSuiteDeclarationArchTest.class,
PlannerTestSuite.class,
ExecutionTestSuite.class,
JdbcTestSuite.class,
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite3.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite3.java
index 4d9074ecfc8cc..3832c3469f115 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite3.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite3.java
@@ -17,14 +17,15 @@
package org.apache.ignite.testsuites;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
/**
* Calcite tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
IntegrationTestSuite.class
})
public class IgniteCalciteTestSuite3 {
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index c9605a431cd29..833729b5e0b60 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -104,14 +104,14 @@
import org.apache.ignite.internal.processors.query.calcite.thin.MultiLineQueryTest;
import org.apache.ignite.internal.processors.tx.TxThreadLockingTest;
import org.apache.ignite.internal.processors.tx.TxWithExceptionalInterceptorTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
OrToUnionRuleTest.class,
ProjectScanMergeRuleTest.class,
CalciteQueryProcessorTest.class,
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/JdbcTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/JdbcTestSuite.java
index b69f3cf83470d..2e2a21ff77008 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/JdbcTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/JdbcTestSuite.java
@@ -25,14 +25,14 @@
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcSetClientInfoTest;
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcThinConnectionSavepointTest;
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcThinTransactionalSelfTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite JDBC tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
JdbcQueryTest.class,
JdbcCrossEngineTest.class,
JdbcThinConnectionSavepointTest.class,
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java
index bac3237c6360e..66c7335d27073 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java
@@ -55,14 +55,14 @@
import org.apache.ignite.internal.processors.query.calcite.planner.WindowPlannerTest;
import org.apache.ignite.internal.processors.query.calcite.planner.hints.HintsTestSuite;
import org.apache.ignite.internal.processors.query.calcite.planner.tpc.TpchQueryPlannerTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
PlanExecutionTest.class,
PlanSplitterTest.class,
CorrelatedNestedLoopJoinPlannerTest.class,
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/ScriptTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/ScriptTestSuite.java
index fbed0087a3aeb..de42dee731541 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/ScriptTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/ScriptTestSuite.java
@@ -17,9 +17,35 @@
package org.apache.ignite.testsuites;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Pattern;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.IgnitionEx;
+import org.apache.ignite.internal.processors.query.QueryEngine;
import org.apache.ignite.internal.processors.query.calcite.logical.ScriptRunnerTestsEnvironment;
-import org.apache.ignite.internal.processors.query.calcite.logical.ScriptTestRunner;
-import org.junit.runner.RunWith;
+import org.apache.ignite.internal.processors.query.calcite.logical.SqlScriptRunner;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.logger.GridTestLog4jLogger;
+import org.apache.ignite.thread.IgniteThread;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.TestFactory;
/**
* Test suite to run SQL test scripts.
@@ -71,7 +97,253 @@
* @see Extended format documentation.
*
*/
-@RunWith(ScriptTestRunner.class)
@ScriptRunnerTestsEnvironment(scriptsRoot = "modules/calcite/src/test/sql", timeout = 180000)
public class ScriptTestSuite {
+ /** Filesystem. */
+ private static final FileSystem FS = FileSystems.getDefault();
+
+ /** Suffix of the scripts that are reported as ignored. */
+ private static final String IGNORED_SUFFIX = ".test_ignore";
+
+ /** Shared finder. */
+ private static final TcpDiscoveryVmIpFinder sharedFinder = new TcpDiscoveryVmIpFinder().setShared(true);
+
+ /** */
+ private static IgniteLogger log;
+
+ static {
+ try {
+ log = new GridTestLog4jLogger(U.resolveIgnitePath("modules/core/src/test/config/log4j2-test.xml"));
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.err);
+
+ log = null;
+
+ assert false : "Cannot init logger";
+ }
+ }
+
+ /** Scripts root directory. */
+ private final Path scriptsRoot;
+
+ /** Regex to filter test path to run only specified tests. */
+ private final Pattern testRegex;
+
+ /** Nodes count. */
+ private final int nodes;
+
+ /** Restart cluster for each test group. */
+ private final boolean restartCluster;
+
+ /** Test script timeout. */
+ private final long timeout;
+
+ /** Directory (test group) of the last executed script. */
+ private Path lastTestDir;
+
+ /** */
+ public ScriptTestSuite() {
+ ScriptRunnerTestsEnvironment env = ScriptTestSuite.class.getAnnotation(ScriptRunnerTestsEnvironment.class);
+
+ assert !F.isEmpty(env.scriptsRoot());
+
+ nodes = env.nodes();
+ scriptsRoot = FS.getPath(U.resolveIgnitePath(env.scriptsRoot()).getPath());
+ testRegex = F.isEmpty(env.regex()) ? null : Pattern.compile(env.regex());
+ restartCluster = env.restart();
+ timeout = env.timeout();
+ }
+
+ /**
+ * Generates dynamic tests for each script file in the configured directory.
+ *
+ * @return Stream of dynamic tests.
+ * @throws Exception If failed to walk the script directory.
+ */
+ @TestFactory
+ public List generateTests() throws Exception {
+ // Start cluster if not already started
+ if (F.isEmpty(Ignition.allGrids())) {
+ startCluster();
+ }
+
+ return Files.walk(scriptsRoot)
+ .sorted()
+ .filter(p -> !p.equals(scriptsRoot))
+ .filter(p -> !Files.isDirectory(p))
+ .filter(p -> testRegex == null || testRegex.matcher(p.toString()).find())
+ .filter(p -> {
+ String fileName = p.getFileName().toString();
+
+ return testRegex != null || fileName.endsWith(".test") || fileName.endsWith(".test_slow")
+ || fileName.endsWith(IGNORED_SUFFIX);
+ })
+ .map(p -> {
+ String dirName;
+ if (p.getNameCount() - 1 > scriptsRoot.getNameCount())
+ dirName = p.subpath(scriptsRoot.getNameCount(), p.getNameCount() - 1).toString();
+ else
+ dirName = scriptsRoot.subpath(scriptsRoot.getNameCount() - 1, scriptsRoot.getNameCount()).toString();
+
+ String fileName = p.getFileName().toString();
+
+ // Ignored scripts are reported as skipped (only when no regex filter is set).
+ if (testRegex == null && fileName.endsWith(IGNORED_SUFFIX)) {
+ return DynamicTest.dynamicTest(dirName + "/" + fileName,
+ () -> Assumptions.abort("Script is ignored: " + dirName + "/" + fileName));
+ }
+
+ return DynamicTest.dynamicTest(dirName + "/" + fileName, () -> {
+ runSingleTest(p, dirName, fileName);
+ });
+ })
+ .toList();
+ }
+
+ /** */
+ @AfterAll
+ public static void tearDown() {
+ IgnitionEx.stopAll(true, null);
+ }
+
+ /**
+ * Runs a single test.
+ *
+ * @param test Test file path.
+ * @param dirName Directory name.
+ * @param fileName File name.
+ * @throws Exception If test fails.
+ */
+ private void runSingleTest(Path test, String dirName, String fileName) {
+ beforeTest(test.getParent());
+
+ log.info(">>> Start: " + dirName + "/" + fileName);
+
+ try {
+ Ignite ign = F.first(Ignition.allGrids());
+
+ QueryEngine engine = Commons.lookupComponent(
+ ((IgniteEx)ign).context(),
+ QueryEngine.class
+ );
+
+ SqlScriptRunner scriptTestRunner = new SqlScriptRunner(test, engine, log);
+
+ try {
+ runScript(scriptTestRunner);
+ }
+ catch (Error | RuntimeException e) {
+ throw e;
+ }
+ catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ finally {
+ log.info(">>> Finish: " + dirName + "/" + fileName);
+ }
+ }
+
+ /**
+ * Cleanup before test.
+ *
+ * @param testDir Directory (test group) of the script to run.
+ */
+ private void beforeTest(Path testDir) {
+ // Restart cluster only on a test group (directory) boundary.
+ if (restartCluster && lastTestDir != null && !lastTestDir.equals(testDir) && !F.isEmpty(Ignition.allGrids())) {
+ log.info(">>> Restart cluster");
+
+ Ignition.stopAll(false);
+ }
+
+ lastTestDir = testDir;
+
+ if (F.isEmpty(Ignition.allGrids()))
+ startCluster();
+ else {
+ Ignite ign = F.first(Ignition.allGrids());
+
+ for (String cacheName : ign.cacheNames())
+ ign.destroyCache(cacheName);
+ }
+ }
+
+ /**
+ * Starts the cluster.
+ */
+ private void startCluster() {
+ for (int i = 0; i < nodes; ++i) {
+ Ignition.start(
+ new IgniteConfiguration()
+ .setIgniteInstanceName("srv" + i)
+ .setDiscoverySpi(
+ new TcpDiscoverySpi()
+ .setIpFinder(sharedFinder)
+ )
+ .setGridLogger(log)
+ );
+ }
+ }
+
+ /**
+ * Runs the script with timeout support.
+ *
+ * @param scriptRunner Script runner.
+ */
+ private void runScript(SqlScriptRunner scriptRunner) throws Throwable {
+ final AtomicReference ex = new AtomicReference<>();
+
+ Thread runner = new IgniteThread("srv0", "test-runner", new Runnable() {
+ @Override public void run() {
+ try {
+ scriptRunner.run();
+ }
+ catch (Throwable e) {
+ ex.set(e);
+ }
+ }
+ });
+
+ runner.start();
+
+ runner.join(timeout);
+
+ if (runner.isAlive()) {
+ U.error(log,
+ "Test has been timed out and will be interrupted");
+
+ List nodes = IgnitionEx.allGridsx();
+
+ for (Ignite node : nodes)
+ ((IgniteKernal)node).dumpDebugInfo();
+
+ // We dump threads to stdout, because we can loose logs in case
+ // the build is cancelled on TeamCity.
+ U.dumpThreads(null);
+
+ U.dumpThreads(log);
+
+ // Try to interrupt runner several times for case when InterruptedException is handled invalid.
+ for (int i = 0; i < 100 && runner.isAlive(); ++i) {
+ U.interrupt(runner);
+
+ U.sleep(10);
+ }
+
+ U.join(runner, log);
+
+ // Restart cluster
+ Ignition.stopAll(true);
+ startCluster();
+
+ throw new TimeoutException("Test has been timed out");
+ }
+
+ Throwable t = ex.get();
+
+ if (t != null)
+ throw t;
+ }
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/UtilTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/UtilTestSuite.java
index 527f0240060d6..8d8e0a477702a 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/UtilTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/UtilTestSuite.java
@@ -24,14 +24,14 @@
import org.apache.ignite.internal.processors.query.calcite.exec.task.QueryBlockingTaskExecutorTest;
import org.apache.ignite.internal.processors.query.calcite.exec.task.QueryTasksQueueTest;
import org.apache.ignite.internal.processors.query.calcite.exec.tracker.MemoryTrackerTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
/**
* Calcite utility classes tests.
*/
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@Suite
+@SelectClasses({
ClosableIteratorsHolderTest.class,
MemoryTrackerTest.class,
QueryCheckerTest.class,
diff --git a/modules/calcite/src/test/resources/junit-platform.properties b/modules/calcite/src/test/resources/junit-platform.properties
new file mode 100644
index 0000000000000..84e51f4627393
--- /dev/null
+++ b/modules/calcite/src/test/resources/junit-platform.properties
@@ -0,0 +1,14 @@
+# Catch-all fallback timeout for all test and lifecycle methods.
+junit.jupiter.execution.timeout.default = 5m
+
+# Specific default timeout for standard @Test methods (overrides the catch-all).
+junit.jupiter.execution.timeout.test.method.default = 5m
+
+# Specific default timeout for lifecycle methods (e.g., @BeforeEach, @AfterAll).
+junit.jupiter.execution.timeout.lifecycle.method.default = 60s
+
+# Dump threads if test runs more than expected.
+junit.jupiter.execution.timeout.threaddump.enabled = true
+
+# With random run GridTestClockTimer can never be initialized.
+junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$OrderAnnotation
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
index f6e1f1eb6777f..3fe32fe07bee5 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
@@ -685,6 +685,11 @@ private void resolveWorkDirectory() throws Exception {
sft.mkdirMarshaller();
}
+ /** */
+ protected static void beforeFirstTest0() {
+ sharedStaticIpFinder = new TcpDiscoveryVmIpFinder(true);
+ }
+
/** */
protected void beforeFirstTest() throws Exception {
sharedStaticIpFinder = new TcpDiscoveryVmIpFinder(true);
@@ -779,7 +784,7 @@ private void runAfterTest() throws Exception {
* @param afterTestFinished Boolean flag used to tell whether {@code afterTest()} finished execution.
* @return Scheduled executor used when scheduling.
*/
- private ScheduledExecutorService scheduleThreadDumpOnAfterTestTimeOut(AtomicBoolean afterTestFinished) {
+ public ScheduledExecutorService scheduleThreadDumpOnAfterTestTimeOut(AtomicBoolean afterTestFinished) {
// Compute class name as string to avoid holding reference to the test class instance in task.
String testClsName = getClass().getName();
diff --git a/modules/tools/src/main/java/org/apache/ignite/tools/junit/JUnitTeamcityReporter.java b/modules/tools/src/main/java/org/apache/ignite/tools/junit/JUnitTeamcityReporter.java
index 463c41ab4c32d..9ddcc276cf6c0 100644
--- a/modules/tools/src/main/java/org/apache/ignite/tools/junit/JUnitTeamcityReporter.java
+++ b/modules/tools/src/main/java/org/apache/ignite/tools/junit/JUnitTeamcityReporter.java
@@ -220,8 +220,12 @@ private String fileName() {
return "test-" + prevSuite + prevFlush + ".xml";
}
- /** */
- private String escapeForTeamcity(String msg) {
+ /**
+ * @param msg Message.
+ *
+ * @return Escaped string.
+ */
+ public static String escapeForTeamcity(String msg) {
return (msg == null ? "null" : msg)
.replace("|", "||")
.replace("\r", "|r")
diff --git a/modules/tools/src/main/java/org/apache/ignite/tools/surefire/testsuites/CheckAllTestsInSuites.java b/modules/tools/src/main/java/org/apache/ignite/tools/surefire/testsuites/CheckAllTestsInSuites.java
index 6d6351b3bf64b..9398ec287b86f 100644
--- a/modules/tools/src/main/java/org/apache/ignite/tools/surefire/testsuites/CheckAllTestsInSuites.java
+++ b/modules/tools/src/main/java/org/apache/ignite/tools/surefire/testsuites/CheckAllTestsInSuites.java
@@ -113,6 +113,13 @@ private void processSuite(Description suite, Set suitedClasses,
}
}
+ /** Calcite module tests inherited from legacy junit4 related classes. */
+ private static final Set CALCITE_TESTS = Set.of(
+ "org.apache.ignite.internal.processors.query.calcite.IndexWithSameNameCalciteTest",
+ "org.apache.ignite.internal.processors.cache.DdlTransactionCalciteSelfTest",
+ "org.apache.ignite.internal.processors.query.calcite.message.CalciteCommunicationMessageSerializationTest"
+ );
+
/**
* Check whether class is a test class or a suite.
*
@@ -121,11 +128,13 @@ private void processSuite(Description suite, Set suitedClasses,
* Exclusion of the rule is Parameterized.class, so classes are marked with it are test classes.
*/
private boolean isTestClass(Description desc) {
+ if (CALCITE_TESTS.contains(desc.getDisplayName()))
+ return false;
+
RunWith runWith = desc.getAnnotation(RunWith.class);
return runWith == null
|| runWith.value().equals(Parameterized.class)
- || !(Suite.class.isAssignableFrom(runWith.value())
- || "org.scalatest.Suites".equals(desc.getTestClass().getSuperclass().getName()));
+ || !(Suite.class.isAssignableFrom(runWith.value()));
}
}
diff --git a/parent/pom.xml b/parent/pom.xml
index b50c31f2c48cb..ae8a698ea5dde 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -968,12 +968,15 @@
**/*.cmd
**/*.ps1
**/*.json
+ **/apache-2.0.txt
**/.dockerignore
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Examples/ExpectedOutput/*.txt
packaging/**
src/test/sql/**
+
+ src/test/resources/junit-platform.properties
docs/_site/**
docs/assets/images/**