diff --git a/hamcrest/src/main/java/org/hamcrest/collection/IsIterableContainingInAnyOrder.java b/hamcrest/src/main/java/org/hamcrest/collection/IsIterableContainingInAnyOrder.java index 262a1263..7af9ecce 100644 --- a/hamcrest/src/main/java/org/hamcrest/collection/IsIterableContainingInAnyOrder.java +++ b/hamcrest/src/main/java/org/hamcrest/collection/IsIterableContainingInAnyOrder.java @@ -3,11 +3,9 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.hamcrest.comparator.ComparatorMatcherBuilder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; import static org.hamcrest.core.IsEqual.equalTo; @@ -138,6 +136,42 @@ public static Matcher> containsInAnyOrder(T... items) return new IsIterableContainingInAnyOrder<>(matchers); } + /** + *

+ * Creates an order agnostic matcher for {@link Iterable}s that matches when a single pass over + * the examined {@link Iterable} yields a series of items, each logically equal according to + * the comparator to one item anywhere in the specified items. For a positive match, the + * examined iterable must be of the same length as the number of specified items. + *

+ *

+ * N.B. each of the specified items will only be used once during a given examination, so be + * careful when specifying items that may be equal to more than one entry in an examined + * iterable. + *

+ *

+ * For example: + *

+ *
assertThat(Arrays.asList("first", "second"), containsInAnyOrder(new StringLengthComparator(), "abcde", "ZYXWVU"))
+ * + * @param + * the matcher type. + * @param comparator + * the comparator to use to compare items to the items provided. + * @param items + * the items that must equal (according to the provided comparator) the items provided by + * an examined {@link Iterable} in any order + * @return The matcher. + */ + @SafeVarargs + public static Matcher> containsInAnyOrder(Comparator comparator, T... items) { + List> matchers = new ArrayList<>(); + for (T item : items) { + matchers.add(ComparatorMatcherBuilder.comparedBy(comparator).comparesEqualTo(item)); + } + + return containsInAnyOrder(matchers); + } + /** *

* Creates an order agnostic matcher for {@link Iterable}s that matches when a single pass over diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java index d27ff3ff..f29e1334 100644 --- a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java +++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java @@ -5,11 +5,15 @@ import org.hamcrest.collection.IsIterableContainingInOrderTest.WithValue; import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; import static java.util.Arrays.asList; import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; import static org.hamcrest.collection.IsIterableContainingInOrderTest.make; import static org.hamcrest.collection.IsIterableContainingInOrderTest.value; +import static org.hamcrest.collection.IsIterableContainingInAnyOrderTest.Item.item; public class IsIterableContainingInAnyOrderTest extends AbstractMatcherTest { @@ -52,4 +56,98 @@ public void testHasAReadableDescription() { assertDescription("iterable with items [<1>, <2>] in any order", containsInAnyOrder(1, 2)); } + private static final ItemValueComparator comparator = new ItemValueComparator(); + + private static class ItemValueComparator implements Comparator { + @Override + public int compare(Item o1, Item o2) { + return Integer.compare(o1.value, o2.value); + } + + @Override + public String toString() { + return ItemValueComparator.class.getSimpleName(); + } + } + + public static class Item { + private final String key; + private final int value; + + private Item(String key, int value) { + this.key = key; + this.value = value; + } + + public static Item item(String key, int value) { + return new Item(key, value); + } + + public int getValue() { + return value; + } + + @Override + public String toString() { + return key + ":" + value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Item item = (Item) o; + return Objects.equals(key, item.key); + } + + @Override + public int hashCode() { + return Objects.hash(key); + } + } + + public void testMatchesSingleItemIterableWithComparator() { + assertMatches("single item", containsInAnyOrder(comparator, item("key", 1)), Collections.singletonList(item("foo", 1))); + } + + public void testDoesNotMatchEmptyWithComparator() { + List actual = Collections.emptyList(); + Matcher> expected = containsInAnyOrder(comparator, item("key", 5), item("key", 10)); + assertMismatchDescription("no item matches: a value equal to when compared by , a value equal to when compared by in []", expected, actual); + } + + public void testMatchesIterableOutOfOrderWithComparator() { + List actual = asList(item("foo", 2), item("bar", 1)); + Matcher> expected = containsInAnyOrder(comparator, item("key", 1), item("key", 2)); + assertMatches("Out of order", expected, actual); + } + + public void testMatchesIterableInOrderWithComparator() { + List actual = asList(item("foo", 1), item("bar", 2)); + Matcher> expected = containsInAnyOrder(comparator, item("key", 1), item("key", 2)); + assertMatches("In order", expected, actual); + } + + public void testDoesNotMatchIfOneOfMultipleElementsMismatchesWithComparator() { + List actual = asList(item("foo", 1), item("bar", 2), item("baz", 4)); + Matcher> expected = containsInAnyOrder(comparator, item("key", 1), item("key", 2), item("key", 3)); + assertMismatchDescription("not matched: ", expected, actual); + } + + public void testDoesNotMatchIfThereAreMoreElementsThanMatchersWithComparator() { + List actual = asList(item("foo", 1), item("bar", 2), item("baz", 3)); + final Matcher> expected = containsInAnyOrder(comparator, item("key", 1), item("key", 3)); + assertMismatchDescription("not matched: ", expected, actual); + } + + public void testDoesNotMatchIfThereAreMoreMatchersThanElementsWithComparator() { + List actual = asList(item("foo", 1), item("bar", 2)); + Matcher> expected = containsInAnyOrder(comparator, item("key", 1), item("key", 2), item("key", 3)); + assertMismatchDescription("no item matches: a value equal to when compared by in [, ]", expected, actual); + } + + public void testHasAReadableDescriptionWithComparator() { + assertDescription("iterable with items [a value equal to when compared by , a value equal to when compared by ] in any order", + containsInAnyOrder(comparator, item("foo", 1), item("bar", 2))); + } }