Skip to content

Commit

Permalink
More changes for singly-traversable 'contains' assertions (closes #1941)
Browse files Browse the repository at this point in the history
  • Loading branch information
rpolton authored and scordio committed Aug 28, 2020
1 parent 7279cd7 commit a06f16c
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 41 deletions.
14 changes: 8 additions & 6 deletions src/main/java/org/assertj/core/internal/Iterables.java
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,10 @@ public void assertHasSameSizeAs(AssertionInfo info, Iterable<?> actual, Iterable
* @throws AssertionError if the given {@code Iterable} does not contain the given values.
*/
public void assertContains(AssertionInfo info, Iterable<?> actual, Object[] values) {
if (commonCheckThatIterableAssertionSucceeds(info, actual, values)) return;
final List<?> actualAsList = newArrayList(actual);
if (commonCheckThatIterableAssertionSucceeds(info, actualAsList, values)) return;
// check for elements in values that are missing in actual.
assertIterableContainsGivenValues(actual, values, info);
assertIterableContainsGivenValues(actualAsList, values, info);
}

private void assertIterableContainsGivenValues(Iterable<?> actual, Object[] values, AssertionInfo info) {
Expand Down Expand Up @@ -370,14 +371,15 @@ private void iterablesRemove(Iterable<?> actual, Object value) {
* {@code Iterable} contains values that are not in the given array.
*/
public void assertContainsOnly(AssertionInfo info, Iterable<?> actual, Object[] expectedValues) {
if (commonCheckThatIterableAssertionSucceeds(info, actual, expectedValues)) return;
final List<?> actualAsList = newArrayList(actual);
if (commonCheckThatIterableAssertionSucceeds(info, actualAsList, expectedValues)) return;

// after the for loop, unexpected = expectedValues - actual
List<Object> unexpectedValues = newArrayList(actual);
List<Object> unexpectedValues = newArrayList(actualAsList);
// after the for loop, missing = actual - expectedValues
List<Object> missingValues = newArrayList(expectedValues);
for (Object expected : expectedValues) {
if (iterableContains(actual, expected)) {
if (iterableContains(actualAsList, expected)) {
// since expected was found in actual:
// -- it does not belong to the missing elements
iterablesRemove(missingValues, expected);
Expand All @@ -387,7 +389,7 @@ public void assertContainsOnly(AssertionInfo info, Iterable<?> actual, Object[]
}

if (!unexpectedValues.isEmpty() || !missingValues.isEmpty()) {
throw failures.failure(info, shouldContainOnly(actual, expectedValues,
throw failures.failure(info, shouldContainOnly(actualAsList, expectedValues,
missingValues, unexpectedValues,
comparisonStrategy));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.assertj.core.error.ShouldContainExactly.elementsDifferAtIndex;
import static org.assertj.core.error.ShouldContainExactly.shouldContainExactly;
import static org.assertj.core.internal.ErrorMessages.valuesToLookForIsNull;
import static org.assertj.core.internal.iterables.SinglyIterableFactory.createSinglyIterable;
import static org.assertj.core.test.ObjectArrays.emptyArray;
import static org.assertj.core.test.TestData.someInfo;
import static org.assertj.core.util.Arrays.array;
Expand All @@ -28,7 +29,6 @@
import static org.assertj.core.util.Lists.newArrayList;
import static org.mockito.Mockito.verify;

import java.util.Iterator;
import java.util.List;

import org.assertj.core.api.AssertionInfo;
Expand All @@ -53,32 +53,6 @@ void should_pass_if_nonrestartable_actual_contains_exactly_given_values() {
iterables.assertContainsExactly(someInfo(), createSinglyIterable(actual), array("Luke", "Yoda", "Leia"));
}

static Iterable<String> createSinglyIterable(final List<String> values) {
// can't use Iterable<> for anonymous class in java 8
return new Iterable<String>() {
private boolean isIteratorCreated = false;

@Override
public Iterator<String> iterator() {
if (isIteratorCreated) throw new IllegalArgumentException("Cannot create two iterators on a singly-iterable sequence");
isIteratorCreated = true;
return new Iterator<String>() {
private final Iterator<String> listIterator = values.iterator();

@Override
public boolean hasNext() {
return listIterator.hasNext();
}

@Override
public String next() {
return listIterator.next();
}
};
}
};
}

@Test
void should_pass_if_actual_contains_given_values_exactly_with_null_elements() {
iterables.assertContainsExactly(someInfo(), actual, array("Luke", "Yoda", "Leia"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.assertj.core.error.ShouldContainOnly.shouldContainOnly;
import static org.assertj.core.internal.ErrorMessages.valuesToLookForIsNull;
import static org.assertj.core.internal.iterables.SinglyIterableFactory.createSinglyIterable;
import static org.assertj.core.test.ObjectArrays.emptyArray;
import static org.assertj.core.test.TestData.someInfo;
import static org.assertj.core.util.Arrays.array;
Expand All @@ -35,7 +36,7 @@

/**
* Tests for <code>{@link Iterables#assertContainsOnly(AssertionInfo, Collection, Object[])}</code>.
*
*
* @author Alex Ruiz
* @author Joel Costigliola
*/
Expand Down Expand Up @@ -77,7 +78,8 @@ void should_pass_if_actual_and_given_values_are_empty() {

@Test
void should_fail_if_array_of_values_to_look_for_is_empty_and_actual_is_not() {
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> iterables.assertContainsOnly(someInfo(), actual, emptyArray()));
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> iterables.assertContainsOnly(someInfo(), actual,
emptyArray()));
}

@Test
Expand All @@ -88,7 +90,8 @@ void should_throw_error_if_array_of_values_to_look_for_is_null() {

@Test
void should_fail_if_actual_is_null() {
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> iterables.assertContainsOnly(someInfo(), null, array("Yoda")))
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> iterables.assertContainsOnly(someInfo(), null,
array("Yoda")))
.withMessage(actualIsNull());
}

Expand Down Expand Up @@ -163,11 +166,16 @@ void should_fail_if_actual_does_not_contain_given_values_only_according_to_custo
AssertionInfo info = someInfo();
Object[] expected = { "Luke", "Yoda", "Han" };

Throwable error = catchThrowable(() -> iterablesWithCaseInsensitiveComparisonStrategy.assertContainsOnly(info, actual, expected));
Throwable error = catchThrowable(() -> iterablesWithCaseInsensitiveComparisonStrategy.assertContainsOnly(info, actual,
expected));

assertThat(error).isInstanceOf(AssertionError.class);
verify(failures).failure(info, shouldContainOnly(actual, expected, newArrayList("Han"), newArrayList("Leia"),
comparisonStrategy));
}

@Test
void should_pass_if_nonrestartable_actual_contains_only_given_values() {
iterables.assertContainsOnly(someInfo(), createSinglyIterable(actual), array("Luke", "Yoda", "Leia"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.assertj.core.error.ShouldContainSequence.shouldContainSequence;
import static org.assertj.core.internal.ErrorMessages.valuesToLookForIsNull;
import static org.assertj.core.internal.iterables.Iterables_assertContainsExactly_Test.createSinglyIterable;
import static org.assertj.core.test.ObjectArrays.emptyArray;
import static org.assertj.core.test.TestData.someInfo;
import static org.assertj.core.util.Arrays.array;
Expand Down Expand Up @@ -146,7 +145,7 @@ void should_pass_if_actual_is_an_infinite_sequence_and_contains_sequence() {

@Test
void should_pass_if_actual_is_a_singly_traversable_sequence_and_contains_sequence() {
Iterable<String> actual = createSinglyIterable(list("Leia", "Luke", "Yoda", "Obi-Wan"));
Iterable<String> actual = SinglyIterableFactory.createSinglyIterable(list("Leia", "Luke", "Yoda", "Obi-Wan"));
iterables.assertContainsSequence(someInfo(), actual, array("Leia", "Luke", "Yoda", "Obi-Wan"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.assertj.core.error.ShouldContain.shouldContain;
import static org.assertj.core.internal.ErrorMessages.valuesToLookForIsNull;
import static org.assertj.core.internal.iterables.SinglyIterableFactory.createSinglyIterable;
import static org.assertj.core.test.ObjectArrays.emptyArray;
import static org.assertj.core.test.TestData.someInfo;
import static org.assertj.core.util.Arrays.array;
Expand All @@ -35,7 +36,7 @@

/**
* Tests for <code>{@link Iterables#assertContains(AssertionInfo, Collection, Object[])}</code>.
*
*
* @author Alex Ruiz
* @author Joel Costigliola
*/
Expand Down Expand Up @@ -72,7 +73,12 @@ void should_pass_if_actual_and_given_values_are_empty() {
actual.clear();
iterables.assertContains(someInfo(), actual, array());
}


@Test
void should_pass_if_nonrestartable_actual_contains_given_values() {
iterables.assertContains(someInfo(), createSinglyIterable(actual), array("Luke", "Yoda", "Leia"));
}

@Test
void should_fail_if_array_of_values_to_look_for_is_empty_and_actual_is_not() {
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> iterables.assertContains(someInfo(), actual, emptyArray()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright 2012-2020 the original author or authors.
*/
package org.assertj.core.internal.iterables;

import java.util.Iterator;
import java.util.List;

class SinglyIterableFactory {

static Iterable<String> createSinglyIterable(final List<String> values) {
// can't use Iterable<> for anonymous class in java 8
return new Iterable<String>() {
private boolean isIteratorCreated = false;

@Override
public Iterator<String> iterator() {
if (isIteratorCreated) throw new IllegalArgumentException("Cannot create two iterators on a singly-iterable sequence");
isIteratorCreated = true;
return new Iterator<String>() {
private final Iterator<String> listIterator = values.iterator();

@Override
public boolean hasNext() {
return listIterator.hasNext();
}

@Override
public String next() {
return listIterator.next();
}
};
}
};
}

}

0 comments on commit a06f16c

Please sign in to comment.