Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,41 @@
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.api.parallel.Isolated;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

/**
* {@link Lazy} tests.
*
* @author Ashley Scopes
*/
@DisplayName("Lazy tests")
@Execution(ExecutionMode.CONCURRENT)
@Execution(ExecutionMode.SAME_THREAD)
@Isolated("Time-sensitive tests")
@SuppressWarnings("unchecked")
class LazyTest {

@DisplayName("Initializing with a null supplier throws a NullPointerException")
@DisplayName("Initialising with a null supplier throws a NullPointerException")
@Test
void initializingWithNullSupplierThrowsNullPointerException() {
@Timeout(15)
void initialisingWithNullSupplierThrowsNullPointerException() {
thenCode(() -> new Lazy<>(null))
.isInstanceOf(NullPointerException.class);
}

@DisplayName("access() returns the cached result")
@Test
@RepeatedTest(5)
@Timeout(15)
void accessReturnsTheCachedResult() {
// Given
var value = new Object();
Expand All @@ -88,9 +93,9 @@ void accessReturnsTheCachedResult() {
}

@DisplayName("access() synchronizes correctly on initial accesses")
@ValueSource(ints = {2, 4, 10, 100})
@MethodSource("concurrentRepeatCases")
@ParameterizedTest(name = "for {0} concurrent read(s)")
@Timeout(30)
@Timeout(15)
void accessSynchronizesCorrectlyOnInitialAccesses(int concurrency) {
// This is closeable in Java 19, but not before.
@SuppressWarnings("resource")
Expand Down Expand Up @@ -142,9 +147,9 @@ void accessSynchronizesCorrectlyOnInitialAccesses(int concurrency) {
}

@DisplayName("access() synchronizes correctly on subsequent accesses")
@ValueSource(ints = {2, 4, 10, 100})
@MethodSource("concurrentRepeatCases")
@ParameterizedTest(name = "for {0} concurrent read(s)")
@Timeout(30)
@Timeout(15)
void accessSynchronizesCorrectlyOnSubsequentAccesses(int concurrency) {
// This is closeable in Java 19, but not before.
@SuppressWarnings("resource")
Expand Down Expand Up @@ -189,8 +194,9 @@ void accessSynchronizesCorrectlyOnSubsequentAccesses(int concurrency) {
}
}

@DisplayName("access returns a new value when the lazy is destroyed")
@Test
@DisplayName("access() returns a new value when the lazy is destroyed")
@RepeatedTest(5)
@Timeout(15)
void accessReturnsNewValueWhenLazyIsDestroyed() {
// Given
final var firstValue = new Object();
Expand All @@ -217,8 +223,9 @@ void accessReturnsNewValueWhenLazyIsDestroyed() {
}

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

@DisplayName("toString() returns the expected values when uninitialized")
@Test
@RepeatedTest(5)
@Timeout(15)
void toStringReturnsExpectedValuesWhenUninitialized() {
// Given
var supplier = (Supplier<Object>) mock(Supplier.class);
Expand All @@ -250,7 +258,8 @@ void toStringReturnsExpectedValuesWhenUninitialized() {
}

@DisplayName("ifInitialized() calls the callable when initialized")
@Test
@RepeatedTest(5)
@Timeout(15)
void ifInitializedCallsTheCallableWhenInitialized() {
// Given
var initializer = (Supplier<Object>) mock(Supplier.class);
Expand All @@ -267,9 +276,9 @@ void ifInitializedCallsTheCallableWhenInitialized() {
}

@DisplayName("ifInitialized() handles race conditions")
@ValueSource(ints = {5, 10, 50, 100})
@MethodSource("concurrentRepeatCases")
@ParameterizedTest(name = "for concurrency = {0}")
@Timeout(30)
@Timeout(15)
void ifInitializedHandlesRaceConditionsCorrectly(int concurrency) {
// Given
var initializer = (Supplier<Object>) mock(Supplier.class);
Expand Down Expand Up @@ -301,7 +310,8 @@ void ifInitializedHandlesRaceConditionsCorrectly(int concurrency) {
}

@DisplayName("ifInitialized() propagates exceptions when initialized")
@Test
@RepeatedTest(5)
@Timeout(15)
void ifInitializedPropagatesExceptions() {
// Given
var initializer = (Supplier<Object>) mock(Supplier.class);
Expand All @@ -321,7 +331,8 @@ void ifInitializedPropagatesExceptions() {
}

@DisplayName("ifInitialized() does not call the callable when not initialized")
@Test
@RepeatedTest(5)
@Timeout(15)
void ifInitializedDoesNotCallTheCallableWhenNotInitialized() {
// Given
var initializer = (Supplier<Object>) mock(Supplier.class);
Expand All @@ -338,13 +349,17 @@ void ifInitializedDoesNotCallTheCallableWhenNotInitialized() {
verifyNoInteractions(callback);
}

static Stream<Arguments> toStringInitializedCases() {
static Stream<Arguments> toStringInitialisedCases() {
return Stream.of(
Arguments.of(new Something(), "Lazy{data=Something{}}"),
Arguments.of("Hello, World!", "Lazy{data=\"Hello, World!\"}")
);
}

static IntStream concurrentRepeatCases() {
return IntStream.of(2, 3, 5, 10, 20, 30);
}

static class Something {

@Override
Expand Down