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

ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new eb8049f977 IGNITE-18638 Make tests print thread dump when timing out 
(#1585)
eb8049f977 is described below

commit eb8049f977a0bc454de70cd7bd4c7eedf89349d5
Author: Roman Puchkovskiy <[email protected]>
AuthorDate: Fri Jan 27 11:02:26 2023 +0400

    IGNITE-18638 Make tests print thread dump when timing out (#1585)
---
 buildscripts/java-integration-test.gradle          |  7 ++
 buildscripts/java-junit5.gradle                    |  7 ++
 modules/core/build.gradle                          |  3 +
 .../internal/util/IgniteStripedLockSelfTest.java   |  3 +
 .../testframework/junit/DumpThreadsOnTimeout.java  | 99 ++++++++++++++++++++++
 5 files changed, 119 insertions(+)

diff --git a/buildscripts/java-integration-test.gradle 
b/buildscripts/java-integration-test.gradle
index 5ce10afc4f..47fe614955 100644
--- a/buildscripts/java-integration-test.gradle
+++ b/buildscripts/java-integration-test.gradle
@@ -47,6 +47,13 @@ testing {
                     testTask.configure {
                         shouldRunAfter(test)
                         maxHeapSize = '16g'
+
+                        // Define default test timeouts to avoid everhanging 
tests.
+                        systemProperty 
'junit.jupiter.execution.timeout.testable.method.default', '60s'
+                        systemProperty 
'junit.jupiter.execution.timeout.lifecycle.method.default', '60s'
+
+                        // Enable auto-detection of JUnit 5 extensions to 
automatically apply DumpThreadsOnTimeout extension.
+                        systemProperty 
'junit.jupiter.extensions.autodetection.enabled', true
                     }
                 }
             }
diff --git a/buildscripts/java-junit5.gradle b/buildscripts/java-junit5.gradle
index 97de37d5f8..718c5db78b 100644
--- a/buildscripts/java-junit5.gradle
+++ b/buildscripts/java-junit5.gradle
@@ -20,6 +20,13 @@ test {
     testLogging {
         events "passed", "skipped", "failed", "standard_error"
     }
+
+    // Define default test timeouts to avoid everhanging tests.
+    systemProperty 'junit.jupiter.execution.timeout.testable.method.default', 
'60s'
+    systemProperty 'junit.jupiter.execution.timeout.lifecycle.method.default', 
'60s'
+
+    // Enable auto-detection of JUnit 5 extensions to automatically apply 
DumpThreadsOnTimeout extension.
+    systemProperty 'junit.jupiter.extensions.autodetection.enabled', true
 }
 
 dependencies {
diff --git a/modules/core/build.gradle b/modules/core/build.gradle
index a0b667955d..36cbae098e 100644
--- a/modules/core/build.gradle
+++ b/modules/core/build.gradle
@@ -35,6 +35,8 @@ dependencies {
     testImplementation libs.mockito.inline
     testImplementation libs.mockito.junit
 
+    testFixturesAnnotationProcessor libs.auto.service
+
     testFixturesImplementation libs.hamcrest.core
     testFixturesImplementation libs.mockito.core
     testFixturesImplementation libs.junit.testkit
@@ -43,6 +45,7 @@ dependencies {
     testFixturesImplementation libs.junit5.impl
     testFixturesImplementation libs.junit5.params
     testFixturesImplementation libs.mockito.core
+    testFixturesImplementation libs.auto.service.annotations
 }
 
 test {
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteStripedLockSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteStripedLockSelfTest.java
index bfff3757d1..f2c8136691 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteStripedLockSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteStripedLockSelfTest.java
@@ -24,13 +24,16 @@ import static org.junit.jupiter.api.Assertions.fail;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 
 /**
  * Test for {@link IgniteStripedLock}.
  */
+@Timeout(value = 10, unit = TimeUnit.MINUTES)
 public class IgniteStripedLockSelfTest {
     private static final int STRIPE_COUNT = 16;
 
diff --git 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/junit/DumpThreadsOnTimeout.java
 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/junit/DumpThreadsOnTimeout.java
new file mode 100644
index 0000000000..f44da76019
--- /dev/null
+++ 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/junit/DumpThreadsOnTimeout.java
@@ -0,0 +1,99 @@
+/*
+ * 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.internal.testframework.junit;
+
+import com.google.auto.service.AutoService;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.concurrent.TimeoutException;
+import org.junit.jupiter.api.extension.Extension;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import 
org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
+import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
+
+/**
+ * This extension prints a thread dump (to the standard out) if a testable 
method (for instance, a method annotated with
+ * {@link org.junit.jupiter.api.Test} or a lifecycle method (like {@link 
org.junit.jupiter.api.BeforeEach} method) times out.
+ *
+ * <p>This extension is designed to be
+ * <a 
href="https://junit.org/junit5/docs/current/user-guide/#extensions-registration-automatic";>automatically
 registered</a>
+ * via META-INF/services/org.junit.jupiter.api.extension.Extension.
+ * For this to work, system property {@code 
junit.jupiter.extensions.autodetection.enabled} must be set to {@code true}.
+ * If the property is set (currently, this is done via Gradle build scripts), 
it is enough to add this module as a dependency
+ * to make tests automatically register this extension, like this:
+ *
+ * <pre>
+ * integrationTestImplementation(testFixtures(project(':ignite-core')))
+ * </pre>
+ */
+@AutoService(Extension.class)
+public class DumpThreadsOnTimeout implements TestExecutionExceptionHandler, 
LifecycleMethodExecutionExceptionHandler {
+    @Override
+    public void handleTestExecutionException(ExtensionContext context, 
Throwable throwable) throws Throwable {
+        handleThrowable(throwable, "Test method");
+    }
+
+    private static void handleThrowable(Throwable throwable, String category) 
throws Throwable {
+        if (isJunitMethodTimeout(throwable)) {
+            // We take throwable.getMessage() because TimeoutExtension puts a 
description of 'what has timed out' to it.
+            System.out.println(category + " " + throwable.getMessage() + ", 
dumping threads");
+
+            dumpThreadsToStdout();
+        }
+
+        // Rethrow the thing to make sure the framework sees the failure.
+        throw throwable;
+    }
+
+    private static boolean isJunitMethodTimeout(Throwable throwable) {
+        // This is a pretty hacky and not too reliable way to determine 
whether this is the timeout event we are
+        // interested in (meaning, a timed-out testable/lifecycle method 
execution), but it looks like this is the
+        // best we can do.
+        return throwable instanceof TimeoutException && 
throwable.getMessage().contains(" timed out after ");
+    }
+
+    private static void dumpThreadsToStdout() {
+        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
+        ThreadInfo[] infos = bean.dumpAllThreads(true, true);
+
+        for (ThreadInfo info : infos) {
+            System.out.println(info);
+        }
+    }
+
+    @Override
+    public void handleBeforeAllMethodExecutionException(ExtensionContext 
context, Throwable throwable) throws Throwable {
+        handleThrowable(throwable, "Before all method");
+    }
+
+    @Override
+    public void handleBeforeEachMethodExecutionException(ExtensionContext 
context, Throwable throwable) throws Throwable {
+        handleThrowable(throwable, "Before each method");
+    }
+
+    @Override
+    public void handleAfterEachMethodExecutionException(ExtensionContext 
context, Throwable throwable) throws Throwable {
+        handleThrowable(throwable, "After each method");
+    }
+
+    @Override
+    public void handleAfterAllMethodExecutionException(ExtensionContext 
context, Throwable throwable) throws Throwable {
+        handleThrowable(throwable, "After all method");
+    }
+}

Reply via email to