|
14 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; |
15 | 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
16 | 16 |
|
| 17 | +import java.util.Locale; |
17 | 18 | import java.util.Random; |
18 | 19 | import java.util.concurrent.CountDownLatch; |
19 | 20 | import java.util.concurrent.TimeUnit; |
|
39 | 40 | import org.junit.jupiter.api.RepeatedTest; |
40 | 41 | import org.junit.jupiter.api.Test; |
41 | 42 | import org.junit.jupiter.api.Timeout; |
| 43 | +import org.junit.jupiter.api.extension.BeforeEachCallback; |
| 44 | +import org.junit.jupiter.api.extension.ExtensionContext; |
| 45 | +import org.junit.jupiter.api.extension.RegisterExtension; |
| 46 | +import org.junit.jupiter.api.extension.TestWatcher; |
42 | 47 | import org.slf4j.Logger; |
43 | 48 | import org.slf4j.LoggerFactory; |
44 | 49 |
|
|
50 | 55 | public abstract class SailConcurrencyTest { |
51 | 56 |
|
52 | 57 | private static final Logger logger = LoggerFactory.getLogger(SailConcurrencyTest.class); |
| 58 | + private static final String TIMED_OUT_KEY = "timedOutTest"; |
| 59 | + |
| 60 | + @RegisterExtension |
| 61 | + static final TimeoutClassFailureWatcher TIMEOUT_CLASS_FAILURE_WATCHER = new TimeoutClassFailureWatcher(); |
53 | 62 | /*-----------* |
54 | 63 | * Constants * |
55 | 64 | *-----------*/ |
@@ -90,6 +99,49 @@ public void tearDown() { |
90 | 99 | store.shutDown(); |
91 | 100 | } |
92 | 101 |
|
| 102 | + private static final class TimeoutClassFailureWatcher implements TestWatcher, BeforeEachCallback { |
| 103 | + |
| 104 | + @Override |
| 105 | + public void beforeEach(ExtensionContext context) { |
| 106 | + AtomicReference<String> timedOut = getTimedOutRef(context); |
| 107 | + String timedOutTest = timedOut.get(); |
| 108 | + if (timedOutTest != null) { |
| 109 | + Assertions.fail("Previous test timed out (" + timedOutTest + "); failing remaining tests in class"); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + @Override |
| 114 | + public void testFailed(ExtensionContext context, Throwable cause) { |
| 115 | + if (isTimeout(cause)) { |
| 116 | + getTimedOutRef(context).compareAndSet(null, context.getDisplayName()); |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + private static AtomicReference<String> getTimedOutRef(ExtensionContext context) { |
| 121 | + ExtensionContext.Namespace namespace = ExtensionContext.Namespace.create(SailConcurrencyTest.class, |
| 122 | + context.getRequiredTestClass()); |
| 123 | + return context.getStore(namespace) |
| 124 | + .getOrComputeIfAbsent(TIMED_OUT_KEY, key -> new AtomicReference<String>(), |
| 125 | + AtomicReference.class); |
| 126 | + } |
| 127 | + |
| 128 | + private static boolean isTimeout(Throwable cause) { |
| 129 | + Throwable current = cause; |
| 130 | + while (current != null) { |
| 131 | + if (current instanceof java.util.concurrent.TimeoutException |
| 132 | + || "org.junit.jupiter.api.TimeoutException".equals(current.getClass().getName())) { |
| 133 | + return true; |
| 134 | + } |
| 135 | + String message = current.getMessage(); |
| 136 | + if (message != null && message.toLowerCase(Locale.ROOT).contains("timed out")) { |
| 137 | + return true; |
| 138 | + } |
| 139 | + current = current.getCause(); |
| 140 | + } |
| 141 | + return false; |
| 142 | + } |
| 143 | + } |
| 144 | + |
93 | 145 | protected class UploadTransaction implements Runnable { |
94 | 146 |
|
95 | 147 | private final IRI context; |
|
0 commit comments