From 20141e9ba955ec94374740f1f5fb801f9b818b6b Mon Sep 17 00:00:00 2001 From: XxxXxxXxx233 Date: Sun, 25 Apr 2021 10:24:58 +0800 Subject: [PATCH] Add IsIterableIntersecting --- .../src/main/java/org/hamcrest/Matchers.java | 56 +++++++ .../collection/IsIterableIntersecting.java | 138 ++++++++++++++++++ .../IsIterableIntersectingTest.java | 46 ++++++ 3 files changed, 240 insertions(+) create mode 100644 hamcrest/src/main/java/org/hamcrest/collection/IsIterableIntersecting.java create mode 100644 hamcrest/src/test/java/org/hamcrest/collection/IsIterableIntersectingTest.java diff --git a/hamcrest/src/main/java/org/hamcrest/Matchers.java b/hamcrest/src/main/java/org/hamcrest/Matchers.java index d4f543ea..4e505c22 100644 --- a/hamcrest/src/main/java/org/hamcrest/Matchers.java +++ b/hamcrest/src/main/java/org/hamcrest/Matchers.java @@ -1712,5 +1712,61 @@ public static org.hamcrest.Matcher hasXPath(java.lang.String x return org.hamcrest.xml.HasXPath.hasXPath(xPath, namespaceContext); } + /** + *

+ * 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. For a positive match, the examined + * iterable must have have at least one item in common with specified matchers. + *

+ *

+ * For example: + *

+ *
assertThat("Intersect with", Arrays.asList(1, 2, 3, 4), intersectWith(equalTo(2), equalTo(3)));
+ * + * @param itemMatchers + * the matchers that must be satisfied by the items provided by an examined {@link Iterable} in the same relative order + */ + @SafeVarargs + public static org.hamcrest.Matcher> intersectWith(org.hamcrest.Matcher... itemMatchers) { + return org.hamcrest.collection.IsIterableIntersecting.intersectWith(itemMatchers); + } + + /** + *

+ * 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. For a positive match, the examined + * iterable must have have at least one item in common with specified matchers. + *

+ *

+ * For example: + *

+ *
assertThat("Intersect with", Arrays.asList(1, 2, 3, 4), intersectWith(2, 3));
+ * + * @param items + * the items that must equal the items provided by an examined {@link Iterable} in any order + */ + @SafeVarargs + public static org.hamcrest.Matcher> intersectWith(T... items) { + return org.hamcrest.collection.IsIterableIntersecting.intersectWith(items); + } + + /** + *

+ * 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. For a positive match, the examined + * iterable must have have at least one item in common with specified matchers. + *

+ *

+ * For example: + *

+ *
assertThat("Intersect with", Arrays.asList(1, 2, 3, 4), intersectWith(Arrays.asList(equalTo(2), equalTo(3))));
+ * + * @param itemMatchers + * a list of matchers, each of which must be satisfied by the items provided by + * an examined {@link Iterable} in the same relative order + */ + public static org.hamcrest.Matcher> intersectWith(java.util.Collection> itemMatchers) { + return org.hamcrest.collection.IsIterableIntersecting.intersectWith(itemMatchers); + } } diff --git a/hamcrest/src/main/java/org/hamcrest/collection/IsIterableIntersecting.java b/hamcrest/src/main/java/org/hamcrest/collection/IsIterableIntersecting.java new file mode 100644 index 00000000..33538621 --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/collection/IsIterableIntersecting.java @@ -0,0 +1,138 @@ +package org.hamcrest.collection; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.hamcrest.core.IsEqual.equalTo; + +public class IsIterableIntersecting extends TypeSafeDiagnosingMatcher> { + private final Collection> matchers; + + public IsIterableIntersecting(Collection> matchers) { + this.matchers = matchers; + } + + @Override + protected boolean matchesSafely(Iterable items, Description mismatchDescription) { + final Matching matching = new Matching<>(matchers, mismatchDescription); + for (T item : items) { + if (matching.matches(item)) { + return true; + } + } + + return matching.isFinished(items); + } + + @Override + public void describeTo(Description description) { + description.appendText("iterable with items ") + .appendList("[", ", ", "]", matchers) + .appendText(" intersecting"); + } + + private static class Matching { + private final Collection> matchers; + private final Description mismatchDescription; + + private Matching(Collection> matchers, Description mismatchDescription) { + this.matchers = new ArrayList<>(matchers); + this.mismatchDescription = mismatchDescription; + } + + public boolean matches(S item) { + if (matchers.isEmpty()) { + mismatchDescription.appendText("no match for: ").appendValue(item); + return false; + } + return isMatched(item); + } + + public boolean isFinished(Iterable items) { + if (matchers.isEmpty()) { + return true; + } + mismatchDescription + .appendText("no item intersects: ").appendList("", ", ", "", matchers) + .appendText(" with ").appendValueList("[", ", ", "]", items); + return false; + } + + private boolean isMatched(S item) { + for (Matcher matcher : matchers) { + if (matcher.matches(item)) { + matchers.clear(); + return true; + } + } + return false; + } + } + + /** + *

+ * 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. For a positive match, the examined + * iterable must have have at least one item in common with specified matchers. + *

+ *

+ * For example: + *

+ *
assertThat("Intersect with", Arrays.asList(1, 2, 3, 4), intersectWith(equalTo(2), equalTo(3)));
+ * + * @param itemMatchers + * the matchers that must be satisfied by the items provided by an examined {@link Iterable} in the same relative order + */ + @SafeVarargs + public static Matcher> intersectWith(Matcher... itemMatchers) { + return intersectWith(Arrays.asList(itemMatchers)); + } + + /** + *

+ * 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. For a positive match, the examined + * iterable must have have at least one item in common with specified matchers. + *

+ *

+ * For example: + *

+ *
assertThat("Intersect with", Arrays.asList(1, 2, 3, 4), intersectWith(2, 3));
+ * + * @param items + * the items that must equal the items provided by an examined {@link Iterable} in any order + */ + @SafeVarargs + public static Matcher> intersectWith(T... items) { + List> matchers = new ArrayList<>(); + for (T item : items) { + matchers.add(equalTo(item)); + } + return new IsIterableIntersecting<>(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. For a positive match, the examined + * iterable must have have at least one item in common with specified matchers. + *

+ *

+ * For example: + *

+ *
assertThat("Intersect with", Arrays.asList(1, 2, 3, 4), intersectWith(Arrays.asList(equalTo(2), equalTo(3))));
+ * + * @param itemMatchers + * a list of matchers, each of which must be satisfied by the items provided by + * an examined {@link Iterable} in the same relative order + */ + public static Matcher> intersectWith(Collection> itemMatchers) { + return new IsIterableIntersecting<>(itemMatchers); + } +} diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableIntersectingTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableIntersectingTest.java new file mode 100644 index 00000000..17fc3d04 --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableIntersectingTest.java @@ -0,0 +1,46 @@ +package org.hamcrest.collection; + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; + +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.Arrays; + +import static org.hamcrest.collection.IsIterableIntersecting.intersectWith; + +public class IsIterableIntersectingTest extends AbstractMatcherTest { + @Override + protected Matcher createMatcher() { return intersectWith(1, 2); } + + public void testEmptyNotIntersect() { + assertMismatchDescription("no item intersects: <1>, <2> with []", intersectWith(1, 2), Collections.emptyList()); + } + + public void testNotIntersect() { + assertMismatchDescription("no item intersects: <1>, <2>, <3> with [<4>, <5>]", intersectWith(1, 2, 3), Arrays.asList(4, 5)); + } + + public void testSingleIntersect() { + assertMatches("Single intersect", intersectWith(1), Collections.singletonList(1)); + } + + public void testPartlyIntersect() { + assertMatches("Partly intersect", intersectWith(1, 2, 3), Arrays.asList(4, 2, 5)); + } + + public void testFullyIntersect() { + assertMatches("Fully intersect", intersectWith(1, 2, 3), Arrays.asList(3, 4, 2, 1, 5)); + assertMatches("Fully intersect", intersectWith(1, 2, 3), Arrays.asList(2, 3)); + assertMatches("Fully intersect", intersectWith(1, 2, 3), Arrays.asList(1, 2, 3)); + } + + public void testDuplicateIntersect() { + assertMatches("Duplicate intersect", intersectWith(1, 2, 3), Arrays.asList(2, 2, 2, 4)); + assertMatches("Duplicate intersect", intersectWith(1, 2, 2, 2, 3), Arrays.asList(2, 4, 4, 4)); + } + + public void testHasAReadableDescription() { + assertDescription("iterable with items [<1>, <2>] intersecting", intersectWith(1, 2)); + } +}