java-topology/test/jdk/java/lang/LazyConstant/LazyMapTest.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

574 lines
21 KiB
Java

/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary Basic tests for lazy map methods
* @enablePreview
* @modules java.base/java.util:+open
* @run junit LazyMapTest
*/
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.mapping;
import static org.junit.jupiter.api.Assertions.*;
final class LazyMapTest {
enum Value {
// Zero is here so that we have enums with ordinals before the first one
// actually used in input sets (i.e. ZERO is not in the input set)
ZERO(0),
ILLEGAL_BEFORE(-1),
// Valid values
THIRTEEN(13) {
@Override
public String toString() {
// getEnumConstants will be `null` for this enum as it is overridden
return super.toString()+" (Overridden)";
}
},
ILLEGAL_BETWEEN(-2),
FORTY_TWO(42),
// Illegal values (not in the input set)
ILLEGAL_AFTER(-3);
final int intValue;
Value(int intValue) {
this.intValue = intValue;
}
int asInt() {
return intValue;
}
}
private static final Function<Value, Integer> MAPPER = Value::asInt;
private static final Value KEY = Value.FORTY_TWO;
private static final Integer VALUE = MAPPER.apply(KEY);
@ParameterizedTest
@MethodSource("allSets")
void factoryInvariants(Set<Value> set) {
assertThrows(NullPointerException.class, () -> Map.ofLazy(set, null), set.getClass().getSimpleName());
assertThrows(NullPointerException.class, () -> Map.ofLazy(null, MAPPER));
Set<Value> setWithNull = new HashSet<>();
setWithNull.add(KEY);
setWithNull.add(null);
assertThrows(NullPointerException.class, () -> Map.ofLazy(setWithNull, MAPPER));
}
@ParameterizedTest
@MethodSource("emptySets")
void empty(Set<Value> set) {
var lazy = newLazyMap(set);
assertTrue(lazy.isEmpty());
assertEquals("{}", lazy.toString());
assertThrows(NullPointerException.class, () -> lazy.get(null));
assertNotEquals(null, lazy);
}
@ParameterizedTest
@MethodSource("allSets")
void size(Set<Value> set) {
assertEquals(newRegularMap(set).size(), newLazyMap(set).size());
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void get(Set<Value> set) {
LazyConstantTestUtil.CountingFunction<Value, Integer> cf = new LazyConstantTestUtil.CountingFunction<>(MAPPER);
var lazy = Map.ofLazy(set, cf);
int cnt = 1;
for (Value v : set) {
assertEquals(MAPPER.apply(v), lazy.get(v));
assertEquals(cnt, cf.cnt());
assertEquals(MAPPER.apply(v), lazy.get(v));
assertEquals(cnt++, cf.cnt());
}
assertNull(lazy.get(Value.ILLEGAL_BETWEEN));
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void exception(Set<Value> set) {
LazyConstantTestUtil.CountingFunction<Value, Integer> cif = new LazyConstantTestUtil.CountingFunction<>(_ -> {
throw new UnsupportedOperationException();
});
var lazy = Map.ofLazy(set, cif);
assertThrows(UnsupportedOperationException.class, () -> lazy.get(KEY));
assertEquals(1, cif.cnt());
assertThrows(UnsupportedOperationException.class, () -> lazy.get(KEY));
assertEquals(2, cif.cnt());
assertThrows(UnsupportedOperationException.class, lazy::toString);
assertEquals(3, cif.cnt());
}
@ParameterizedTest
@MethodSource("allSets")
void containsKey(Set<Value> set) {
var lazy = newLazyMap(set);
for (Value v : set) {
assertTrue(lazy.containsKey(v));
}
assertFalse(lazy.containsKey(Value.ILLEGAL_BETWEEN));
}
@ParameterizedTest
@MethodSource("allSets")
void containsValue(Set<Value> set) {
var lazy = newLazyMap(set);
for (Value v : set) {
assertTrue(lazy.containsValue(MAPPER.apply(v)));
}
assertFalse(lazy.containsValue(MAPPER.apply(Value.ILLEGAL_BETWEEN)));
}
@ParameterizedTest
@MethodSource("allSets")
void forEach(Set<Value> set) {
var lazy = newLazyMap(set);
var ref = newRegularMap(set);
Set<Map.Entry<Value, Integer>> expected = ref.entrySet();
Set<Map.Entry<Value, Integer>> actual = new HashSet<>();
lazy.forEach((k, v) -> actual.add(new AbstractMap.SimpleImmutableEntry<>(k , v)));
assertEquals(expected, actual);
}
@ParameterizedTest
@MethodSource("emptySets")
void toStringTestEmpty(Set<Value> set) {
var lazy = newLazyMap(set);
assertEquals("{}", lazy.toString());
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void toStringTest(Set<Value> set) {
var lazy = newLazyMap(set);
var toString = lazy.toString();
assertTrue(toString.startsWith("{"));
assertTrue(toString.endsWith("}"));
// Key order is unspecified
for (Value key : set) {
toString = lazy.toString();
assertTrue(toString.contains(key + "=" + MAPPER.apply(key)), toString);
}
// One between the values
assertEquals(set.size() - 1, toString.chars().filter(ch -> ch == ',').count());
}
@ParameterizedTest
@MethodSource("allSets")
void hashCodeTest(Set<Value> set) {
var lazy = newLazyMap(set);
var regular = newRegularMap(set);
assertEquals(regular.hashCode(), lazy.hashCode());
}
@ParameterizedTest
@MethodSource("allSets")
void equality(Set<Value> set) {
var lazy = newLazyMap(set);
var regular = newRegularMap(set);
assertEquals(regular, lazy);
assertEquals(lazy, regular);
assertNotEquals("A", lazy);
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void circular(Set<Value> set) {
final AtomicReference<Map<?, ?>> ref = new AtomicReference<>();
Map<Value, Map<?, ?>> lazy = Map.ofLazy(set, _ -> ref.get());
ref.set(lazy);
lazy.get(KEY);
var toString = lazy.toString();
assertTrue(toString.contains("FORTY_TWO=(this Map)"), toString);
assertDoesNotThrow((() -> lazy.equals(lazy)));
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void recursiveCall(Set<Value> set) {
final AtomicReference<Map<Value, ?>> ref = new AtomicReference<>();
@SuppressWarnings("unchecked")
Map<Value, Map<Value, Object>> lazy = Map.ofLazy(set, k -> (Map<Value, Object>) ref.get().get(k));
ref.set(lazy);
var x = assertThrows(IllegalStateException.class, () -> lazy.get(KEY));
assertEquals("Recursive initialization of a lazy collection is illegal", x.getMessage());
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void entrySet(Set<Value> set) {
var lazy = newLazyMap(set).entrySet();
var regular = newRegularMap(set).entrySet();
assertTrue(regular.equals(lazy));
assertTrue(lazy.equals(regular));
assertTrue(regular.equals(lazy));
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void entrySetToString(Set<Value> set) {
var lazy = newLazyMap(set);
var lazyEntrySet = lazy.entrySet();
var toString = lazyEntrySet.toString();
for (var key : set) {
assertTrue(toString.contains(key + "=" + MAPPER.apply(key)));
}
assertTrue(toString.startsWith("["));
assertTrue(toString.endsWith("]"));
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void values(Set<Value> set) {
var lazy = newLazyMap(set);
var lazyValues = lazy.values();
// Look at one of the elements
var val = lazyValues.stream().iterator().next();
assertEquals(lazy.size() - 1, functionCounter(lazy));
// Mod ops
assertThrows(UnsupportedOperationException.class, () -> lazyValues.remove(val));
assertThrows(UnsupportedOperationException.class, () -> lazyValues.add(val));
assertThrows(UnsupportedOperationException.class, lazyValues::clear);
assertThrows(UnsupportedOperationException.class, () -> lazyValues.addAll(Set.of(VALUE)));
assertThrows(UnsupportedOperationException.class, () -> lazyValues.removeIf(i -> true));
assertThrows(UnsupportedOperationException.class, () -> lazyValues.retainAll(Set.of(VALUE)));
}
@ParameterizedTest
@MethodSource("allSets")
void valuesToString(Set<Value> set) {
var lazy = newLazyMap(set);
var lazyValues = lazy.values();
var toString = lazyValues.toString();
// Key order is unspecified
for (Value key : set) {
assertTrue(toString.contains(MAPPER.apply(key).toString()), toString);
}
assertTrue(toString.startsWith("["), toString);
assertTrue(toString.endsWith("]"), toString);
}
@ParameterizedTest
@MethodSource("allSets")
void iteratorNext(Set<Value> set) {
Set<Value> encountered = new HashSet<>();
var iterator = newLazyMap(set).entrySet().iterator();
while (iterator.hasNext()) {
var entry = iterator.next();
assertEquals(MAPPER.apply(entry.getKey()), entry.getValue());
encountered.add(entry.getKey());
}
assertEquals(set, encountered);
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void iteratorForEachRemaining(Set<Value> set) {
Set<Value> encountered = new HashSet<>();
var iterator = newLazyMap(set).entrySet().iterator();
var entry = iterator.next();
assertEquals(MAPPER.apply(entry.getKey()), entry.getValue());
encountered.add(entry.getKey());
iterator.forEachRemaining(e -> {
assertEquals(MAPPER.apply(e.getKey()), e.getValue());
encountered.add(e.getKey());
});
assertEquals(set, encountered);
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void lazyEntry(Set<Value> set) {
var lazy = newLazyMap(set);
var entry = lazy.entrySet().stream()
.filter(e -> e.getKey().equals(KEY))
.findAny()
.orElseThrow();
assertEquals(lazy.size(), functionCounter(lazy));
var otherDifferent = Map.entry(Value.ZERO, -1);
assertNotEquals(entry, otherDifferent);
assertEquals(lazy.size(), functionCounter(lazy));
var otherEqual = Map.entry(entry.getKey(), entry.getValue());
assertEquals(entry, otherEqual);
assertEquals(lazy.size() - 1, functionCounter(lazy));
assertEquals(entry.hashCode(), otherEqual.hashCode());
}
@ParameterizedTest
@MethodSource("nonEmptySets")
void lazyForEachEntry(Set<Value> set) {
var lazy = newLazyMap(set);
// Only touch the key.
lazy.entrySet().iterator().forEachRemaining(Map.Entry::getKey);
assertEquals(lazy.size(), functionCounter(lazy)); // No evaluation
// Only touch the value.
lazy.entrySet().iterator().forEachRemaining(Map.Entry::getValue);
assertEquals(0, functionCounter(lazy));
}
// Immutability
@ParameterizedTest
@MethodSource("unsupportedOperations")
void unsupported(Operation operation) {
assertThrowsForOperation(UnsupportedOperationException.class, operation);
}
// Method parameter invariant checking
@ParameterizedTest
@MethodSource("nullAverseOperations")
void nullAverse(Operation operation) {
assertThrowsForOperation(NullPointerException.class, operation);
}
static <T extends Throwable> void assertThrowsForOperation(Class<T> expectedType, Operation operation) {
for (Set<Value> set : allSets().toList()) {
var lazy = newLazyMap(set);
assertThrows(expectedType, () -> operation.accept(lazy), set.getClass().getSimpleName() + " " + operation);
}
}
// Implementing interfaces
@ParameterizedTest
@MethodSource("allSets")
void serializable(Set<Value> set) {
var lazy = newLazyMap(set);
assertFalse(lazy instanceof Serializable);
assertFalse(lazy.entrySet() instanceof Serializable);
assertFalse(lazy.values() instanceof Serializable);
}
@Test
void nullResult() {
var lazy = Map.ofLazy(Set.of(0), _ -> null);
assertThrows(NullPointerException.class, () -> lazy.getOrDefault(0, 1));;
assertTrue(lazy.containsKey(0));
}
@ParameterizedTest
@MethodSource("allSets")
void functionHolder(Set<Value> set) {
LazyConstantTestUtil.CountingFunction<Value, Integer> cif = new LazyConstantTestUtil.CountingFunction<>(MAPPER);
Map<Value, Integer> lazy = Map.ofLazy(set, cif);
Object holder = LazyConstantTestUtil.functionHolder(lazy);
int i = 0;
for (Value key : set) {
assertEquals(set.size() - i, LazyConstantTestUtil.functionHolderCounter(holder));
assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder));
int v = lazy.get(key);
int v2 = lazy.get(key);
i++;
}
assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder));
assertNull(LazyConstantTestUtil.functionHolderFunction(holder));
}
@ParameterizedTest
@MethodSource("allSets")
void functionHolderViaEntrySet(Set<Value> set) {
LazyConstantTestUtil.CountingFunction<Value, Integer> cif = new LazyConstantTestUtil.CountingFunction<>(MAPPER);
Map<Value, Integer> lazy = Map.ofLazy(set, cif);
Object holder = LazyConstantTestUtil.functionHolder(lazy);
int i = 0;
for (Map.Entry<Value, Integer> e : lazy.entrySet()) {
assertEquals(set.size() - i, LazyConstantTestUtil.functionHolderCounter(holder));
assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder));
int v = e.getValue();
int v2 = e.getValue();
i++;
}
assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder));
assertNull(LazyConstantTestUtil.functionHolderFunction(holder));
}
@ParameterizedTest
@MethodSource("allSets")
void underlyingRefViaEntrySetForEach(Set<Value> set) {
LazyConstantTestUtil.CountingFunction<Value, Integer> cif = new LazyConstantTestUtil.CountingFunction<>(MAPPER);
Map<Value, Integer> lazy = Map.ofLazy(set, cif);
Object holder = LazyConstantTestUtil.functionHolder(lazy);
final AtomicInteger i = new AtomicInteger();
lazy.entrySet().forEach(e -> {
assertEquals(set.size() - i.get(), LazyConstantTestUtil.functionHolderCounter(holder));
assertSame(cif, LazyConstantTestUtil.functionHolderFunction(holder));
Integer val = e.getValue();
Integer val2 = e.getValue();
i.incrementAndGet();
});
assertEquals(0, LazyConstantTestUtil.functionHolderCounter(holder));
assertNull(LazyConstantTestUtil.functionHolderFunction(holder));
}
@Test
void usesOptimizedVersion() {
Map<Value, Integer> enumMap = Map.ofLazy(EnumSet.of(KEY), Value::asInt);
assertTrue(enumMap.getClass().getName().contains("Enum"), enumMap.getClass().getName());
Map<Value, Integer> emptyMap = Map.ofLazy(EnumSet.noneOf(Value.class), Value::asInt);
assertFalse(emptyMap.getClass().getName().contains("Enum"), emptyMap.getClass().getName());
Map<Value, Integer> regularMap = Map.ofLazy(Set.of(KEY), Value::asInt);
assertFalse(regularMap.getClass().getName().contains("Enum"), regularMap.getClass().getName());
}
@Test
void overriddenEnum() {
final var overridden = Value.THIRTEEN;
Map<Value, Integer> enumMap = Map.ofLazy(EnumSet.of(overridden), MAPPER);
assertEquals(MAPPER.apply(overridden), enumMap.get(overridden), enumMap.toString());
}
@Test
void enumAliasing() {
enum MyEnum {FOO, BAR}
enum MySecondEnum{BAZ, QUX}
Map<MyEnum, Integer> mapEnum = Map.ofLazy(EnumSet.allOf(MyEnum.class), MyEnum::ordinal);
assertEquals(MyEnum.BAR.ordinal(), mapEnum.get(MyEnum.BAR));
// Make sure class is checked, not just `ordinal()`
assertNull(mapEnum.get(MySecondEnum.QUX));
}
// Support constructs
record Operation(String name,
Consumer<Map<Value, Integer>> consumer) implements Consumer<Map<Value, Integer>> {
@java.lang.Override
public void accept(Map<Value, Integer> map) { consumer.accept(map); }
@java.lang.Override
public String toString() { return name; }
}
static Stream<Operation> nullAverseOperations() {
return Stream.of(
new Operation("forEach", m -> m.forEach(null))
);
}
static Stream<Operation> unsupportedOperations() {
return Stream.of(
new Operation("clear", Map::clear),
new Operation("compute", m -> m.compute(KEY, (_, _) -> 1)),
new Operation("computeIfAbsent", m -> m.computeIfAbsent(KEY, _ -> 1)),
new Operation("computeIfPresent", m -> m.computeIfPresent(KEY, (_, _) -> 1)),
new Operation("merge", m -> m.merge(KEY, VALUE, (a, _) -> a)),
new Operation("put", m -> m.put(KEY, 0)),
new Operation("putAll", m -> m.putAll(Map.of())),
new Operation("remove1", m -> m.remove(KEY)),
new Operation("remove2", m -> m.remove(KEY, VALUE)),
new Operation("replace2", m -> m.replace(KEY, 1)),
new Operation("replace3", m -> m.replace(KEY, VALUE, 1)),
new Operation("replaceAll", m -> m.replaceAll((a, _) -> MAPPER.apply(a)))
);
}
static Map<Value, Integer> newLazyMap(Set<Value> set) {
return Map.ofLazy(set, MAPPER);
}
static Map<Value, Integer> newRegularMap(Set<Value> set) {
return set.stream()
.collect(Collectors.toMap(Function.identity(), MAPPER));
}
private static Stream<Set<Value>> nonEmptySets() {
return Stream.of(
Set.of(KEY, Value.THIRTEEN),
linkedHashSet(Value.THIRTEEN, KEY),
treeSet(KEY, Value.THIRTEEN),
EnumSet.of(KEY, Value.THIRTEEN)
);
}
private static Stream<Set<Value>> emptySets() {
return Stream.of(
Set.of(),
linkedHashSet(),
treeSet(),
EnumSet.noneOf(Value.class)
);
}
private static Stream<Set<Value>> allSets() {
return Stream.concat(
nonEmptySets(),
emptySets()
);
}
static Set<Value> treeSet(Value... values) {
return populate(new TreeSet<>(Comparator.comparingInt(Value::asInt).reversed()),values);
}
static Set<Value> linkedHashSet(Value... values) {
return populate(new LinkedHashSet<>(), values);
}
static Set<Value> populate(Set<Value> set, Value... values) {
set.addAll(Arrays.asList(values));
return set;
}
private static int functionCounter(Map<?, ?> lazy) {
final Object holder = LazyConstantTestUtil.functionHolder(lazy);
return LazyConstantTestUtil.functionHolderCounter(holder);
}
}