Skip to content

Commit

Permalink
Make containsAllOf() not greedy.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=87944280
  • Loading branch information
kluever authored and cpovirk committed Mar 6, 2015
1 parent dc0a910 commit 7c43b0a
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 46 deletions.
54 changes: 35 additions & 19 deletions core/src/main/java/com/google/common/truth/IterableSubject.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,29 +185,45 @@ public Ordered containsAllIn(Iterable<?> expected) {
return containsAll("contains all elements in", expected);
}

private Ordered containsAll(String failVerb, Iterable<?> expected) {
// would really like to use ArrayDeque here, but it doesn't allow nulls
List<?> toRemove = Lists.newArrayList(expected);

boolean inOrder = true;

// remove each item in the subject, as many times as it occurs in the subject.
for (Object item : getSubject()) {
int index = toRemove.indexOf(item);
if (index != -1) {
toRemove.remove(index);

// in order as long as the next expected item in the subject is the first item remaining
// in toRemove
inOrder &= (index == 0);
private Ordered containsAll(String failVerb, Iterable<?> expectedIterable) {
List<?> actual = Lists.newLinkedList(getSubject());
List<?> expected = Lists.newArrayList(expectedIterable);

List<Object> missing = Lists.newArrayList();
List<Object> actualNotInOrder = Lists.newArrayList();

boolean ordered = true;
// step through the expected elements...
for (Object e : expected) {
int index = actual.indexOf(e);
if (index != -1) { // if we find the element in the actual list...
// drain all the elements that come before that element into actualNotInOrder
moveElements(actual, actualNotInOrder, index);
// and remove the element from the actual list
actual.remove(0);
} else { // otherwise try removing it from actualNotInOrder...
if (actualNotInOrder.remove(e)) { // if it was in actualNotInOrder, we're not in order
ordered = false;
} else { // if it's not in actualNotInOrder, we're missing an expected element
missing.add(e);
}
}
}

if (!toRemove.isEmpty()) {
failWithBadResults(failVerb, expected, "is missing", countDuplicates(toRemove));
// if we have any missing expected elements, fail
if (!missing.isEmpty()) {
failWithBadResults(failVerb, expected, "is missing", countDuplicates(missing));
}
return ordered ? IN_ORDER : new NotInOrder("contains all elements in order", expected);
}

return inOrder ? IN_ORDER : new NotInOrder("contains all elements in order", expected);
/**
* Removes at most the given number of available elements from the input list
* and adds them to the given output collection.
*/
private static void moveElements(List<?> input, Collection<Object> output, int maxElements) {
for (int i = 0; i < maxElements; i++) {
output.add(input.remove(0));
}
}

/**
Expand Down
59 changes: 32 additions & 27 deletions core/src/test/java/com/google/common/truth/IterableTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.junit.Assert.fail;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import org.junit.Test;
Expand Down Expand Up @@ -199,6 +200,28 @@ public class IterableTest {
}
}

@Test public void iterableContainsAllOfWithExtras() {
try {
assertThat(iterable("y", "x")).containsAllOf("x", "y", "z");
} catch (AssertionError expected) {
assertThat(expected).hasMessage(
"Not true that <[y, x]> contains all of <[x, y, z]>. It is missing <[z]>");
return;
}
fail("Should have thrown.");
}

@Test public void iterableContainsAllOfWithExtraCopiesOfOutOfOrder() {
try {
assertThat(iterable("y", "x")).containsAllOf("x", "y", "y");
} catch (AssertionError expected) {
assertThat(expected).hasMessage(
"Not true that <[y, x]> contains all of <[x, y, y]>. It is missing <[y]>");
return;
}
fail("Should have thrown.");
}

@Test public void iterableContainsAllOfWithDuplicatesFailure() {
try {
assertThat(iterable(1, 2, 3)).containsAllOf(1, 2, 2, 2, 3, 4);
Expand Down Expand Up @@ -245,30 +268,8 @@ public class IterableTest {
assertThat(iterable(3, 1, 4, 1, 5)).containsAllOf(3, 1, 5).inOrder();
assertThat(iterable("x", "y", "y", "z")).containsAllOf("x", "y", "z").inOrder();
assertThat(iterable("x", "x", "y", "z")).containsAllOf("x", "y", "z").inOrder();
}

@Test public void iterableContainsAllOfInOrderWithGaps_badFailure1() {
try {
// This test probably should pass.
assertThat(iterable("z", "x", "y", "z")).containsAllOf("x", "y", "z").inOrder();
} catch (AssertionError e) {
assertThat(e).hasMessage(
"Not true that <[z, x, y, z]> contains all elements in order <[x, y, z]>");
return;
}
fail("Should have thrown.");
}

@Test public void iterableContainsAllOfInOrderWithGaps_badFailure2() {
try {
// This test probably should pass.
assertThat(iterable("x", "x", "y", "z", "x")).containsAllOf("x", "y", "z", "x").inOrder();
} catch (AssertionError e) {
assertThat(e).hasMessage(
"Not true that <[x, x, y, z, x]> contains all elements in order <[x, y, z, x]>");
return;
}
fail("Should have thrown.");
assertThat(iterable("z", "x", "y", "z")).containsAllOf("x", "y", "z").inOrder();
assertThat(iterable("x", "x", "y", "z", "x")).containsAllOf("x", "y", "z", "x").inOrder();
}

@Test public void iterableContainsAllOfInOrderWithNull() {
Expand All @@ -287,14 +288,18 @@ public class IterableTest {
}

@Test public void iterableContainsAllOfInOrderWithOneShotIterable() {
final Iterator<Object> iterator = iterable(2, 1, null, 4, "a", 3, "b").iterator();
Iterable<Object> iterable = new Iterable<Object>() {
final Iterable<Object> iterable = iterable(2, 1, null, 4, "a", 3, "b");
final Iterator<Object> iterator = iterable.iterator();
Iterable<Object> oneShot = new Iterable<Object>() {
@Override public Iterator<Object> iterator() {
return iterator;
}
@Override public String toString() {
return Iterables.toString(iterable);
}
};

assertThat(iterable).containsAllOf(1, null, 3).inOrder();
assertThat(oneShot).containsAllOf(1, null, 3).inOrder();
}

@Test public void iterableContainsAllOfInOrderWithOneShotIterableWrongOrder() {
Expand Down

0 comments on commit 7c43b0a

Please sign in to comment.