Skip to content

Commit 325342f

Browse files
authored
Merge pull request #6 from skodjob/wait-for
Add `Wait.until()` to the test-frame
2 parents d180583 + d00fe1d commit 325342f

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.skodjob.wait;
2+
3+
import java.io.PrintWriter;
4+
import java.io.StringWriter;
5+
import java.time.Duration;
6+
import java.util.function.BooleanSupplier;
7+
8+
public class Wait {
9+
10+
/**
11+
* For every poll (happening once each {@code pollIntervalMs}) checks if supplier {@code ready} is true.
12+
* If yes, the wait is closed. Otherwise, waits another {@code pollIntervalMs} and tries again.
13+
* Once the wait timeout (specified by {@code timeoutMs} is reached and supplier wasn't true until that time,
14+
* throws {@link WaitException}.
15+
*
16+
* @param description information about on what we are waiting
17+
* @param pollIntervalMs poll interval in milliseconds
18+
* @param timeoutMs timeout specified in milliseconds
19+
* @param ready {@link BooleanSupplier} containing code, which should be executed each poll, verifying readiness
20+
* of the particular thing
21+
*/
22+
public static void until(String description, long pollIntervalMs, long timeoutMs, BooleanSupplier ready) {
23+
until(description, pollIntervalMs, timeoutMs, ready, () -> {});
24+
}
25+
26+
/**
27+
* For every poll (happening once each {@code pollIntervalMs}) checks if supplier {@code ready} is true.
28+
* If yes, the wait is closed. Otherwise, waits another {@code pollIntervalMs} and tries again.
29+
* Once the wait timeout (specified by {@code timeoutMs} is reached and supplier wasn't true until that time,
30+
* runs the {@code onTimeout} (f.e. print of logs, showing the actual value that was checked inside {@code ready}),
31+
* and finally throws {@link WaitException}.
32+
*
33+
* @param description information about on what we are waiting
34+
* @param pollIntervalMs poll interval in milliseconds
35+
* @param timeoutMs timeout specified in milliseconds
36+
* @param ready {@link BooleanSupplier} containing code, which should be executed each poll, verifying readiness
37+
* of the particular thing
38+
* @param onTimeout {@link Runnable} executed once timeout is reached and before the {@link WaitException} is thrown.
39+
*/
40+
public static void until(String description, long pollIntervalMs, long timeoutMs, BooleanSupplier ready, Runnable onTimeout) {
41+
System.out.println("Waiting for " + description);
42+
long deadline = System.currentTimeMillis() + timeoutMs;
43+
44+
String exceptionMessage = null;
45+
String previousExceptionMessage = null;
46+
47+
// in case we are polling every 1s, we want to print exception after x tries, not on the first try
48+
// for minutes poll interval will 2 be enough
49+
int exceptionAppearanceCount = Duration.ofMillis(pollIntervalMs).toMinutes() > 0 ? 2 : Math.max((int) (timeoutMs / pollIntervalMs) / 4, 2);
50+
int exceptionCount = 0;
51+
int newExceptionAppearance = 0;
52+
53+
StringWriter stackTraceError = new StringWriter();
54+
55+
while (true) {
56+
boolean result;
57+
try {
58+
result = ready.getAsBoolean();
59+
} catch (Exception e) {
60+
exceptionMessage = e.getMessage();
61+
62+
if (++exceptionCount == exceptionAppearanceCount && exceptionMessage != null && exceptionMessage.equals(previousExceptionMessage)) {
63+
System.out.println("While waiting for " + description + " exception occurred: " + exceptionMessage);
64+
// log the stacktrace
65+
e.printStackTrace(new PrintWriter(stackTraceError));
66+
} else if (exceptionMessage != null && !exceptionMessage.equals(previousExceptionMessage) && ++newExceptionAppearance == 2) {
67+
previousExceptionMessage = exceptionMessage;
68+
}
69+
70+
result = false;
71+
}
72+
long timeLeft = deadline - System.currentTimeMillis();
73+
if (result) {
74+
return;
75+
}
76+
if (timeLeft <= 0) {
77+
if (exceptionCount > 1) {
78+
System.out.println("Exception waiting for " + description + ", " + exceptionMessage);
79+
80+
if (!stackTraceError.toString().isEmpty()) {
81+
// printing handled stacktrace
82+
System.out.println(stackTraceError);
83+
}
84+
}
85+
onTimeout.run();
86+
WaitException waitException = new WaitException("Timeout after " + timeoutMs + " ms waiting for " + description);
87+
waitException.printStackTrace();
88+
throw waitException;
89+
}
90+
long sleepTime = Math.min(pollIntervalMs, timeLeft);
91+
try {
92+
Thread.sleep(sleepTime);
93+
} catch (InterruptedException e) {
94+
return;
95+
}
96+
}
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.skodjob.wait;
2+
3+
public class WaitException extends RuntimeException {
4+
public WaitException(String message) {
5+
super(message);
6+
}
7+
}

0 commit comments

Comments
 (0)