org.assertj.vavr.internal.Multimaps Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of assertj-vavr Show documentation
Show all versions of assertj-vavr Show documentation
Rich and fluent assertions for testing Vavr tools
/*
* 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.
*
* Copyright 2017-2020 the original author or authors.
*/
package org.assertj.vavr.internal;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import io.vavr.collection.*;
import io.vavr.control.Option;
import org.assertj.core.api.AssertionInfo;
import org.assertj.core.api.Condition;
import org.assertj.core.internal.Conditions;
import org.assertj.core.internal.Failures;
import org.assertj.core.internal.Objects;
import java.util.function.Predicate;
import static io.vavr.Predicates.not;
import static org.assertj.core.error.ElementsShouldBe.elementsShouldBe;
import static org.assertj.core.error.ShouldContain.shouldContain;
import static org.assertj.core.error.ShouldContainAnyOf.shouldContainAnyOf;
import static org.assertj.core.error.ShouldContainExactly.elementsDifferAtIndex;
import static org.assertj.core.error.ShouldContainExactly.shouldContainExactly;
import static org.assertj.core.error.ShouldContainKeys.shouldContainKeys;
import static org.assertj.core.error.ShouldContainOnly.shouldContainOnly;
import static org.assertj.core.error.ShouldContainOnlyKeys.shouldContainOnlyKeys;
import static org.assertj.core.error.ShouldContainValue.shouldContainValue;
import static org.assertj.core.error.ShouldContainValues.shouldContainValues;
import static org.assertj.core.error.ShouldNotContain.shouldNotContain;
import static org.assertj.core.error.ShouldNotContainKeys.shouldNotContainKeys;
import static org.assertj.core.error.ShouldNotContainValue.shouldNotContainValue;
import static org.assertj.core.internal.Arrays.assertIsArray;
import static org.assertj.core.internal.CommonValidations.failIfEmptySinceActualIsNotEmpty;
import static org.assertj.core.internal.CommonValidations.hasSameSizeAsCheck;
import static org.assertj.core.util.Objects.areEqual;
import static org.assertj.core.util.Preconditions.checkArgument;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.vavr.api.ShouldNotContainValues.shouldNotContainValues;
public final class Multimaps {
private static final Multimaps INSTANCE = new Multimaps();
private Failures failures = Failures.instance();
private Conditions conditions = Conditions.instance();
private Multimaps() {
}
public static Multimaps instance() {
return INSTANCE;
}
/**
* Verifies that the given {@code Multimap} contains the value for given {@code key} that satisfy given {@code valueCondition}.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param key the given key to check
* @param valueCondition the given condition for check value
* @throws NullPointerException if the given values is {@code null}
* @throws AssertionError if the actual map is {@code null}
* @throws AssertionError if the actual map does not contain the given {@code key}
* @throws AssertionError if the actual map contains the given key, but value does not match the given {@code valueCondition}
*/
@SuppressWarnings("unchecked")
public void assertHasEntrySatisfying(AssertionInfo info, Multimap actual, K key,
Condition super V> valueCondition) {
conditions.assertIsNotNull(valueCondition);
assertContainsKeys(info, actual, key);
Option> value = actual.get(key);
value
.getOrElse(List.empty())
.filter(valueCondition::matches)
.getOrElseThrow(() -> failures.failure(info, elementsShouldBe(actual, value, valueCondition)));
}
public void assertContainsAnyOf(AssertionInfo info, Multimap actual,
Tuple2[] entries) {
doCommonContainsCheck(info, actual, entries);
if (actual.isEmpty() && entries.length == 0) return;
failIfEmptySinceActualIsNotEmpty(entries);
for (Tuple2 extends K, ? extends V> entry : entries) {
if (containsEntry(actual, entry)) return;
}
throw failures.failure(info, shouldContainAnyOf(actual, entries));
}
/**
* Asserts that the given {@code Multimap} contains the given entries, in any order.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param entries the entries that are expected to be in the given {@code Multimap}
* @throws NullPointerException if the array of entries is {@code null}
* @throws IllegalArgumentException if the array of entries is empty
* @throws NullPointerException if any of the entries in the given array is {@code null}
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given {@code Multimap} does not contain the given entries
*/
public void assertContains(AssertionInfo info, Multimap actual,
Tuple2[] entries) {
doCommonContainsCheck(info, actual, entries);
if (actual.isEmpty() && entries.length == 0) return;
failIfEmptySinceActualIsNotEmpty(entries);
final Set> notFound = Array.of(entries).filter(entryNotPresentIn(actual)).toSet();
if (isNotEmpty(notFound)) {
throw failures.failure(info, shouldContain(actual, entries, notFound));
}
}
/**
* Asserts that the given {@code Multimap} contains the given entries only.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param entries the entries that are expected to only be in the given {@code Multimap}
* @throws AssertionError if the array of entries is {@code null}
* @throws AssertionError if the array of entries is empty
* @throws NullPointerException if any of the entries in the given array is {@code null}
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given {@code Multimap} contains any of the given entries
*/
public void assertContainsOnly(AssertionInfo info, Multimap actual, Iterable> entries) {
assertNotNull(info, actual);
failIfNull(entries);
if (actual.isEmpty() && !entries.iterator().hasNext()) return;
failIfEmpty(entries);
Multimap expected = HashMultimap.withSeq().ofEntries(entries);
Multimap notExpected = actual.filter(entryNotPresentIn(expected));
if (isNotEmpty(notExpected)) {
Multimap notFound = expected.filter(entryNotPresentIn(actual));
throw failures.failure(info, shouldContainOnly(actual, expected, notFound, notExpected));
}
}
/**
* Verifies that the actual {@code Multimap} contains only the given entries and nothing else, in order.
* This assertion should only be used with {@code Multimap} that have a consistent iteration order (i.e. don't use it with
* {@link io.vavr.collection.HashMultimap}).
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param entries the given entries
* @throws NullPointerException if the given entries array is {@code null}
* @throws AssertionError if the actual map is {@code null}
* @throws IllegalArgumentException if the given entries array is empty
* @throws AssertionError if the actual {@code Multimap} does not contain the given entries with same order, i.e. the actual {@code Multimap}
* contains some or none of the given entries, or the actual {@code Multimap} contains more entries than the given ones
* or entries are the same but the order is not
*/
public void assertContainsExactly(AssertionInfo info, Multimap actual,
@SuppressWarnings("unchecked") Tuple2 extends K, ? extends V>... entries) {
doCommonContainsCheck(info, actual, entries);
if (actual.isEmpty() && entries.length == 0) return;
failIfEmpty(entries);
assertHasSameSizeAs(info, actual, entries);
final Multimap expectedEntries = asLinkedMultimap(entries);
final Multimap notExpected = actual.filter(entry -> !expectedEntries.contains(entry));
final Multimap notFound = expectedEntries.filter(entry -> !actual.contains(entry));
if (notExpected.isEmpty() && notFound.isEmpty()) {
// check entries order
int index = 0;
for (K keyFromActual : actual.keySet()) {
if (areNotEqual(keyFromActual, entries[index]._1)) {
Tuple2> actualEntry = Tuple.of(keyFromActual, actual.get(keyFromActual).get());
throw failures.failure(info, elementsDifferAtIndex(actualEntry, entries[index], index));
}
index++;
}
// all entries are in the same order.
return;
}
throw failures.failure(info, shouldContainExactly(actual, List.of(entries), notFound, notExpected));
}
/**
* Asserts that the given {@code Multimap} does not contain the given entries.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param entries the entries that are expected to be in the given {@code Multimap}
* @throws NullPointerException if the array of entries is {@code null}
* @throws IllegalArgumentException if the array of entries is empty
* @throws NullPointerException if any of the entries in the given array is {@code null}
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given {@code Multimap} contains any of the given entries
*/
public void assertDoesNotContain(AssertionInfo info, Multimap actual,
Tuple2[] entries) {
failIfNullOrEmpty(entries);
assertNotNull(info, actual);
failIfEmptySinceActualIsNotEmpty(entries);
final Set> found = Array.of(entries).filter(actual::contains).toSet();
if (isNotEmpty(found)) {
throw failures.failure(info, shouldNotContain(actual, entries, found));
}
}
/**
* Verifies that the actual {@code Multimap} contains the given keys.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param keys the given keys
* @throws NullPointerException if the array of keys is {@code null}
* @throws IllegalArgumentException if the array of keys is empty
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given {@code Multimap} does not contain the given keys
*/
public void assertContainsKeys(AssertionInfo info, Multimap actual,
@SuppressWarnings("unchecked") K... keys) {
doCommonContainsCheck(info, actual, keys);
if (doCommonEmptinessChecks(actual, keys)) return;
Set expected = HashSet.of(keys);
Set notFound = expected.filter(keyNotPresentIn(actual.keySet()));
if (isNotEmpty(notFound)) {
throw failures.failure(info, shouldContainKeys(actual, notFound.toJavaSet()));
}
}
/**
* Asserts that the given {@code Multimap} contains the given keys, in any order.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param keys the keys that are expected to be in the given {@code Multimap}
* @throws NullPointerException if the array of keys is {@code null}
* @throws IllegalArgumentException if the array of keys is empty
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given {@code Multimap} does not contain the given keys
*/
public void assertContainsOnlyKeys(AssertionInfo info, Multimap actual, K[] keys) {
doCommonContainsCheck(info, actual, keys);
if (doCommonEmptinessChecks(actual, keys)) return;
Set expected = HashSet.of(keys);
Set notExpected = actual.keySet().filter(keyNotPresentIn(expected));
if (isNotEmpty(notExpected)) {
Set notFound = expected.filter(keyNotPresentIn(actual.keySet()));
throw failures.failure(info, shouldContainOnlyKeys(actual, expected, notFound, notExpected));
}
}
/**
* Verifies that the actual {@code Multimap} does not contain the given keys.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param keys the given keys
* @throws NullPointerException if the array of keys is {@code null}
* @throws IllegalArgumentException if the array of keys is empty
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given {@code Multimap} contains the given keys
*/
public void assertDoesNotContainKeys(AssertionInfo info, Multimap actual,
@SuppressWarnings("unchecked") K... keys) {
doCommonContainsCheck(info, actual, keys);
if (doCommonEmptinessChecks(actual, keys)) return;
Set expected = HashSet.of(keys);
Set found = expected.filter(keyPresentIn(actual.keySet()));
if (isNotEmpty(found)) {
throw failures.failure(info, shouldNotContainKeys(actual, found.toJavaSet()));
}
}
/**
* Verifies that the actual {@code Multimap} contains the given value.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param value the given value
* @throws AssertionError if the actual {@code Multimap} is {@code null}
* @throws AssertionError if the actual {@code Multimap} does not contain the given value
*/
public void assertContainsValue(AssertionInfo info, Multimap actual, V value) {
assertNotNull(info, actual);
if (!actual.containsValue(value)) throw failures.failure(info, shouldContainValue(actual, value));
}
/**
* Verifies that the actual {@code Multimap} contains the given values.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param values the given values
* @throws AssertionError if the actual {@code Multimap} is {@code null}
* @throws AssertionError if the actual {@code Multimap} does not contain the given values
* @throws NullPointerException if values vararg is {@code null}
*/
public void assertContainsValues(AssertionInfo info, Multimap actual,
@SuppressWarnings("unchecked") V... values) {
assertNotNull(info, actual);
checkNotNull(values, "The array of values to look for should not be null");
if (actual.isEmpty() && values.length == 0) return;
Set expected = HashSet.of(values);
Set notFound = expected.filter(valueNotPresentIn(actual.values()));
if (isNotEmpty(notFound)) throw failures.failure(info, shouldContainValues(actual, notFound.toJavaSet()));
}
/**
* Verifies that the actual {@code Multimap} does not contain the given value.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param value the given value
* @throws AssertionError if the actual {@code Multimap} is {@code null}
* @throws AssertionError if the actual {@code Multimap} contains the given value
*/
public void assertDoesNotContainValue(AssertionInfo info, Multimap actual, V value) {
assertNotNull(info, actual);
if (actual.containsValue(value)) throw failures.failure(info, shouldNotContainValue(actual, value));
}
/**
* Verifies that the actual {@code Multimap} does not contain the given values.
*
* @param key type
* @param value type
* @param info contains information about the assertion
* @param actual the given {@code Multimap}
* @param values the given values
* @throws AssertionError if the actual {@code Multimap} is {@code null}
* @throws AssertionError if the actual {@code Multimap} contains the given values
* @throws NullPointerException if values vararg is {@code null}
*/
public void assertDoesNotContainValues(AssertionInfo info, Multimap actual,
@SuppressWarnings("unchecked") V... values) {
assertNotNull(info, actual);
checkNotNull(values, "The array of values to look for should not be null");
if (actual.isEmpty() && values.length == 0) return;
Set expected = HashSet.of(values);
Set found = expected.filter(valuePresentIn(actual.values()));
if (isNotEmpty(found)) throw failures.failure(info, shouldNotContainValues(actual, found.toJavaSet()));
}
/**
* Asserts that the number of entries in the given {@code Multimap} has the same size as the other array.
*
* @param info contains information about the assertion
* @param multimap the given {@code Multimap}
* @param other the group to compare
* @throws AssertionError if the given {@code Multimap} is {@code null}
* @throws AssertionError if the given array is {@code null}
* @throws AssertionError if the number of entries in the given {@code Multimap} does not have the same size
*/
public void assertHasSameSizeAs(AssertionInfo info, Multimap, ?> multimap, Object other) {
assertNotNull(info, multimap);
assertIsArray(info, other);
hasSameSizeAsCheck(info, multimap, other, multimap.size());
}
private void doCommonContainsCheck(AssertionInfo info, Multimap actual,
Tuple2 extends K, ? extends V>[] entries) {
assertNotNull(info, actual);
failIfNull(entries);
}
private void doCommonContainsCheck(AssertionInfo info, Multimap actual, K[] keys) {
assertNotNull(info, actual);
failIfNull(keys);
}
private boolean doCommonEmptinessChecks(Multimap actual, K[] keys) {
if (actual.isEmpty() && keys.length == 0) {
return true;
}
failIfEmpty(keys);
return false;
}
private static Multimap asLinkedMultimap(Tuple2 extends K, ? extends V>[] entries) {
if (entries.length != nonNullEntries(Array.of(entries)).length()) {
throw new NullPointerException("One of expected entries is null");
} else {
return LinkedHashMultimap.withSeq().ofEntries(entries);
}
}
private static Traversable> nonNullEntries(Traversable> entries) {
return entries.filter(java.util.Objects::nonNull);
}
private boolean containsEntry(Multimap actual, Tuple2 extends K, ? extends V> entry) {
checkNotNull(entry, "Entry to look for should not be null");
return actual.containsKey(entry._1) && actual.get(entry._1).get().contains(entry._2);
}
private static boolean areNotEqual(K actualKey, K expectedKey) {
return !areEqual(actualKey, expectedKey);
}
private static void failIfNullOrEmpty(Tuple2 extends K, ? extends V>[] entries) {
failIfNull(entries);
failIfEmpty(entries);
}
private static void failIfNull(Iterable> entries) {
checkNotNull(entries, "The entries should not be null");
}
private static void failIfNull(K[] keys) {
checkNotNull(keys, "The array of keys to look for should not be null");
}
private static void failIfNull(Tuple2 extends K, ? extends V>[] entries) {
checkNotNull(entries, "The array of entries should not be null");
}
private static void failIfEmpty(Iterable> entries) {
checkArgument(entries.iterator().hasNext(), "The entries should not be empty");
}
private static void failIfEmpty(Tuple2 extends K, ? extends V>[] entries) {
checkArgument(entries.length > 0, "The array of entries to look for should not be empty");
}
private static void failIfEmpty(K[] keys) {
checkArgument(keys.length > 0, "The array of keys to look for should not be empty");
}
private static void assertNotNull(AssertionInfo info, Multimap, ?> actual) {
Objects.instance().assertNotNull(info, actual);
}
private static Predicate> entryNotPresentIn(Multimap map) {
return tuple -> !map.contains(tuple);
}
private static Predicate keyNotPresentIn(Set elements) {
return not(keyPresentIn(elements));
}
private static Predicate keyPresentIn(Set elements) {
return elements::contains;
}
private static Predicate valueNotPresentIn(Traversable elements) {
return not(valuePresentIn(elements));
}
private static Predicate valuePresentIn(Traversable elements) {
return elements::contains;
}
private static boolean isNotEmpty(Traversable traversable) {
return !traversable.isEmpty();
}
}