Skip to content

Commit 3fdd74c

Browse files
Test recursive updates and compute interactions
Co-authored-by: martinfrancois <f.martin@fastmail.com>
1 parent 39899e0 commit 3fdd74c

File tree

1 file changed

+82
-12
lines changed

1 file changed

+82
-12
lines changed

platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreTests.java

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static org.mockito.Mockito.verifyNoInteractions;
2626
import static org.mockito.Mockito.verifyNoMoreInteractions;
2727

28+
import java.io.Serial;
2829
import java.util.List;
2930
import java.util.concurrent.atomic.AtomicInteger;
3031
import java.util.function.Function;
@@ -82,6 +83,13 @@ void valueIsComputedIfAbsent() {
8283
assertEquals(value, store.get(namespace, key));
8384
}
8485

86+
@Test
87+
void valueIsComputedIfNull() {
88+
assertNull(store.put(namespace, key, null));
89+
assertEquals(value, store.computeIfAbsent(namespace, key, __ -> value));
90+
assertEquals(value, store.get(namespace, key));
91+
}
92+
8593
@SuppressWarnings("deprecation")
8694
@Test
8795
void valueIsNotComputedIfPresentLocally() {
@@ -316,22 +324,42 @@ void computeIfAbsentWithTypeSafetyAndPrimitiveValueType() {
316324
@SuppressWarnings("deprecation")
317325
@Test
318326
void getOrComputeIfAbsentWithExceptionThrowingCreatorFunction() {
319-
var e = assertThrows(RuntimeException.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> {
320-
throw new RuntimeException("boom");
327+
var e = assertThrows(ComputeException.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> {
328+
throw new ComputeException("boom");
321329
}));
322-
assertSame(e, assertThrows(RuntimeException.class, () -> store.get(namespace, key)));
323-
assertSame(e, assertThrows(RuntimeException.class, () -> store.remove(namespace, key)));
330+
assertSame(e, assertThrows(ComputeException.class, () -> store.get(namespace, key)));
331+
assertSame(e, assertThrows(ComputeException.class, () -> store.remove(namespace, key)));
324332
}
325333

326334
@Test
327335
void computeIfAbsentWithExceptionThrowingCreatorFunction() {
328-
assertThrows(RuntimeException.class, () -> store.computeIfAbsent(namespace, key, __ -> {
329-
throw new RuntimeException("boom");
336+
assertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, key, __ -> {
337+
throw new ComputeException("boom");
330338
}));
331339
assertNull(store.get(namespace, key));
332340
assertNull(store.remove(namespace, key));
333341
}
334342

343+
@SuppressWarnings("deprecation")
344+
@Test
345+
void getOrComputeIfAbsentDoesNotSeeComputeIfAbsentWithExceptionThrowingCreatorFunction() {
346+
assertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, key, __ -> {
347+
throw new ComputeException("boom");
348+
}));
349+
assertNull(store.get(namespace, key));
350+
assertEquals(value, store.getOrComputeIfAbsent(namespace, key, __ -> value));
351+
}
352+
353+
@SuppressWarnings("deprecation")
354+
@Test
355+
void computeIfAbsentSeesGetOrComputeIfAbsentWithExceptionThrowingCreatorFunction() {
356+
assertThrows(ComputeException.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> {
357+
throw new ComputeException("boom");
358+
}));
359+
assertThrows(ComputeException.class, () -> store.get(namespace, key));
360+
assertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, key, __ -> value));
361+
}
362+
335363
@Test
336364
void removeWithTypeSafetyAndInvalidRequiredTypeThrowsException() {
337365
Integer key = 42;
@@ -416,6 +444,35 @@ void simulateRaceConditionInComputeIfAbsent() throws Exception {
416444
assertEquals(1, counter.get());
417445
assertThat(values).hasSize(threads).containsOnly(1);
418446
}
447+
448+
@SuppressWarnings("deprecation")
449+
@Test
450+
void updateRecursivelyGetOrComputeIfAbsent() {
451+
try (var localStore = new NamespacedHierarchicalStore<>(null)) {
452+
var value = localStore.getOrComputeIfAbsent(namespace, new CollidingKey("a"), //
453+
a -> requireNonNull(localStore.getOrComputeIfAbsent(namespace, new CollidingKey("b"), //
454+
b -> "enigma")));
455+
assertEquals("enigma", value);
456+
}
457+
}
458+
459+
@Test
460+
void updateRecursivelyComputeIfAbsent() {
461+
try (var localStore = new NamespacedHierarchicalStore<>(null)) {
462+
var value = localStore.computeIfAbsent(namespace, new CollidingKey("a"), //
463+
a -> localStore.computeIfAbsent(namespace, new CollidingKey("b"), //
464+
b -> "enigma"));
465+
assertEquals("enigma", value);
466+
}
467+
}
468+
469+
private record CollidingKey(String value) {
470+
471+
@Override
472+
public int hashCode() {
473+
return 42;
474+
}
475+
}
419476
}
420477

421478
@Nested
@@ -522,19 +579,19 @@ void doesNotCallCloseActionForNullValues() {
522579
@Test
523580
void doesNotCallCloseActionForValuesThatThrowExceptionsDuringCleanup() throws Throwable {
524581
store.put(namespace, "key1", "value1");
525-
assertThrows(RuntimeException.class, () -> store.computeIfAbsent(namespace, "key2", __ -> {
526-
throw new RuntimeException("boom");
582+
assertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, "key2", __ -> {
583+
throw new ComputeException("boom");
527584
}));
528-
assertThrows(RuntimeException.class, () -> store.getOrComputeIfAbsent(namespace, "key2", __ -> {
529-
throw new RuntimeException("boom");
585+
assertThrows(ComputeException.class, () -> store.getOrComputeIfAbsent(namespace, "key3", __ -> {
586+
throw new ComputeException("boom");
530587
}));
531-
store.put(namespace, "key3", "value3");
588+
store.put(namespace, "key4", "value4");
532589

533590
store.close();
534591
assertClosed();
535592

536593
var inOrder = inOrder(closeAction);
537-
inOrder.verify(closeAction).close(namespace, "key3", "value3");
594+
inOrder.verify(closeAction).close(namespace, "key4", "value4");
538595
inOrder.verify(closeAction).close(namespace, "key1", "value1");
539596
inOrder.verifyNoMoreInteractions();
540597
}
@@ -672,4 +729,17 @@ public String toString() {
672729
}
673730
};
674731
}
732+
733+
/**
734+
* To avoid confusion with other Runtime exceptions that can be thrown.
735+
*/
736+
private static final class ComputeException extends RuntimeException {
737+
738+
@Serial
739+
private static final long serialVersionUID = 1L;
740+
741+
ComputeException(String msg) {
742+
super(msg);
743+
}
744+
}
675745
}

0 commit comments

Comments
 (0)