Skip to content

Commit 446fd84

Browse files
committed
Merged PR hamcrest#79 - New matcher HasSubsequence and changed code slightly to match "Hamcrest 7" style.
2 parents 944a628 + 1ee2cd8 commit 446fd84

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.hamcrest.collection;
2+
3+
import org.hamcrest.TypeSafeDiagnosingMatcher;
4+
import org.hamcrest.Description;
5+
import org.hamcrest.Factory;
6+
import org.hamcrest.Matcher;
7+
import static org.hamcrest.core.IsEqual.equalTo;
8+
9+
import java.util.Arrays;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.Collection;
13+
14+
/**
15+
* Matches if a collection contains another collection's items in
16+
* consecutive order anywhere inside it.
17+
*/
18+
public class HasSubsequence<T>
19+
extends TypeSafeDiagnosingMatcher<Collection<? extends T>> {
20+
private final List<Matcher<? super T>> matchers;
21+
22+
public HasSubsequence(List<Matcher<? super T>> matchers) {
23+
this.matchers = matchers;
24+
}
25+
26+
@Override
27+
public boolean matchesSafely(
28+
Collection<? extends T> subsequenceToMatch,
29+
Description mismatchDescription) {
30+
List<? extends T> subsequenceToMatchList = new ArrayList<T>(subsequenceToMatch);
31+
32+
for (int i = 0; i <= subsequenceToMatchList.size() - matchers.size(); i++) {
33+
boolean allMatchersMatched = true;
34+
for (int j = 0; j < matchers.size(); j++) {
35+
Matcher<? super T> matcher = matchers.get(j);
36+
if (!matcher.matches(subsequenceToMatchList.get(i + j))) {
37+
allMatchersMatched = false;
38+
break;
39+
}
40+
}
41+
if (allMatchersMatched) {
42+
return true;
43+
}
44+
}
45+
46+
mismatchDescription
47+
.appendText("could not find subsequence inside collection ")
48+
.appendValueList("[", ", ", "]", subsequenceToMatch);
49+
return false;
50+
}
51+
52+
@Override
53+
public void describeTo(Description description) {
54+
description
55+
.appendText("collection contains subsequence matching ")
56+
.appendList("[", ", ", "]", matchers);
57+
}
58+
59+
@Factory
60+
public static <T> Matcher<Collection<? extends T>> hasSubsequence(
61+
List<Matcher<? super T>> matchers) {
62+
return new HasSubsequence<T>(matchers);
63+
}
64+
65+
@Factory
66+
public static <T> Matcher<Collection<? extends T>> hasSubsequence(
67+
Matcher<? super T>... matchers) {
68+
return hasSubsequence(Arrays.asList(matchers));
69+
}
70+
71+
@Factory
72+
public static <T> Matcher<Collection<? extends T>> hasSubsequence(
73+
Iterable<? extends T> items) {
74+
List<Matcher<? super T>> matchers = new ArrayList<Matcher<? super T>>();
75+
for (Object item : items) {
76+
matchers.add(equalTo(item));
77+
}
78+
return new HasSubsequence<T>(matchers);
79+
}
80+
81+
@Factory
82+
public static <T> Matcher<Collection<? extends T>> hasSubsequence(T... elements) {
83+
return hasSubsequence(Arrays.asList(elements));
84+
}
85+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.hamcrest.collection;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.List;
6+
7+
import org.hamcrest.AbstractMatcherTest;
8+
import org.hamcrest.FeatureMatcher;
9+
import org.hamcrest.Matcher;
10+
11+
import static java.util.Arrays.asList;
12+
import static org.hamcrest.collection.HasSubsequence.hasSubsequence;
13+
import static org.hamcrest.core.IsEqual.equalTo;
14+
15+
@SuppressWarnings("unchecked")
16+
public class HasSubsequenceTest extends AbstractMatcherTest {
17+
private final Matcher<Collection<? extends WithValue>> contains123 = hasSubsequence(value(1), value(2), value(3));
18+
19+
@Override
20+
protected Matcher<?> createMatcher() {
21+
return hasSubsequence(1, 2);
22+
}
23+
24+
public void testMatchingSingleItemCollection() throws Exception {
25+
assertMatches("Single item iterable", hasSubsequence(1), asList(1));
26+
}
27+
28+
public void testMatchingMultipleItemCollection() throws Exception {
29+
assertMatches("Multiple item iterable", hasSubsequence(1, 2, 3), asList(1, 2, 3));
30+
}
31+
32+
public void testMatchesWithMoreElementsThanExpectedAtBeginning() throws Exception {
33+
assertMatches("More elements at beginning", hasSubsequence(2, 3, 4), asList(1, 2, 3, 4));
34+
}
35+
36+
public void testMatchesWithMoreElementsThanExpectedAtEnd() throws Exception {
37+
assertMatches("More elements at end", hasSubsequence(1, 2, 3), asList(1, 2, 3, 4));
38+
}
39+
40+
public void testDoesNotMatchWithMoreElementsThanExpectedInBetween() throws Exception {
41+
assertMismatchDescription("could not find subsequence inside collection [<1>, <2>, <3>]", hasSubsequence(1, 3), asList(1, 2, 3));
42+
}
43+
44+
public void testMatchesSubSection() throws Exception {
45+
assertMatches("Sub section of iterable", hasSubsequence(2, 3), asList(1, 2, 3, 4));
46+
}
47+
48+
public void testDoesNotMatchWithFewerElementsThanExpected() throws Exception {
49+
List<WithValue> valueList = asList(make(1), make(2));
50+
assertMismatchDescription("could not find subsequence inside collection [<WithValue 1>, <WithValue 2>]", contains123, valueList);
51+
}
52+
53+
public void testDoesNotMatchIfSingleItemNotFound() throws Exception {
54+
assertMismatchDescription("could not find subsequence inside collection [<WithValue 3>]", hasSubsequence(value(4)), asList(make(3)));
55+
}
56+
57+
public void testDoesNotMatchIfOneOfMultipleItemsNotFound() throws Exception {
58+
assertMismatchDescription("could not find subsequence inside collection [<WithValue 1>, <WithValue 2>, <WithValue 4>]", contains123, asList(make(1), make(2), make(4)));
59+
}
60+
61+
public void testDoesNotMatchEmptyCollection() throws Exception {
62+
assertMismatchDescription("could not find subsequence inside collection []", hasSubsequence(value(4)), new ArrayList<WithValue>());
63+
}
64+
65+
public void testHasAReadableDescription() {
66+
assertDescription("collection contains subsequence matching [<1>, <2>]", hasSubsequence(1, 2));
67+
}
68+
69+
public static class WithValue {
70+
private final int value;
71+
public WithValue(int value) { this.value = value; }
72+
public int getValue() { return value; }
73+
@Override public String toString() { return "WithValue " + value; }
74+
}
75+
76+
public static WithValue make(int value) {
77+
return new WithValue(value);
78+
}
79+
80+
public static Matcher<WithValue> value(int value) {
81+
return new FeatureMatcher<WithValue, Integer>(equalTo(value), "value with", "value") {
82+
@Override protected Integer featureValueOf(WithValue actual) { return actual.getValue(); }
83+
};
84+
}
85+
}

0 commit comments

Comments
 (0)