Skip to content

Commit 7698752

Browse files
author
smgfreeman
committed
Better error reporting for iterable containing in order.
Changed matchers collection to a list
1 parent 6472f4e commit 7698752

File tree

3 files changed

+97
-34
lines changed

3 files changed

+97
-34
lines changed

hamcrest-library/src/main/java/org/hamcrest/collection/IsArrayContainingInOrder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class IsArrayContainingInOrder<E> extends TypeSafeMatcher<E[]> {
1616
private final Collection<Matcher<? super E>> matchers;
1717
private final IsIterableContainingInOrder<E> iterableMatcher;
1818

19-
public IsArrayContainingInOrder(Collection<Matcher<? super E>> matchers) {
19+
public IsArrayContainingInOrder(List<Matcher<? super E>> matchers) {
2020
this.iterableMatcher = new IsIterableContainingInOrder<E>(matchers);
2121
this.matchers = matchers;
2222
}
@@ -100,7 +100,7 @@ public static <E> Matcher<E[]> arrayContaining(Matcher<? super E>... matchers) {
100100
}
101101

102102
@Factory
103-
public static <E> Matcher<E[]> arrayContaining(Collection<Matcher<? super E>> matchers) {
103+
public static <E> Matcher<E[]> arrayContaining(List<Matcher<? super E>> matchers) {
104104
return new IsArrayContainingInOrder<E>(matchers);
105105
}
106106

hamcrest-library/src/main/java/org/hamcrest/collection/IsIterableContainingInOrder.java

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,82 @@
44

55
import java.util.ArrayList;
66
import java.util.Arrays;
7-
import java.util.Collection;
8-
import java.util.Iterator;
97
import java.util.List;
108

119
import org.hamcrest.Description;
1210
import org.hamcrest.Factory;
1311
import org.hamcrest.Matcher;
14-
import org.hamcrest.TypeSafeMatcher;
12+
import org.hamcrest.TypeSafeDiagnosingMatcher;
1513

16-
public class IsIterableContainingInOrder<E> extends TypeSafeMatcher<Iterable<E>> {
17-
private final Collection<Matcher<? super E>> matchers;
14+
public class IsIterableContainingInOrder<E> extends TypeSafeDiagnosingMatcher<Iterable<E>> {
15+
private final List<Matcher<? super E>> matchers;
1816

19-
public IsIterableContainingInOrder(Collection<Matcher<? super E>> contents) {
20-
if (contents.isEmpty()) {
21-
throw new IllegalArgumentException("Should specify at least one expected element");
22-
}
23-
this.matchers = contents;
17+
public IsIterableContainingInOrder(List<Matcher<? super E>> matchers) {
18+
this.matchers = matchers;
2419
}
25-
20+
2621
@Override
27-
public boolean matchesSafely(Iterable<E> iterable) {
28-
Iterator<E> items = iterable.iterator();
29-
Iterator<Matcher<? super E>> matchersIterator = matchers.iterator();
30-
while (items.hasNext() && matchersIterator.hasNext()) {
31-
if (!matchersIterator.next().matches(items.next())) {
32-
return false;
33-
}
22+
protected boolean matchesSafely(Iterable<E> iterable, Description mismatchDescription) {
23+
MatchSeries<E> matching = new MatchSeries<E>(matchers, mismatchDescription);
24+
for (E item : iterable) {
25+
if (! matching.matches(item)) {
26+
return false;
3427
}
35-
return !items.hasNext() && !matchersIterator.hasNext();
36-
}
37-
38-
@Override
39-
public void describeMismatchSafely(Iterable<E> actual, Description mismatchDescription) {
40-
mismatchDescription.appendText("iterable was ").appendValueList("[", ", ", "]", actual);
28+
}
29+
30+
return matching.isFinished();
4131
}
4232

4333
public void describeTo(Description description) {
44-
description.appendText("iterable over ")
45-
.appendList("[", ", ", "]", matchers);
34+
description.appendText("iterable over ").appendList("[", ", ", "]", matchers);
35+
}
36+
37+
private static class MatchSeries<F> {
38+
public final List<Matcher<? super F>> matchers;
39+
private final Description mismatchDescription;
40+
public int nextMatchIx = 0;
41+
42+
public MatchSeries(List<Matcher<? super F>> matchers, Description mismatchDescription) {
43+
this.mismatchDescription = mismatchDescription;
44+
if (matchers.isEmpty()) {
45+
throw new IllegalArgumentException("Should specify at least one expected element");
46+
}
47+
this.matchers = matchers;
48+
}
49+
50+
public boolean matches(F item) {
51+
return isNotSurplus(item) && isMatched(item);
52+
}
53+
54+
private boolean isMatched(F item) {
55+
Matcher<? super F> matcher = matchers.get(nextMatchIx);
56+
if (!matcher.matches(item)) {
57+
describeMismatch(matcher, item);
58+
return false;
59+
}
60+
nextMatchIx++;
61+
return true;
62+
}
63+
64+
private boolean isNotSurplus(F item) {
65+
if (matchers.size() <= nextMatchIx) {
66+
mismatchDescription.appendText("surplus item: ").appendValue(item);
67+
return false;
68+
}
69+
return true;
70+
}
71+
72+
public boolean isFinished() {
73+
if (nextMatchIx < matchers.size()) {
74+
mismatchDescription.appendText("surplus matcher: ").appendDescriptionOf(matchers.get(nextMatchIx));
75+
return false;
76+
}
77+
return true;
78+
}
79+
private void describeMismatch(Matcher<? super F> matcher, F item) {
80+
mismatchDescription.appendText("item " + nextMatchIx + ": ");
81+
matcher.describeMismatch(item, mismatchDescription);
82+
}
4683
}
4784

4885
@Factory
@@ -160,7 +197,7 @@ public static <E> Matcher<Iterable<E>> contains(Matcher<E> first, Matcher<? supe
160197
}
161198

162199
@Factory
163-
public static <E> Matcher<Iterable<E>> contains(Collection<Matcher<? super E>> contents) {
200+
public static <E> Matcher<Iterable<E>> contains(List<Matcher<? super E>> contents) {
164201
return new IsIterableContainingInOrder<E>(contents);
165202
}
166203
}

hamcrest-unit-test/src/main/java/org/hamcrest/collection/IsIterableContainingInOrderTest.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package org.hamcrest.collection;
22

33
import static java.util.Arrays.asList;
4+
import static org.hamcrest.Matchers.equalTo;
45
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
56

7+
import java.util.ArrayList;
8+
69
import org.hamcrest.AbstractMatcherTest;
10+
import org.hamcrest.FeatureMatcher;
711
import org.hamcrest.Matcher;
812

913
public class IsIterableContainingInOrderTest extends AbstractMatcherTest {
@@ -22,22 +26,44 @@ public void testMatchingMultipleItemIterable() throws Exception {
2226
}
2327

2428
public void testDoesNotMatchWithMoreElementsThanExpected() throws Exception {
25-
assertMismatchDescription("iterable was [<1>, <2>, <3>, <4>]", contains(1, 2, 3), asList(1, 2, 3, 4));
29+
assertMismatchDescription("surplus item: <4>", contains(1, 2, 3), asList(1, 2, 3, 4));
2630
}
2731

28-
public void testDoesNotMatchWithLessElementsThanExpected() throws Exception {
29-
assertMismatchDescription("iterable was [<1>, <2>]", contains(1, 2, 3), asList(1, 2));
32+
public void testDoesNotMatchWithFewerElementsThanExpected() throws Exception {
33+
assertMismatchDescription("surplus matcher: value with <3>", contains(value(1), value(2), value(3)), asList(make(1), make(2)));
3034
}
3135

36+
@SuppressWarnings("unchecked")
3237
public void testDoesNotMatchIfSingleItemMismatches() throws Exception {
33-
assertMismatchDescription("iterable was [<2>]", contains(3), asList(2));
38+
assertMismatchDescription("item 0: value was <3>", contains(value(4)), asList(make(3)));
3439
}
3540

3641
public void testDoesNotMatchIfOneOfMultipleItemsMismatch() throws Exception {
37-
assertMismatchDescription("iterable was [<1>, <2>, <4>]", contains(1, 2, 3), asList(1, 2, 4));
42+
assertMismatchDescription("item 2: value was <4>", contains(value(1), value(2), value(3)), asList(make(1), make(2), make(4)));
43+
}
44+
45+
@SuppressWarnings("unchecked")
46+
public void testDoesNotMatchEmptyIterable() throws Exception {
47+
assertMismatchDescription("surplus matcher: value with <4>", contains(value(4)), new ArrayList<WithValue>());
3848
}
3949

4050
public void testHasAReadableDescription() {
4151
assertDescription("iterable over [<1>, <2>]", contains(1, 2));
4252
}
53+
54+
public static class WithValue {
55+
private final int value;
56+
public WithValue(int value) { this.value = value; }
57+
public int getValue() { return value; }
58+
}
59+
60+
private static WithValue make(int value) {
61+
return new WithValue(value);
62+
}
63+
64+
private static Matcher<WithValue> value(int value) {
65+
return new FeatureMatcher<WithValue, Integer>(equalTo(value), "value with", "value") {
66+
@Override protected Integer featureValueOf(WithValue actual) { return actual.getValue(); }
67+
};
68+
}
4369
}

0 commit comments

Comments
 (0)