
com.github.robtimus.junit.support.collections.MapTests Maven / Gradle / Ivy
/*
* MapTests.java
* Copyright 2020 Rob Spoor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.robtimus.junit.support.collections;
import static com.github.robtimus.junit.support.collections.CollectionAssertions.assertHasElements;
import static com.github.robtimus.junit.support.collections.CollectionUtils.toList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import com.github.robtimus.junit.support.collections.annotation.ContainsIncompatibleKeyNotSupported;
import com.github.robtimus.junit.support.collections.annotation.ContainsIncompatibleNotSupported;
import com.github.robtimus.junit.support.collections.annotation.ContainsNullKeyNotSupported;
import com.github.robtimus.junit.support.collections.annotation.ContainsNullNotSupported;
import com.github.robtimus.junit.support.collections.annotation.RemoveIncompatibleKeyNotSupported;
import com.github.robtimus.junit.support.collections.annotation.RemoveIncompatibleNotSupported;
import com.github.robtimus.junit.support.collections.annotation.RemoveNullKeyNotSupported;
import com.github.robtimus.junit.support.collections.annotation.RemoveNullNotSupported;
import com.github.robtimus.junit.support.collections.annotation.StoreNullKeyNotSupported;
import com.github.robtimus.junit.support.collections.annotation.StoreNullNotSupported;
/**
* Base interface for testing separate {@link Map} functionalities.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
public interface MapTests {
/**
* Returns the map to test. This should be populated, i.e. not empty, unless the map can only be empty.
*
* This method will be called only once for each test. This makes it possible to initialize the map in a method annotated with {@link BeforeEach},
* and perform additional tests after the pre-defined test has finished.
*
* @return The map to test.
*/
Map map();
/**
* Returns a map with the expected entries contained by the map to test.
* This should not be of the same type as the map to test, but preferably of some well-known map type like {@link HashMap}.
*
* @return A map with the expected entries contained by the map to test.
*/
Map expectedEntries();
/**
* Returns some entries that should not be contained by the map to test. For both {@link Map#keySet()} and {@link Map#values()}, the returned map
* should have no common elements with {@link #expectedEntries()}.
*
* @return A map with entries that should not be contained by the entries to test.
*/
Map nonContainedEntries();
/**
* Contains tests for {@link Map#containsKey(Object)}.
*
* By default, the tests in this interface assume that calling {@link Map#containsKey(Object)} with {@code null} or an instance of an incompatible
* type will simply return {@code false}. If either is not the case, annotate your class with {@link ContainsNullKeyNotSupported} and/or
* {@link ContainsIncompatibleKeyNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("containsKey(Object)")
interface ContainsKeyTests extends MapTests {
@Test
@DisplayName("containsKey(Object)")
default void testContainsKey() {
Map map = map();
for (K o : expectedEntries().keySet()) {
assertTrue(map.containsKey(o));
}
for (K o : nonContainedEntries().keySet()) {
assertFalse(map.containsKey(o));
}
}
@Test
@DisplayName("containsKey(Object) with null")
default void testContainsKeyWithNull(TestInfo testInfo) {
Map map = map();
ContainsNullKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(ContainsNullKeyNotSupported.class);
if (annotation == null) {
assertFalse(map.containsKey(null));
} else {
assertThrows(annotation.expected(), () -> map.containsKey(null));
}
}
@Test
@DisplayName("containsKey(Object) with an incompatible object")
default void testContainsKeyWithIncompatibleObject(TestInfo testInfo) {
Map map = map();
ContainsIncompatibleKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(ContainsIncompatibleKeyNotSupported.class);
if (annotation == null) {
assertFalse(map.containsKey(new IncompatibleObject()));
} else {
assertThrows(annotation.expected(), () -> map.containsKey(new IncompatibleObject()));
}
}
}
/**
* Contains tests for {@link Map#containsValue(Object)}.
*
* By default, the tests in this interface assume that calling {@link Map#containsValue(Object)} with {@code null} or an instance of an
* incompatible type will simply return {@code false}. If either is not the case, annotate your class with {@link ContainsNullNotSupported} and/or
* {@link ContainsIncompatibleNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("containsValue(Object)")
interface ContainsValueTests extends MapTests {
@Test
@DisplayName("containsValue(Object)")
default void testContainsValue() {
Map map = map();
for (V o : expectedEntries().values()) {
assertTrue(map.containsValue(o));
}
for (V o : nonContainedEntries().values()) {
assertFalse(map.containsValue(o));
}
}
@Test
@DisplayName("containsValue(Object) with null")
default void testContainsValueWithNull(TestInfo testInfo) {
Map map = map();
ContainsNullNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(ContainsNullNotSupported.class);
if (annotation == null) {
assertFalse(map.containsValue(null));
} else {
assertThrows(annotation.expected(), () -> map.containsValue(null));
}
}
@Test
@DisplayName("containsValue(Object) with an incompatible object")
default void testContainsValueWithIncompatibleObject(TestInfo testInfo) {
Map map = map();
ContainsIncompatibleNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(ContainsIncompatibleNotSupported.class);
if (annotation == null) {
assertFalse(map.containsValue(new IncompatibleObject()));
} else {
assertThrows(annotation.expected(), () -> map.containsValue(new IncompatibleObject()));
}
}
}
/**
* Contains tests for {@link Map#get(Object)}.
*
* By default, the tests in this interface assume that calling {@link Map#get(Object)} with {@code null} or an instance of an incompatible type
* will simply return {@code false}. If either is not the case, annotate your class with {@link ContainsNullKeyNotSupported} and/or
* {@link ContainsIncompatibleKeyNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("get(Object)")
interface GetTests extends MapTests {
@Test
@DisplayName("get(Object)")
default void testGet() {
Map map = map();
for (Map.Entry entry : expectedEntries().entrySet()) {
assertEquals(entry.getValue(), map.get(entry.getKey()));
}
for (K key : nonContainedEntries().keySet()) {
assertNull(map.get(key));
}
}
@Test
@DisplayName("get(Object) with null")
default void testGetWithNull(TestInfo testInfo) {
Map map = map();
ContainsNullKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(ContainsNullKeyNotSupported.class);
if (annotation == null) {
assertNull(map.get(null));
} else {
assertThrows(annotation.expected(), () -> map.get(null));
}
}
@Test
@DisplayName("get(Object) with an incompatible object")
default void testGetWithIncompatibleObject(TestInfo testInfo) {
Map map = map();
ContainsIncompatibleKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(ContainsIncompatibleKeyNotSupported.class);
if (annotation == null) {
assertNull(map.get(new IncompatibleObject()));
} else {
assertThrows(annotation.expected(), () -> map.get(new IncompatibleObject()));
}
}
}
/**
* Contains tests for {@link Map#put(Object, Object)}.
*
* By default, the tests in this interface assume that calling {@link Map#put(Object, Object)} with a {@code null} key or value will simply add
* such an entry. If either is not the case, annotate your class with {@link StoreNullKeyNotSupported} and/or
* {@link StoreNullNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("put(Object, Object)")
interface PutTests extends MapTests {
/**
* Returns a unary operator that can be used to create new values to set with {@link Map#put(Object, Object)}.
*
* @return A unary operator that can be used to create new values to set with {@link Map#put(Object, Object)}.
*/
UnaryOperator replaceValueOperator();
@Test
@DisplayName("put(Object, Object)")
default void testPut() {
Map map = map();
Map expectedEntries = new HashMap<>(expectedEntries());
UnaryOperator operator = replaceValueOperator();
for (Map.Entry entry : expectedEntries.entrySet()) {
V value = entry.getValue();
assertEquals(value, map.put(entry.getKey(), operator.apply(value)));
}
Map nonContainedEntries = nonContainedEntries();
for (Map.Entry entry : nonContainedEntries.entrySet()) {
assertNull(map.put(entry.getKey(), entry.getValue()));
}
expectedEntries.replaceAll((k, v) -> operator.apply(v));
expectedEntries.putAll(nonContainedEntries);
assertEquals(expectedEntries, map);
}
@Test
@DisplayName("put(Object, Object) with null key")
default void testPutWithNullKey(TestInfo testInfo) {
Map map = map();
StoreNullKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(StoreNullKeyNotSupported.class);
V nonContainedValue = nonContainedEntries().values().iterator().next();
if (annotation == null) {
assertNull(map.put(null, nonContainedValue));
} else {
assertThrows(annotation.expected(), () -> map.put(null, nonContainedValue));
}
if (annotation == null) {
Map expectedEntries = new HashMap<>(expectedEntries());
expectedEntries.put(null, nonContainedValue);
assertEquals(expectedEntries, map);
} else {
assertEquals(expectedEntries(), map);
}
}
@Test
@DisplayName("put(Object, Object) with null value")
default void testPutWithNullValue(TestInfo testInfo) {
Map map = map();
StoreNullNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(StoreNullNotSupported.class);
Map expectedEntries = expectedEntries();
for (Map.Entry entry : expectedEntries.entrySet()) {
if (annotation == null) {
assertEquals(entry.getValue(), map.put(entry.getKey(), null));
} else {
assertThrows(annotation.expected(), () -> map.put(entry.getKey(), null));
}
}
if (annotation == null) {
expectedEntries = new HashMap<>(expectedEntries);
expectedEntries.replaceAll((k, v) -> null);
assertEquals(expectedEntries, map);
} else {
assertEquals(expectedEntries(), map);
}
}
}
/**
* Contains tests for {@link Map#remove(Object)}.
*
* By default, the tests in this interface assume that calling {@link Map#remove(Object)} with {@code null} or an instance of an incompatible type
* will simply return {@code false}. If either is not the case, annotate your class with {@link RemoveNullKeyNotSupported} and/or
* {@link RemoveIncompatibleKeyNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@TestInstance(Lifecycle.PER_CLASS)
@DisplayName("remove(Object)")
interface RemoveTests extends MapTests {
@ParameterizedTest(name = "{0}: {1}")
@ArgumentsSource(RemoveArgumentsProvider.class)
@DisplayName("remove(Object)")
default void testRemove(Object key, Object expectedValue, boolean expected) {
Map map = map();
assertEquals(expectedValue, map.remove(key));
Map expectedEntries = expectedEntries();
if (expected) {
expectedEntries = new HashMap<>(expectedEntries);
expectedEntries.remove(key);
}
assertEquals(expectedEntries, map);
}
@Test
@DisplayName("remove(Object) with null")
default void testRemoveNull(TestInfo testInfo) {
Map map = map();
RemoveNullKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(RemoveNullKeyNotSupported.class);
if (annotation == null) {
assertNull(map.remove(null));
} else {
assertThrows(annotation.expected(), () -> map.remove(null));
}
assertEquals(expectedEntries(), map);
}
@Test
@DisplayName("remove(Object) with incompatible object")
default void testRemoveIncompatibleObject(TestInfo testInfo) {
Map map = map();
RemoveIncompatibleKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(RemoveIncompatibleKeyNotSupported.class);
if (annotation == null) {
assertNull(map.remove(new IncompatibleObject()));
} else {
assertThrows(annotation.expected(), () -> map.remove(new IncompatibleObject()));
}
assertEquals(expectedEntries(), map);
}
}
/**
* Contains tests for {@link Map#putAll(Map)}.
*
* By default, the tests in this interface assume that calling {@link Map#putAll(Map)} with a map containing {@code null} keys or values will
* simply add the entry with the {@code null} key or value. If this is not the case, annotate your class with {@link StoreNullKeyNotSupported}
* and/or {@link StoreNullNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@TestInstance(Lifecycle.PER_CLASS)
@DisplayName("putAll(Map)")
interface PutAllTests extends MapTests {
/**
* Returns a unary operator that can be used to create new values to set with {@link Map#putAll(Map)}.
*
* @return A unary operator that can be used to create new values to set with {@link Map#putAll(Map)}.
*/
UnaryOperator replaceValueOperator();
@Test
@DisplayName("putAll(Map)")
default void testPutAll() {
Map map = map();
Map expectedEntries = new HashMap<>(expectedEntries());
map.putAll(Collections.emptyMap());
assertEquals(expectedEntries, map);
UnaryOperator operator = replaceValueOperator();
Map m = new HashMap<>();
for (Iterator> i = expectedEntries.entrySet().iterator(); i.hasNext(); ) {
Map.Entry entry = i.next();
K key = entry.getKey();
V value = entry.getValue();
V newValue = operator.apply(value);
m.put(key, operator.apply(value));
expectedEntries.put(key, newValue);
if (i.hasNext()) {
i.next();
}
}
map.putAll(m);
assertEquals(expectedEntries, map);
Map nonContainedEntries = nonContainedEntries();
map.putAll(nonContainedEntries);
expectedEntries.putAll(nonContainedEntries);
assertEquals(expectedEntries, map);
}
@Test
@DisplayName("putAll(Map) with a null map")
default void testPutAllWithNullMap() {
Map map = map();
assertThrows(NullPointerException.class, () -> map.putAll(null));
assertEquals(expectedEntries(), map);
}
@Test
@DisplayName("putAll(Map) with a map with a null key")
default void testPutAllWithMapWithNullKey(TestInfo testInfo) {
Map map = map();
V nonContainedValue = nonContainedEntries().values().iterator().next();
Map m = Collections.singletonMap(null, nonContainedValue);
StoreNullKeyNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(StoreNullKeyNotSupported.class);
Map expectedEntries = expectedEntries();
if (annotation == null) {
map.putAll(m);
expectedEntries = new HashMap<>(expectedEntries);
expectedEntries.put(null, nonContainedValue);
} else {
assertThrows(annotation.expected(), () -> map.putAll(m));
}
assertEquals(expectedEntries, map);
}
@Test
@DisplayName("putAll(Map) with a map with a null value")
default void testPutAllWithMapWithNullValue(TestInfo testInfo) {
Map map = map();
Map expectedEntries = expectedEntries();
Map m = new HashMap<>(expectedEntries);
m.replaceAll((k, v) -> null);
StoreNullNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(StoreNullNotSupported.class);
if (annotation == null) {
map.putAll(m);
expectedEntries = new HashMap<>(expectedEntries);
expectedEntries.replaceAll((k, v) -> null);
} else {
assertThrows(annotation.expected(), () -> map.putAll(m));
}
assertEquals(expectedEntries, map);
}
}
/**
* Contains tests for {@link Map#clear()}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("clear()")
interface ClearTests extends MapTests {
@Test
@DisplayName("clear()")
default void testClear() {
Map map = map();
map.clear();
assertEquals(Collections.emptyMap(), map);
}
}
/**
* Contains tests for {@link Map#keySet()}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("keySet()")
interface KeySetTests extends MapTests, SetTests {
@Override
default Set iterable() {
return map().keySet();
}
@Override
default Collection expectedElements() {
return expectedEntries().keySet();
}
@Override
default Collection nonContainedElements() {
return nonContainedEntries().keySet();
}
/**
* Contains tests for {@link Set#iterator()} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("forEach(Consumer)")
interface IteratorTests extends KeySetTests, com.github.robtimus.junit.support.collections.IteratorTests {
@Override
default Set iterable() {
return KeySetTests.super.iterable();
}
@Override
default Collection expectedElements() {
return KeySetTests.super.expectedElements();
}
/**
* Contains tests for {@link Iterator#hasNext()} and {@link Iterator#next()} for key set iterators.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("iteration")
interface IterationTests
extends IteratorTests, com.github.robtimus.junit.support.collections.IteratorTests.IterationTests {
// no new tests needed
}
/**
* Contains tests for {@link Iterator#remove()} for key set iterators.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("remove()")
interface RemoveTests extends IteratorTests, com.github.robtimus.junit.support.collections.IteratorTests.RemoveTests {
@Override
@Test
@DisplayName("remove() for every element")
default void testRemoveEveryElement() {
Map map = map();
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
List remaining = toList(keySet);
assertHasElements(remaining, Collections.emptyList(), fixedOrder());
assertEquals(Collections.emptyMap(), map);
}
@Override
@Test
@DisplayName("remove() for every even-indexed element")
default void testRemoveEveryEvenIndexedElement() {
Map map = map();
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
Map expectedEntries = new HashMap<>(expectedEntries());
List expectedElements = new ArrayList<>(expectedEntries.keySet());
boolean remove = true;
while (iterator.hasNext()) {
K element = iterator.next();
if (remove) {
expectedElements.remove(element);
expectedEntries.remove(element);
iterator.remove();
}
remove = !remove;
}
List remaining = toList(keySet);
assertHasElements(remaining, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("remove() before next()")
default void testRemoveBeforeNext() {
Map map = map();
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
assertThrows(IllegalStateException.class, iterator::remove);
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
List remaining = toList(keySet);
assertHasElements(remaining, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("remove() after remove()")
default void testRemoveAfterRemove() {
Map map = map();
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
Map expectedEntries = new HashMap<>(expectedEntries());
List expectedElements = new ArrayList<>(expectedEntries.keySet());
boolean remove = true;
while (iterator.hasNext()) {
K element = iterator.next();
if (remove) {
expectedElements.remove(element);
expectedEntries.remove(element);
iterator.remove();
assertThrows(IllegalStateException.class, iterator::remove);
}
remove = !remove;
}
List remaining = toList(keySet);
assertHasElements(remaining, expectedElements, fixedOrder());
}
}
/**
* Contains tests for {@link Iterator#forEachRemaining(Consumer)} for key set iterators.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("forEachRemaining(Consumer)")
interface ForEachRemainingTests
extends IteratorTests, com.github.robtimus.junit.support.collections.IteratorTests.ForEachRemainingTests {
// no new tests needed
}
}
/**
* Contains tests for {@link Iterable#forEach(Consumer)} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("forEach(Consumer)")
interface ForEachTests extends KeySetTests, IterableTests.ForEachTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#contains(Object)} for key sets.
*
* By default, the tests in this interface assume that calling {@link Set#contains(Object)} with {@code null} or an instance of an
* incompatible type will simply return {@code false}. If either is not the case, annotate your class with {@link ContainsNullNotSupported}
* and/or {@link ContainsIncompatibleNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("contains(Object)")
interface ContainsTests extends KeySetTests, CollectionTests.ContainsTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#toArray()} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("toArray()")
interface ToObjectArrayTests extends KeySetTests, CollectionTests.ToObjectArrayTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#toArray(Object[])} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("toArray(Object[])")
interface ToArrayTests extends KeySetTests, CollectionTests.ToArrayTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#add(Object)} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("add(Object)")
interface AddTests extends KeySetTests, UnmodifiableCollectionTests.AddTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#remove(Object)} for key sets.
*
* By default, the tests in this interface assume that calling {@link Set#remove(Object)} with {@code null} or an instance of an incompatible
* type will simply return {@code false}. If either is not the case, annotate your class with {@link RemoveNullNotSupported} and/or
* {@link RemoveIncompatibleNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@TestInstance(Lifecycle.PER_CLASS)
@DisplayName("remove(Object)")
interface RemoveTests extends KeySetTests, CollectionTests.RemoveTests {
@Override
@ParameterizedTest(name = "{0}: {1}")
@ArgumentsSource(CollectionTests.RemoveArgumentsProvider.class)
@DisplayName("remove(Object)")
default void testRemove(Object o, boolean expected) {
Map map = map();
Set keySet = map.keySet();
assertEquals(expected, keySet.remove(o));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
if (expected) {
expectedEntries = new HashMap<>(expectedEntries);
expectedElements = expectedEntries.keySet();
expectedElements.remove(o);
}
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("remove(Object) with null")
default void testRemoveNull(TestInfo testInfo) {
Map map = map();
Set keySet = map.keySet();
RemoveNullNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(RemoveNullNotSupported.class);
if (annotation == null) {
assertFalse(keySet.remove(null));
} else {
assertThrows(annotation.expected(), () -> keySet.remove(null));
}
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("remove(Object) with incompatible object")
default void testRemoveIncompatibleObject(TestInfo testInfo) {
Map map = map();
Set keySet = map.keySet();
RemoveIncompatibleNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(RemoveIncompatibleNotSupported.class);
if (annotation == null) {
assertFalse(keySet.remove(new IncompatibleObject()));
} else {
assertThrows(annotation.expected(), () -> keySet.remove(new IncompatibleObject()));
}
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
}
/**
* Contains tests for {@link Set#containsAll(Collection)} for key sets.
*
* By default, the tests in this interface assume that calling {@link Set#containsAll(Collection)} with a collection containing {@code null}
* or an instance of an incompatible type will simply return {@code false}. If either is not the case, annotate your class with
* {@link ContainsNullNotSupported} and/or {@link ContainsIncompatibleNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@TestInstance(Lifecycle.PER_CLASS)
@DisplayName("containsAll(Collection)")
interface ContainsAllTests extends KeySetTests, CollectionTests.ContainsAllTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#addAll(Collection)} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("addAll(Collection)")
interface AddAllTests extends KeySetTests, UnmodifiableCollectionTests.AddAllTests {
// no new tests needed
}
/**
* Contains tests for {@link Set#removeAll(Collection)} for key sets.
*
* By default, the tests in this interface assume that calling {@link Set#removeAll(Collection)} with a collection containing {@code null} or
* an instance of an incompatible type will simply ignore the {@code null} and incompatible element. If either is not the case, annotate your
* class with {@link RemoveNullNotSupported} and/or {@link RemoveIncompatibleNotSupported}.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@TestInstance(Lifecycle.PER_CLASS)
@DisplayName("removeAll(Collection)")
interface RemoveAllTests extends KeySetTests, CollectionTests.RemoveAllTests {
@Override
@ParameterizedTest(name = "{0}: {1}")
@ArgumentsSource(RemoveAllArgumentsProvider.class)
@DisplayName("removeAll(Collection)")
default void testRemoveAll(Collection> c, boolean expected) {
Map map = map();
Set keySet = map.keySet();
assertEquals(expected, keySet.removeAll(c));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
if (expected) {
expectedEntries = new HashMap<>(expectedEntries);
expectedElements = expectedEntries.keySet();
expectedElements.removeAll(c);
}
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("removeAll(Collection) with a null collection")
default void testRemoveAllWithNullCollection() {
Map map = map();
Set keySet = map.keySet();
assertThrows(NullPointerException.class, () -> keySet.removeAll(null));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("removeAll(Collection) with a collection with a null")
default void testRemoveAllWithCollectionWithNull(TestInfo testInfo) {
Map map = map();
Set keySet = map.keySet();
Collection> c = Collections.singleton(null);
RemoveNullNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(RemoveNullNotSupported.class);
if (annotation == null) {
assertFalse(keySet.removeAll(c));
} else {
assertThrows(annotation.expected(), () -> keySet.removeAll(c));
}
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("removeAll(Collection) with a collection with an incompatible object")
default void testRemoveAllWithCollectionWithIncompatibleObject(TestInfo testInfo) {
Map map = map();
Set keySet = map.keySet();
Collection> c = Collections.singleton(new IncompatibleObject());
RemoveIncompatibleNotSupported annotation = testInfo.getTestClass()
.orElseThrow(() -> new IllegalStateException("test class should be available")) //$NON-NLS-1$
.getAnnotation(RemoveIncompatibleNotSupported.class);
if (annotation == null) {
assertFalse(keySet.removeAll(c));
} else {
assertThrows(annotation.expected(), () -> keySet.removeAll(c));
}
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
}
/**
* Contains tests for {@link Set#removeIf(Predicate)} for key sets.
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@DisplayName("removeIf(Predicate)")
interface RemoveIfTests extends KeySetTests, CollectionTests.RemoveIfTests {
@Override
@Test
@DisplayName("removeIf(Predicate) with matching predicate")
default void testRemoveIfWithMatchingPredicate() {
Map map = map();
Set keySet = map.keySet();
boolean isEmpty = keySet.isEmpty();
assertEquals(!isEmpty, keySet.removeIf(e -> true));
assertHasElements(keySet, Collections.emptyList(), fixedOrder());
assertEquals(Collections.emptyMap(), map);
}
@Override
@Test
@DisplayName("removeIf(Predicate) with non-matching predicate")
default void testRemoveIfWithNonMatchingPredicate() {
Map map = map();
Set keySet = map.keySet();
assertFalse(keySet.removeIf(e -> false));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("removeIf(Predicate) with null predicate")
default void testRemoveIfWithNullPredicate() {
Map map = map();
Set keySet = map.keySet();
assertThrows(NullPointerException.class, () -> keySet.removeIf(null));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
}
/**
* Contains tests for {@link Set#retainAll(Collection)} for key sets.
*
* By default, the tests in this interface assume that calling {@link Set#retainAll(Collection)} with a collection containing {@code null} or
* an instance of an incompatible type will simply ignore the {@code null} and incompatible element. If either is not the case, annotate your
* class with {@link ContainsNullNotSupported} and/or {@link ContainsIncompatibleNotSupported}
*
* @author Rob Spoor
* @param The key type of the map to test.
* @param The value type of the map to test.
*/
@TestInstance(Lifecycle.PER_CLASS)
@DisplayName("retainAll(Collection)")
interface RetainAllTests extends KeySetTests, CollectionTests.RetainAllTests {
@Override
@ParameterizedTest(name = "{0}: {1}")
@ArgumentsSource(RetainAllArgumentsProvider.class)
@DisplayName("retainAll(Collection)")
default void testRetainAll(Collection> c, boolean expected) {
Map map = map();
Set keySet = map.keySet();
assertEquals(expected, keySet.retainAll(c));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
if (expected) {
expectedEntries = new HashMap<>(expectedEntries);
expectedElements = expectedEntries.keySet();
expectedElements.retainAll(c);
}
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("retainAll(Collection) with a null collection")
default void testRetainAllWithNullCollection() {
Map map = map();
Set keySet = map.keySet();
assertThrows(NullPointerException.class, () -> keySet.retainAll(null));
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
assertHasElements(keySet, expectedElements, fixedOrder());
assertEquals(expectedEntries, map);
}
@Override
@Test
@DisplayName("retainAll(Collection) with a collection with a null")
default void testRetainAllWithCollectionWithNull(TestInfo testInfo) {
Map map = map();
Set keySet = map.keySet();
Map expectedEntries = expectedEntries();
Set expectedElements = expectedEntries.keySet();
Collection