From 1eff7b6369db72e2712a0c8d73849175c38bde2d Mon Sep 17 00:00:00 2001 From: Matthias Otterbach Date: Wed, 26 Apr 2023 16:28:10 +0200 Subject: [PATCH] Print stack trace for timed-out tests (of timed-out thread) --- .../statement/TimeoutRunContextStatement.java | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/TimeoutRunContextStatement.java b/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/TimeoutRunContextStatement.java index 95c69378124..bd971877d0d 100644 --- a/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/TimeoutRunContextStatement.java +++ b/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/TimeoutRunContextStatement.java @@ -1,9 +1,9 @@ /* - * Copyright (c) 2010-2017 BSI Business Systems Integration AG. + * Copyright (c) 2010-2023 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html + * https://www.eclipse.org/legal/epl-v10.html * * Contributors: * BSI Business Systems Integration AG - initial API and implementation @@ -17,6 +17,7 @@ import org.eclipse.scout.rt.platform.job.Jobs; import org.eclipse.scout.rt.platform.transaction.TransactionScope; import org.eclipse.scout.rt.platform.util.Assertions; +import org.eclipse.scout.rt.platform.util.concurrent.IRunnable; import org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError; import org.eclipse.scout.rt.platform.util.concurrent.TimedOutError; import org.eclipse.scout.rt.testing.platform.runner.SafeStatementInvoker; @@ -49,18 +50,58 @@ public TimeoutRunContextStatement(final Statement next, final long timeoutMillis @Override public void evaluate() throws Throwable { final SafeStatementInvoker invoker = new SafeStatementInvoker(m_next); + class TimeoutFutureRunnable implements IRunnable { - final IFuture future = Jobs.schedule(invoker, Jobs.newInput() + protected Thread m_thread; + + @Override + public void run() throws Exception { + try { + m_thread = Thread.currentThread(); + invoker.run(); + } + finally { + m_thread = null; + } + } + + public StackTraceElement[] getStackTrace() { + Thread t = m_thread; + if (t != null) { + return t.getStackTrace(); + } + return new StackTraceElement[0]; + } + } + final TimeoutFutureRunnable runnable = new TimeoutFutureRunnable(); + + final IFuture future = Jobs.schedule(runnable, Jobs.newInput() .withRunContext(RunContext.CURRENT.get().copy().withTransactionScope(TransactionScope.REQUIRES_NEW)) // Run in new TX, because the same TX is not allowed to be used by multiple threads. .withName("Running test with support for JUnit timeout")); try { future.awaitDone(m_timeoutMillis, TimeUnit.MILLISECONDS); } catch (ThreadInterruptedError | TimedOutError e) { // NOSONAR + StackTraceElement[] stackTrace = runnable.getStackTrace(); future.cancel(true); + printStackTrace(stackTrace); throw new TestTimedOutException(m_timeoutMillis, TimeUnit.MILLISECONDS); // JUnit timeout exception } invoker.throwOnError(); } + + protected void printStackTrace(StackTraceElement[] stackTrace) { + StringBuilder trace = new StringBuilder(); + for (StackTraceElement traceElement : stackTrace) { + if (trace.length() != 0) { + trace.append('\n'); + } + trace.append("\tat "); + trace.append(traceElement); + } + if (trace.length() > 0) { + System.err.println("Test timed out at " + trace); + } + } }