diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java index 4493be05eb0..cf987f2b019 100644 --- a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java @@ -41,6 +41,7 @@ import org.apache.solr.util.LogLevelTestRule; import org.apache.solr.util.RevertDefaultThreadHandlerRule; import org.apache.solr.util.StartupLoggingUtils; +import org.apache.solr.util.ThreadDumpOnFailureRule; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; import org.junit.AfterClass; @@ -110,7 +111,8 @@ protected void afterAlways(List errors) { ObjectReleaseTracker.tryClose(); } } - }); + }) + .around(new ThreadDumpOnFailureRule()); /** * Sets the solr.configset.default.confdir system property to the value of {@link diff --git a/solr/test-framework/src/java/org/apache/solr/util/ThreadDumpOnFailureRule.java b/solr/test-framework/src/java/org/apache/solr/util/ThreadDumpOnFailureRule.java new file mode 100644 index 00000000000..ba0fc83a033 --- /dev/null +++ b/solr/test-framework/src/java/org/apache/solr/util/ThreadDumpOnFailureRule.java @@ -0,0 +1,63 @@ +/* + * 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.solr.util; + +import org.apache.solr.core.Diagnostics; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +/** + * JUnit rule that captures and logs a thread dump when a test fails with a timeout. This is useful + * for debugging flaky tests with timeout issues, as it shows what threads were doing at the time of + * failure. + * + *

The thread dump is only captured if the failure throwable or its message contains "Timeout" + * (case-insensitive). + * + *

Usage: + * + *

{@code
+ * @Rule
+ * public TestRule threadDumpRule = new ThreadDumpOnFailureRule();
+ * }
+ */ +public class ThreadDumpOnFailureRule extends TestWatcher { + + @Override + protected void failed(Throwable e, Description description) { + if (isTimeoutRelated(e)) { + Diagnostics.logThreadDumps( + "============ THREAD DUMP ON TIMEOUT FAILURE: " + + description.getDisplayName() + + " ============\nFailure: " + + e.getClass().getSimpleName() + + ": " + + e.getMessage()); + } + } + + private boolean isTimeoutRelated(Throwable e) { + if (e == null) { + return false; + } + if (e.toString().contains("Timeout")) { + return true; + } + // Check the cause recursively + return isTimeoutRelated(e.getCause()); + } +}