Skip to content

Commit ef8f2c2

Browse files
committed
Extract AssertTimeoutPreemptively from AssertTimeout
1 parent 64cac6f commit ef8f2c2

File tree

5 files changed

+420
-351
lines changed

5 files changed

+420
-351
lines changed

junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java

-125
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,10 @@
1414
import static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;
1515

1616
import java.time.Duration;
17-
import java.util.concurrent.ExecutionException;
18-
import java.util.concurrent.ExecutorService;
19-
import java.util.concurrent.Executors;
20-
import java.util.concurrent.Future;
21-
import java.util.concurrent.ThreadFactory;
22-
import java.util.concurrent.TimeUnit;
23-
import java.util.concurrent.TimeoutException;
24-
import java.util.concurrent.atomic.AtomicInteger;
25-
import java.util.concurrent.atomic.AtomicReference;
2617
import java.util.function.Supplier;
2718

28-
import org.junit.jupiter.api.Assertions.TimeoutFailureFactory;
2919
import org.junit.jupiter.api.function.Executable;
3020
import org.junit.jupiter.api.function.ThrowingSupplier;
31-
import org.junit.platform.commons.JUnitException;
32-
import org.opentest4j.AssertionFailedError;
3321

3422
/**
3523
* {@code AssertTimeout} is a collection of utility methods that support asserting
@@ -95,117 +83,4 @@ private static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplie
9583
return result;
9684
}
9785

98-
static void assertTimeoutPreemptively(Duration timeout, Executable executable) {
99-
assertTimeoutPreemptively(timeout, executable, (String) null);
100-
}
101-
102-
static void assertTimeoutPreemptively(Duration timeout, Executable executable, String message) {
103-
assertTimeoutPreemptively(timeout, () -> {
104-
executable.execute();
105-
return null;
106-
}, message);
107-
}
108-
109-
static void assertTimeoutPreemptively(Duration timeout, Executable executable, Supplier<String> messageSupplier) {
110-
assertTimeoutPreemptively(timeout, () -> {
111-
executable.execute();
112-
return null;
113-
}, messageSupplier);
114-
}
115-
116-
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier) {
117-
return assertTimeoutPreemptively(timeout, supplier, null, AssertTimeout::createAssertionFailure);
118-
}
119-
120-
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, String message) {
121-
return assertTimeoutPreemptively(timeout, supplier, message == null ? null : () -> message,
122-
AssertTimeout::createAssertionFailure);
123-
}
124-
125-
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
126-
Supplier<String> messageSupplier) {
127-
return assertTimeoutPreemptively(timeout, supplier, messageSupplier, AssertTimeout::createAssertionFailure);
128-
}
129-
130-
static <T, E extends Throwable> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
131-
Supplier<String> messageSupplier, TimeoutFailureFactory<E> failureFactory) throws E {
132-
AtomicReference<Thread> threadReference = new AtomicReference<>();
133-
ExecutorService executorService = Executors.newSingleThreadExecutor(new TimeoutThreadFactory());
134-
135-
try {
136-
Future<T> future = submitTask(supplier, threadReference, executorService);
137-
return resolveFutureAndHandleException(future, timeout, messageSupplier, threadReference::get,
138-
failureFactory);
139-
}
140-
finally {
141-
executorService.shutdownNow();
142-
}
143-
}
144-
145-
private static <T> Future<T> submitTask(ThrowingSupplier<T> supplier, AtomicReference<Thread> threadReference,
146-
ExecutorService executorService) {
147-
return executorService.submit(() -> {
148-
try {
149-
threadReference.set(Thread.currentThread());
150-
return supplier.get();
151-
}
152-
catch (Throwable throwable) {
153-
throw throwAsUncheckedException(throwable);
154-
}
155-
});
156-
}
157-
158-
private static <T, E extends Throwable> T resolveFutureAndHandleException(Future<T> future, Duration timeout,
159-
Supplier<String> messageSupplier, Supplier<Thread> threadSupplier, TimeoutFailureFactory<E> failureFactory)
160-
throws E {
161-
try {
162-
return future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
163-
}
164-
catch (TimeoutException ex) {
165-
Thread thread = threadSupplier.get();
166-
ExecutionTimeoutException cause = null;
167-
if (thread != null) {
168-
cause = new ExecutionTimeoutException("Execution timed out in thread " + thread.getName());
169-
cause.setStackTrace(thread.getStackTrace());
170-
}
171-
throw failureFactory.createTimeoutFailure(timeout, messageSupplier, cause);
172-
}
173-
catch (ExecutionException ex) {
174-
throw throwAsUncheckedException(ex.getCause());
175-
}
176-
catch (Throwable ex) {
177-
throw throwAsUncheckedException(ex);
178-
}
179-
}
180-
181-
private static AssertionFailedError createAssertionFailure(Duration timeout, Supplier<String> messageSupplier,
182-
Throwable cause) {
183-
return assertionFailure() //
184-
.message(messageSupplier) //
185-
.reason("execution timed out after " + timeout.toMillis() + " ms") //
186-
.cause(cause) //
187-
.build();
188-
}
189-
190-
private static class ExecutionTimeoutException extends JUnitException {
191-
192-
private static final long serialVersionUID = 1L;
193-
194-
ExecutionTimeoutException(String message) {
195-
super(message);
196-
}
197-
}
198-
199-
/**
200-
* The thread factory used for preemptive timeout.
201-
* <p>
202-
* The factory creates threads with meaningful names, helpful for debugging purposes.
203-
*/
204-
private static class TimeoutThreadFactory implements ThreadFactory {
205-
private static final AtomicInteger threadNumber = new AtomicInteger(1);
206-
207-
public Thread newThread(Runnable r) {
208-
return new Thread(r, "junit-timeout-thread-" + threadNumber.getAndIncrement());
209-
}
210-
}
21186
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright 2015-2022 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.api;
12+
13+
import static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;
14+
import static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;
15+
16+
import java.time.Duration;
17+
import java.util.concurrent.ExecutionException;
18+
import java.util.concurrent.ExecutorService;
19+
import java.util.concurrent.Executors;
20+
import java.util.concurrent.Future;
21+
import java.util.concurrent.ThreadFactory;
22+
import java.util.concurrent.TimeUnit;
23+
import java.util.concurrent.TimeoutException;
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
import java.util.concurrent.atomic.AtomicReference;
26+
import java.util.function.Supplier;
27+
28+
import org.junit.jupiter.api.function.Executable;
29+
import org.junit.jupiter.api.function.ThrowingSupplier;
30+
import org.junit.platform.commons.JUnitException;
31+
import org.opentest4j.AssertionFailedError;
32+
33+
/**
34+
* {@code AssertTimeout} is a collection of utility methods that support asserting
35+
* the execution of the code under test did not take longer than the timeout duration
36+
* using a preemptive approach.
37+
*
38+
* @since 5.9.1
39+
*/
40+
class AssertTimeoutPreemptively {
41+
42+
static void assertTimeoutPreemptively(Duration timeout, Executable executable) {
43+
assertTimeoutPreemptively(timeout, executable, (String) null);
44+
}
45+
46+
static void assertTimeoutPreemptively(Duration timeout, Executable executable, String message) {
47+
assertTimeoutPreemptively(timeout, () -> {
48+
executable.execute();
49+
return null;
50+
}, message);
51+
}
52+
53+
static void assertTimeoutPreemptively(Duration timeout, Executable executable, Supplier<String> messageSupplier) {
54+
assertTimeoutPreemptively(timeout, () -> {
55+
executable.execute();
56+
return null;
57+
}, messageSupplier);
58+
}
59+
60+
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier) {
61+
return assertTimeoutPreemptively(timeout, supplier, null, AssertTimeoutPreemptively::createAssertionFailure);
62+
}
63+
64+
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, String message) {
65+
return assertTimeoutPreemptively(timeout, supplier, message == null ? null : () -> message,
66+
AssertTimeoutPreemptively::createAssertionFailure);
67+
}
68+
69+
static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
70+
Supplier<String> messageSupplier) {
71+
return assertTimeoutPreemptively(timeout, supplier, messageSupplier,
72+
AssertTimeoutPreemptively::createAssertionFailure);
73+
}
74+
75+
static <T, E extends Throwable> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
76+
Supplier<String> messageSupplier, Assertions.TimeoutFailureFactory<E> failureFactory) throws E {
77+
AtomicReference<Thread> threadReference = new AtomicReference<>();
78+
ExecutorService executorService = Executors.newSingleThreadExecutor(new TimeoutThreadFactory());
79+
80+
try {
81+
Future<T> future = submitTask(supplier, threadReference, executorService);
82+
return resolveFutureAndHandleException(future, timeout, messageSupplier, threadReference::get,
83+
failureFactory);
84+
}
85+
finally {
86+
executorService.shutdownNow();
87+
}
88+
}
89+
90+
private static <T> Future<T> submitTask(ThrowingSupplier<T> supplier, AtomicReference<Thread> threadReference,
91+
ExecutorService executorService) {
92+
return executorService.submit(() -> {
93+
try {
94+
threadReference.set(Thread.currentThread());
95+
return supplier.get();
96+
}
97+
catch (Throwable throwable) {
98+
throw throwAsUncheckedException(throwable);
99+
}
100+
});
101+
}
102+
103+
private static <T, E extends Throwable> T resolveFutureAndHandleException(Future<T> future, Duration timeout,
104+
Supplier<String> messageSupplier, Supplier<Thread> threadSupplier,
105+
Assertions.TimeoutFailureFactory<E> failureFactory) throws E {
106+
try {
107+
return future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
108+
}
109+
catch (TimeoutException ex) {
110+
Thread thread = threadSupplier.get();
111+
ExecutionTimeoutException cause = null;
112+
if (thread != null) {
113+
cause = new ExecutionTimeoutException("Execution timed out in thread " + thread.getName());
114+
cause.setStackTrace(thread.getStackTrace());
115+
}
116+
throw failureFactory.createTimeoutFailure(timeout, messageSupplier, cause);
117+
}
118+
catch (ExecutionException ex) {
119+
throw throwAsUncheckedException(ex.getCause());
120+
}
121+
catch (Throwable ex) {
122+
throw throwAsUncheckedException(ex);
123+
}
124+
}
125+
126+
private static AssertionFailedError createAssertionFailure(Duration timeout, Supplier<String> messageSupplier,
127+
Throwable cause) {
128+
return assertionFailure() //
129+
.message(messageSupplier) //
130+
.reason("execution timed out after " + timeout.toMillis() + " ms") //
131+
.cause(cause) //
132+
.build();
133+
}
134+
135+
private static class ExecutionTimeoutException extends JUnitException {
136+
137+
private static final long serialVersionUID = 1L;
138+
139+
ExecutionTimeoutException(String message) {
140+
super(message);
141+
}
142+
}
143+
144+
/**
145+
* The thread factory used for preemptive timeout.
146+
* <p>
147+
* The factory creates threads with meaningful names, helpful for debugging purposes.
148+
*/
149+
private static class TimeoutThreadFactory implements ThreadFactory {
150+
private static final AtomicInteger threadNumber = new AtomicInteger(1);
151+
152+
public Thread newThread(Runnable r) {
153+
return new Thread(r, "junit-timeout-thread-" + threadNumber.getAndIncrement());
154+
}
155+
}
156+
}

junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -3396,7 +3396,7 @@ public static <T> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier
33963396
* @see #assertTimeout(Duration, Executable)
33973397
*/
33983398
public static void assertTimeoutPreemptively(Duration timeout, Executable executable) {
3399-
AssertTimeout.assertTimeoutPreemptively(timeout, executable);
3399+
AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, executable);
34003400
}
34013401

34023402
/**
@@ -3419,7 +3419,7 @@ public static void assertTimeoutPreemptively(Duration timeout, Executable execut
34193419
* @see #assertTimeout(Duration, Executable, String)
34203420
*/
34213421
public static void assertTimeoutPreemptively(Duration timeout, Executable executable, String message) {
3422-
AssertTimeout.assertTimeoutPreemptively(timeout, executable, message);
3422+
AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, executable, message);
34233423
}
34243424

34253425
/**
@@ -3444,7 +3444,7 @@ public static void assertTimeoutPreemptively(Duration timeout, Executable execut
34443444
*/
34453445
public static void assertTimeoutPreemptively(Duration timeout, Executable executable,
34463446
Supplier<String> messageSupplier) {
3447-
AssertTimeout.assertTimeoutPreemptively(timeout, executable, messageSupplier);
3447+
AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, executable, messageSupplier);
34483448
}
34493449

34503450
// --- supplier - preemptively ---
@@ -3469,7 +3469,7 @@ public static void assertTimeoutPreemptively(Duration timeout, Executable execut
34693469
* @see #assertTimeout(Duration, Executable)
34703470
*/
34713471
public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier) {
3472-
return AssertTimeout.assertTimeoutPreemptively(timeout, supplier);
3472+
return AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier);
34733473
}
34743474

34753475
/**
@@ -3494,7 +3494,7 @@ public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier
34943494
* @see #assertTimeout(Duration, Executable, String)
34953495
*/
34963496
public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier, String message) {
3497-
return AssertTimeout.assertTimeoutPreemptively(timeout, supplier, message);
3497+
return AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier, message);
34983498
}
34993499

35003500
/**
@@ -3521,7 +3521,7 @@ public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier
35213521
*/
35223522
public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
35233523
Supplier<String> messageSupplier) {
3524-
return AssertTimeout.assertTimeoutPreemptively(timeout, supplier, messageSupplier);
3524+
return AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier, messageSupplier);
35253525
}
35263526

35273527
/**
@@ -3553,7 +3553,7 @@ public static <T> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier
35533553
@API(status = INTERNAL, since = "5.9.1")
35543554
public static <T, E extends Throwable> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,
35553555
Supplier<String> messageSupplier, TimeoutFailureFactory<E> failureFactory) throws E {
3556-
return AssertTimeout.assertTimeoutPreemptively(timeout, supplier, messageSupplier, failureFactory);
3556+
return AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier, messageSupplier, failureFactory);
35573557
}
35583558

35593559
// --- assertInstanceOf ----------------------------------------------------

0 commit comments

Comments
 (0)