-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathConditionPoll.java
More file actions
105 lines (95 loc) · 4.26 KB
/
ConditionPoll.java
File metadata and controls
105 lines (95 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package net.dempsy.utils.test;
import java.util.function.Supplier;
import org.junit.jupiter.api.Assertions;
/**
* <p>
* Multi threaded tests are notoriously difficult to write. You should almost NEVER (though there is one exception to this) simply <em>sleep</em> for a certain amount of time and then expect a condition to be
* met. First of all it makes your tests slow, and it also makes them somewhat platform dependent. Ideally you should wait on the condition and be notified when the condition is met.
* </p>
* <p>
* Given that you're writing a test, the next best thing to this ideal is to <em>poll</em> for the condition. While this should be avoided in production code (where possible), it's not that important in unit
* tests. And it's usually a lot easier to do than setting up a condition variable or a CountDownLatch and triggering it appropriately.
* </p>
* <p>
* Therefore the class has several utility methods for helping to write multithreaded tests by allowing easy polling for a particular condition for a fixed amount of time and returns the final condition value.
* For example:
* </p>
*
* <pre>
* {@code
* import static net.dempsy.utils.test.ConditionPoll.poll;
* import static org.junit.Assert.assertTrue;
* ...
* public int numOfTimesSomethingHappens = 0;
* ....
* new Thread(() -> { doSomethingThatIncrementsNumOfTimesSomethingHappensAndExit() }).start();
*
* ...
* assertTrue(poll(() -> numOfTimesSomethingHappens == numTimesExpected));
* }
* </pre>
*/
public class ConditionPoll {
/**
* The default polling timeout.
*/
public static final long baseTimeoutMillis = 20000;
/**
* This is the interface that serves as the root for anonymous classes passed to the poll call.
*/
@FunctionalInterface
public static interface Condition<T> {
/**
* Return whether or not the condition we are polling for has been met yet.
*/
public boolean conditionMet(T o);
}
/**
* <p>
* Poll for a given condition for timeoutMillis milliseconds. If the condition hasn't been met by then return false. Otherwise, return true as soon as the condition is met.
* </p>
*
* <p>
* Anything passed to as the userObject will be passed on to the {@link Condition} and is for the implementor to use as they see fit.
* </p>
*/
public static <T> boolean poll(final long timeoutMillis, final T userObject, final Condition<T> condition) throws InterruptedException {
boolean conditionMet = condition.conditionMet(userObject);
for (final long endTime = System.currentTimeMillis() + timeoutMillis; endTime > System.currentTimeMillis() && !conditionMet;) {
Thread.sleep(10);
conditionMet = condition.conditionMet(userObject);
}
return conditionMet;
}
/**
* <p>
* Poll for a given condition for {@link ConditionPoll#baseTimeoutMillis} milliseconds. If the condition hasn't been met by then return false. Otherwise, return true as soon as the condition is met.
* </p>
*
* <p>
* Anything passed to as the userObject will be passed on to the {@link Condition} and is for the implementor to use as they see fit.
* </p>
*/
public static <T> boolean poll(final T userObject, final Condition<T> condition) throws InterruptedException {
return poll(baseTimeoutMillis, userObject, condition);
}
public static <T> boolean qpoll(final T userObject, final Condition<T> condition) {
try {
return poll(baseTimeoutMillis, userObject, condition);
} catch (final InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* <p>
* Poll for a given condition for {@link ConditionPoll#baseTimeoutMillis} milliseconds. If the condition hasn't been met by then return false. Otherwise, return true as soon as the condition is met.
* </p>
*/
@SuppressWarnings("unchecked")
public static boolean poll(@SuppressWarnings("rawtypes") final Condition condition) throws InterruptedException {
return poll(baseTimeoutMillis, null, condition);
}
public static void assertTrue(final Supplier<String> errMessage, final boolean value) {
Assertions.assertTrue(value, value ? "" : errMessage.get());
}
}