Skip to content

Commit 1cf76d3

Browse files
authored
Make LazyTest stricter to detect and avoid flakes
Closes #220; will reopen if the issue appears again.
1 parent a606ffe commit 1cf76d3

File tree

1 file changed

+34
-19
lines changed
  • java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/utils

1 file changed

+34
-19
lines changed

java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/utils/LazyTest.java

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,36 +36,41 @@
3636
import java.util.concurrent.Executors;
3737
import java.util.function.Function;
3838
import java.util.function.Supplier;
39+
import java.util.stream.IntStream;
3940
import java.util.stream.Stream;
4041
import org.junit.jupiter.api.DisplayName;
42+
import org.junit.jupiter.api.RepeatedTest;
4143
import org.junit.jupiter.api.Test;
4244
import org.junit.jupiter.api.Timeout;
4345
import org.junit.jupiter.api.parallel.Execution;
4446
import org.junit.jupiter.api.parallel.ExecutionMode;
47+
import org.junit.jupiter.api.parallel.Isolated;
4548
import org.junit.jupiter.params.ParameterizedTest;
4649
import org.junit.jupiter.params.provider.Arguments;
4750
import org.junit.jupiter.params.provider.MethodSource;
48-
import org.junit.jupiter.params.provider.ValueSource;
4951

5052
/**
5153
* {@link Lazy} tests.
5254
*
5355
* @author Ashley Scopes
5456
*/
5557
@DisplayName("Lazy tests")
56-
@Execution(ExecutionMode.CONCURRENT)
58+
@Execution(ExecutionMode.SAME_THREAD)
59+
@Isolated("Time-sensitive tests")
5760
@SuppressWarnings("unchecked")
5861
class LazyTest {
5962

60-
@DisplayName("Initializing with a null supplier throws a NullPointerException")
63+
@DisplayName("Initialising with a null supplier throws a NullPointerException")
6164
@Test
62-
void initializingWithNullSupplierThrowsNullPointerException() {
65+
@Timeout(15)
66+
void initialisingWithNullSupplierThrowsNullPointerException() {
6367
thenCode(() -> new Lazy<>(null))
6468
.isInstanceOf(NullPointerException.class);
6569
}
6670

6771
@DisplayName("access() returns the cached result")
68-
@Test
72+
@RepeatedTest(5)
73+
@Timeout(15)
6974
void accessReturnsTheCachedResult() {
7075
// Given
7176
var value = new Object();
@@ -88,9 +93,9 @@ void accessReturnsTheCachedResult() {
8893
}
8994

9095
@DisplayName("access() synchronizes correctly on initial accesses")
91-
@ValueSource(ints = {2, 4, 10, 100})
96+
@MethodSource("concurrentRepeatCases")
9297
@ParameterizedTest(name = "for {0} concurrent read(s)")
93-
@Timeout(30)
98+
@Timeout(15)
9499
void accessSynchronizesCorrectlyOnInitialAccesses(int concurrency) {
95100
// This is closeable in Java 19, but not before.
96101
@SuppressWarnings("resource")
@@ -142,9 +147,9 @@ void accessSynchronizesCorrectlyOnInitialAccesses(int concurrency) {
142147
}
143148

144149
@DisplayName("access() synchronizes correctly on subsequent accesses")
145-
@ValueSource(ints = {2, 4, 10, 100})
150+
@MethodSource("concurrentRepeatCases")
146151
@ParameterizedTest(name = "for {0} concurrent read(s)")
147-
@Timeout(30)
152+
@Timeout(15)
148153
void accessSynchronizesCorrectlyOnSubsequentAccesses(int concurrency) {
149154
// This is closeable in Java 19, but not before.
150155
@SuppressWarnings("resource")
@@ -189,8 +194,9 @@ void accessSynchronizesCorrectlyOnSubsequentAccesses(int concurrency) {
189194
}
190195
}
191196

192-
@DisplayName("access returns a new value when the lazy is destroyed")
193-
@Test
197+
@DisplayName("access() returns a new value when the lazy is destroyed")
198+
@RepeatedTest(5)
199+
@Timeout(15)
194200
void accessReturnsNewValueWhenLazyIsDestroyed() {
195201
// Given
196202
final var firstValue = new Object();
@@ -217,8 +223,9 @@ void accessReturnsNewValueWhenLazyIsDestroyed() {
217223
}
218224

219225
@DisplayName("toString() returns the expected values when initialized")
220-
@MethodSource("toStringInitializedCases")
226+
@MethodSource("toStringInitialisedCases")
221227
@ParameterizedTest(name = "with \"{0}\" expected to return \"{1}\"")
228+
@Timeout(15)
222229
void toStringReturnsExpectedValuesWhenInitialized(Object value, String expected) {
223230
// Given
224231
var supplier = (Supplier<Object>) mock(Supplier.class);
@@ -234,7 +241,8 @@ void toStringReturnsExpectedValuesWhenInitialized(Object value, String expected)
234241
}
235242

236243
@DisplayName("toString() returns the expected values when uninitialized")
237-
@Test
244+
@RepeatedTest(5)
245+
@Timeout(15)
238246
void toStringReturnsExpectedValuesWhenUninitialized() {
239247
// Given
240248
var supplier = (Supplier<Object>) mock(Supplier.class);
@@ -250,7 +258,8 @@ void toStringReturnsExpectedValuesWhenUninitialized() {
250258
}
251259

252260
@DisplayName("ifInitialized() calls the callable when initialized")
253-
@Test
261+
@RepeatedTest(5)
262+
@Timeout(15)
254263
void ifInitializedCallsTheCallableWhenInitialized() {
255264
// Given
256265
var initializer = (Supplier<Object>) mock(Supplier.class);
@@ -267,9 +276,9 @@ void ifInitializedCallsTheCallableWhenInitialized() {
267276
}
268277

269278
@DisplayName("ifInitialized() handles race conditions")
270-
@ValueSource(ints = {5, 10, 50, 100})
279+
@MethodSource("concurrentRepeatCases")
271280
@ParameterizedTest(name = "for concurrency = {0}")
272-
@Timeout(30)
281+
@Timeout(15)
273282
void ifInitializedHandlesRaceConditionsCorrectly(int concurrency) {
274283
// Given
275284
var initializer = (Supplier<Object>) mock(Supplier.class);
@@ -301,7 +310,8 @@ void ifInitializedHandlesRaceConditionsCorrectly(int concurrency) {
301310
}
302311

303312
@DisplayName("ifInitialized() propagates exceptions when initialized")
304-
@Test
313+
@RepeatedTest(5)
314+
@Timeout(15)
305315
void ifInitializedPropagatesExceptions() {
306316
// Given
307317
var initializer = (Supplier<Object>) mock(Supplier.class);
@@ -321,7 +331,8 @@ void ifInitializedPropagatesExceptions() {
321331
}
322332

323333
@DisplayName("ifInitialized() does not call the callable when not initialized")
324-
@Test
334+
@RepeatedTest(5)
335+
@Timeout(15)
325336
void ifInitializedDoesNotCallTheCallableWhenNotInitialized() {
326337
// Given
327338
var initializer = (Supplier<Object>) mock(Supplier.class);
@@ -338,13 +349,17 @@ void ifInitializedDoesNotCallTheCallableWhenNotInitialized() {
338349
verifyNoInteractions(callback);
339350
}
340351

341-
static Stream<Arguments> toStringInitializedCases() {
352+
static Stream<Arguments> toStringInitialisedCases() {
342353
return Stream.of(
343354
Arguments.of(new Something(), "Lazy{data=Something{}}"),
344355
Arguments.of("Hello, World!", "Lazy{data=\"Hello, World!\"}")
345356
);
346357
}
347358

359+
static IntStream concurrentRepeatCases() {
360+
return IntStream.of(2, 3, 5, 10, 20, 30);
361+
}
362+
348363
static class Something {
349364

350365
@Override

0 commit comments

Comments
 (0)