Skip to content

Commit

Permalink
Fixes #1052 by overloading endsWith assertion (#1074)
Browse files Browse the repository at this point in the history
The new `endsWith(T[])` allows empty arrays whereas the former
`endsWith(T...)` has been changed to `endsWith(T, T...)` to
explicitly prevent empty varargs.
  • Loading branch information
fbiville authored and joel-costigliola committed Oct 3, 2017
1 parent 68c54aa commit f3b181c
Show file tree
Hide file tree
Showing 22 changed files with 207 additions and 62 deletions.
11 changes: 10 additions & 1 deletion src/main/java/org/assertj/core/api/AbstractIterableAssert.java
Expand Up @@ -359,7 +359,16 @@ public SELF startsWith(@SuppressWarnings("unchecked") ELEMENT... sequence) {
* {@inheritDoc}
*/
@Override
public SELF endsWith(@SuppressWarnings("unchecked") ELEMENT... sequence) {
public SELF endsWith(ELEMENT first, @SuppressWarnings("unchecked") ELEMENT... rest) {
iterables.assertEndsWith(info, actual, first, rest);
return myself;
}

/**
* {@inheritDoc}
*/
@Override
public SELF endsWith(@SuppressWarnings("unchecked") ELEMENT[] sequence) {
iterables.assertEndsWith(info, actual, sequence);
return myself;
}
Expand Down
35 changes: 31 additions & 4 deletions src/main/java/org/assertj/core/api/AbstractObjectArrayAssert.java
Expand Up @@ -796,6 +796,33 @@ public SELF startsWith(@SuppressWarnings("unchecked") ELEMENT... sequence) {
return myself;
}

/**
* Verifies that the actual array ends with the given sequence of objects, without any other objects between them.
* Similar to <code>{@link #containsSequence(Object...)}</code>, but it also verifies that the last element in the
* sequence is also last element of the actual array.
* <p>
* Example :
* <pre><code class='java'> String[] abc = {"a", "b", "c"};
*
* // assertions will pass
* assertThat(abc).endsWith(new String[0])
* .endsWith(new String[] {"b", "c"});
*
* // assertion will fail
* assertThat(abc).endsWith(new String[] {"a"});</code></pre>
*
* @param sequence the sequence of objects to look for.
* @return this assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual group is {@code null}.
* @throws AssertionError if the actual group does not end with the given sequence of objects.
*/
@Override
public SELF endsWith(ELEMENT[] sequence) {
arrays.assertEndsWith(info, actual, sequence);
return myself;
}

/**
* Verifies that the actual array ends with the given sequence of objects, without any other objects between them.
* Similar to <code>{@link #containsSequence(Object...)}</code>, but it also verifies that the last element in the
Expand All @@ -810,16 +837,16 @@ public SELF startsWith(@SuppressWarnings("unchecked") ELEMENT... sequence) {
* // assertion will fail
* assertThat(abc).endsWith("a");</code></pre>
*
* @param sequence the sequence of objects to look for.
* @param first the first element of the end sequence of objects to look for.
* @param sequence the rest of the end sequence of objects to look for.
* @return this assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws IllegalArgumentException if the given argument is an empty array.
* @throws AssertionError if the actual group is {@code null}.
* @throws AssertionError if the actual group does not end with the given sequence of objects.
*/
@Override
public SELF endsWith(@SuppressWarnings("unchecked") ELEMENT... sequence) {
arrays.assertEndsWith(info, actual, sequence);
public SELF endsWith(ELEMENT first, @SuppressWarnings("unchecked") ELEMENT... sequence) {
arrays.assertEndsWith(info, actual, first, sequence);
return myself;
}

Expand Down
34 changes: 31 additions & 3 deletions src/main/java/org/assertj/core/api/AtomicReferenceArrayAssert.java
Expand Up @@ -22,6 +22,7 @@
import static org.assertj.core.internal.CommonValidations.checkSubsequenceIsNotNull;
import static org.assertj.core.util.Arrays.array;
import static org.assertj.core.util.Arrays.isArray;
import static org.assertj.core.util.Arrays.prepend;
import static org.assertj.core.util.IterableUtil.toArray;
import static org.assertj.core.util.Lists.newArrayList;
import static org.assertj.core.util.Preconditions.checkNotNull;
Expand Down Expand Up @@ -911,15 +912,42 @@ public AtomicReferenceArrayAssert<T> startsWith(@SuppressWarnings("unchecked") T
* // assertion will fail
* assertThat(abc).endsWith("a");</code></pre>
*
* @param sequence the sequence of objects to look for.
* @param first the first element of the end sequence of objects to look for.
* @param sequence the rest of the end sequence of objects to look for.
* @return this assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual AtomicReferenceArray is {@code null}.
* @throws AssertionError if the actual AtomicReferenceArray does not end with the given sequence of objects.
*/
@Override
public AtomicReferenceArrayAssert<T> endsWith(T first, @SuppressWarnings("unchecked") T... sequence) {
arrays.assertEndsWith(info, array, first, sequence);
return myself;
}

/**
* Verifies that the actual AtomicReferenceArray ends with the given sequence of objects, without any other objects between them.
* Similar to <code>{@link #containsSequence(Object...)}</code>, but it also verifies that the last element in the
* sequence is also last element of the actual AtomicReferenceArray.
* <p>
* Example :
* <pre><code class='java'> AtomicReferenceArray&lt;String&gt; abc = new AtomicReferenceArray&lt;&gt;(new String[]{"a", "b", "c"});
*
* // assertions will pass
* assertThat(abc).endsWith(new String[0])
* .endsWith(new String[] {"b", "c"});
*
* // assertion will fail
* assertThat(abc).endsWith(new String[] {"a"});</code></pre>
*
* @param sequence the (possibly empty) sequence of objects to look for.
* @return this assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws IllegalArgumentException if the given argument is an empty array.
* @throws AssertionError if the actual AtomicReferenceArray is {@code null}.
* @throws AssertionError if the actual AtomicReferenceArray does not end with the given sequence of objects.
*/
@Override
public AtomicReferenceArrayAssert<T> endsWith(@SuppressWarnings("unchecked") T... sequence) {
public AtomicReferenceArrayAssert<T> endsWith(T[] sequence) {
arrays.assertEndsWith(info, array, sequence);
return myself;
}
Expand Down
29 changes: 28 additions & 1 deletion src/main/java/org/assertj/core/api/ObjectEnumerableAssert.java
Expand Up @@ -28,6 +28,7 @@
* @author Mikhail Mazursky
* @author Joel Costigliola
* @author Nicolas François
* @author Florent Biville
*/
public interface ObjectEnumerableAssert<SELF extends ObjectEnumerableAssert<SELF, ELEMENT>, ELEMENT>
extends EnumerableAssert<SELF, ELEMENT> {
Expand Down Expand Up @@ -460,7 +461,33 @@ public interface ObjectEnumerableAssert<SELF extends ObjectEnumerableAssert<SELF
* @throws AssertionError if the actual group is {@code null}.
* @throws AssertionError if the actual group does not end with the given sequence of objects.
*/
SELF endsWith(@SuppressWarnings("unchecked") ELEMENT... sequence);
SELF endsWith(ELEMENT first, @SuppressWarnings("unchecked") ELEMENT... sequence);

/**
* Verifies that the actual group ends with the given sequence of objects, without any other objects between them.
* Similar to <code>{@link #containsSequence(Object...)}</code>, but it also verifies that the last element in the
* sequence is also last element of the actual group.
* <p>
* Example :
* <pre><code class='java'> // an Iterable is used in the example but it would also work with an array
* Iterable&lt;String&gt; abc = newArrayList("a", "b", "c");
*
* // assertions will pass
* assertThat(abc).endsWith(new String[0])
* .endsWith(new String[] {"c"})
* .endsWith(new String[] {"b", "c"});
*
* // assertions will fail
* assertThat(abc).endsWith(new String[] {"a"});
* assertThat(abc).endsWith(new String[] {"a", "b"});</code></pre>
*
* @param sequence the sequence of objects to look for.
* @return this assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual group is {@code null}.
* @throws AssertionError if the actual group does not end with the given sequence of objects.
*/
SELF endsWith(@SuppressWarnings("unchecked") ELEMENT[] sequence);

/**
* Verifies that the actual group contains at least a null element.
Expand Down
18 changes: 15 additions & 3 deletions src/main/java/org/assertj/core/internal/Arrays.java
Expand Up @@ -64,6 +64,7 @@
import static org.assertj.core.internal.IterableDiff.diff;
import static org.assertj.core.util.ArrayWrapperList.wrap;
import static org.assertj.core.util.Arrays.isArray;
import static org.assertj.core.util.Arrays.prepend;
import static org.assertj.core.util.IterableUtil.isNullOrEmpty;
import static org.assertj.core.util.Lists.newArrayList;
import static org.assertj.core.util.Preconditions.checkArgument;
Expand All @@ -90,6 +91,7 @@
* @author Alex Ruiz
* @author Joel Costigliola
* @author Nicolas François
* @author Florent Biville
*/
public class Arrays {

Expand Down Expand Up @@ -443,21 +445,31 @@ void assertStartsWith(AssertionInfo info, Failures failures, Object actual, Obje
}

private static boolean commonChecks(AssertionInfo info, Object actual, Object sequence) {
checkIsNotNull(sequence);
assertNotNull(info, actual);
checkNulls(info, actual, sequence);
// if both actual and values are empty arrays, then assertion passes.
if (isArrayEmpty(actual) && isArrayEmpty(sequence)) return true;
failIfEmptySinceActualIsNotEmpty(sequence);
return false;

}

private static void checkNulls(AssertionInfo info, Object actual, Object sequence) {
checkIsNotNull(sequence);
assertNotNull(info, actual);
}

private AssertionError arrayDoesNotStartWithSequence(AssertionInfo info, Failures failures, Object array,
Object sequence) {
return failures.failure(info, shouldStartWith(array, sequence, comparisonStrategy));
}

void assertEndsWith(AssertionInfo info, Failures failures, Object actual, Object first, Object rest) {
Object[] sequence = prepend(first, rest);
assertEndsWith(info, failures, actual, sequence);
}

void assertEndsWith(AssertionInfo info, Failures failures, Object actual, Object sequence) {
if (commonChecks(info, actual, sequence)) return;
checkNulls(info, actual, sequence);
int sequenceSize = sizeOf(sequence);
int arraySize = sizeOf(actual);
if (arraySize < sequenceSize) throw arrayDoesNotEndWithSequence(info, failures, actual, sequence);
Expand Down
31 changes: 28 additions & 3 deletions src/main/java/org/assertj/core/internal/Iterables.java
Expand Up @@ -59,6 +59,7 @@
import static org.assertj.core.internal.ErrorMessages.nullSequence;
import static org.assertj.core.internal.ErrorMessages.nullSubsequence;
import static org.assertj.core.internal.IterableDiff.diff;
import static org.assertj.core.util.Arrays.prepend;
import static org.assertj.core.util.IterableUtil.isNullOrEmpty;
import static org.assertj.core.util.IterableUtil.sizeOf;
import static org.assertj.core.util.Lists.newArrayList;
Expand All @@ -83,6 +84,7 @@
* @author Maciej Jaskowski
* @author Nicolas François
* @author Joel Costigliola
* @author Florent Biville
*/
public class Iterables {

Expand Down Expand Up @@ -572,14 +574,33 @@ private AssertionError actualDoesNotStartWithSequence(AssertionInfo info, Iterab
*
* @param info contains information about the assertion.
* @param actual the given {@code Iterable}.
* @param first the first element of the end sequence.
* @param rest the optional next elements of the end sequence.
* @throws NullPointerException if the given argument is {@code null}.
* @throws IllegalArgumentException if the given argument is an empty array.
* @throws AssertionError if the given {@code Iterable} is {@code null}.
* @throws AssertionError if the given {@code Iterable} does not end with the given sequence of objects.
*/
public void assertEndsWith(AssertionInfo info, Iterable<?> actual, Object first, Object[] rest) {
Object[] sequence = prepend(first, rest);
assertEndsWith(info, actual, sequence);
}

/**
* Verifies that the given {@code Iterable} ends with the given sequence of objects, without any other objects between
* them. Similar to <code>{@link #assertContainsSequence(AssertionInfo, Iterable, Object[])}</code>, but it also
* verifies that the last element in the sequence is also the last element of the given {@code Iterable}.
*
* @param info contains information about the assertion.
* @param actual the given {@code Iterable}.
* @param sequence the sequence of objects to look for.
* @throws NullPointerException if the given argument is {@code null}.
* @throws IllegalArgumentException if the given argument is an empty array.
* @throws AssertionError if the given {@code Iterable} is {@code null}.
* @throws AssertionError if the given {@code Iterable} does not end with the given sequence of objects.
*/
public void assertEndsWith(AssertionInfo info, Iterable<?> actual, Object[] sequence) {
if (commonCheckThatIterableAssertionSucceeds(info, actual, sequence)) return;
checkNotNullIterables(info, actual, sequence);

int sizeOfActual = sizeOf(actual);
if (sizeOfActual < sequence.length) throw actualDoesNotEndWithSequence(info, actual, sequence);
Expand All @@ -594,14 +615,18 @@ public void assertEndsWith(AssertionInfo info, Iterable<?> actual, Object[] sequ
}

private boolean commonCheckThatIterableAssertionSucceeds(AssertionInfo info, Iterable<?> actual, Object[] sequence) {
checkIsNotNull(sequence);
assertNotNull(info, actual);
checkNotNullIterables(info, actual, sequence);
// if both actual and values are empty, then assertion passes.
if (!actual.iterator().hasNext() && sequence.length == 0) return true;
failIfEmptySinceActualIsNotEmpty(sequence);
return false;
}

private void checkNotNullIterables(AssertionInfo info, Iterable<?> actual, Object[] sequence) {
checkIsNotNull(sequence);
assertNotNull(info, actual);
}

/**
* Asserts that the given {@code Iterable} contains at least a null element.
*
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/org/assertj/core/internal/ObjectArrays.java
Expand Up @@ -364,12 +364,28 @@ public void assertStartsWith(AssertionInfo info, Object[] actual, Object[] seque
* Verifies that the given array ends with the given sequence of objects, without any other objects between them.
* Similar to <code>{@link #assertContainsSequence(AssertionInfo, Object[], Object[])}</code>, but it also verifies
* that the last element in the sequence is also the last element of the given array.
*
*
* @param info contains information about the assertion.
* @param actual the given array.
* @param first the first element of the end sequence of objects to look for.
* @param sequence the rest of the end sequence of objects to look for.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the given array is {@code null}.
* @throws AssertionError if the given array does not end with the given sequence of objects.
*/
public void assertEndsWith(AssertionInfo info, Object[] actual, Object first, Object[] sequence) {
arrays.assertEndsWith(info, failures, actual, first, sequence);
}

/**
* Verifies that the given array ends with the given sequence of objects, without any other objects between them.
* Similar to <code>{@link #assertContainsSequence(AssertionInfo, Object[], Object[])}</code>, but it also verifies
* that the last element in the sequence is also the last element of the given array.
*
* @param info contains information about the assertion.
* @param actual the given array.
* @param sequence the sequence of objects to look for.
* @throws NullPointerException if the given argument is {@code null}.
* @throws IllegalArgumentException if the given argument is an empty array.
* @throws AssertionError if the given array is {@code null}.
* @throws AssertionError if the given array does not end with the given sequence of objects.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/assertj/core/util/Arrays.java
Expand Up @@ -28,6 +28,7 @@
*
* @author Alex Ruiz
* @author Joel Costigliola
* @author Florent Biville
*/
public class Arrays {

Expand Down Expand Up @@ -163,6 +164,14 @@ public static IllegalArgumentException notAnArrayOfPrimitives(Object o) {
return new IllegalArgumentException(String.format("<%s> is not an array of primitives", o));
}

@SuppressWarnings("unchecked")
public static <T> T[] prepend(T first, T... rest) {
T[] result = (T[]) new Object[1 + rest.length];
result[0] = first;
System.arraycopy(rest, 0, result, 1, rest.length);
return result;
}

private Arrays() {}

}
Expand Up @@ -28,6 +28,6 @@ protected AtomicReferenceArrayAssert<Object> invoke_api_method() {

@Override
protected void verify_internal_effects() {
verify(arrays).assertEndsWith(info(), internalArray(), array("Luke", "Yoda"));
verify(arrays).assertEndsWith(info(), internalArray(), "Luke", array("Yoda"));
}
}
Expand Up @@ -26,6 +26,7 @@
*
* @author Alex Ruiz
* @author Joel Costigliola
* @author Florent Biville
*/
public class IterableAssert_endsWith_Test extends IterableAssertBaseTest {

Expand All @@ -36,6 +37,6 @@ protected ConcreteIterableAssert<Object> invoke_api_method() {

@Override
protected void verify_internal_effects() {
verify(iterables).assertEndsWith(getInfo(assertions), getActual(assertions), array("Luke", "Yoda"));
verify(iterables).assertEndsWith(getInfo(assertions), getActual(assertions), "Luke", array("Yoda"));
}
}
Expand Up @@ -25,6 +25,7 @@
*
* @author Alex Ruiz
* @author Mikhail Mazursky
* @author Florent Biville
*/
public class ObjectArrayAssert_endsWith_Test extends ObjectArrayAssertBaseTest {

Expand All @@ -35,6 +36,6 @@ protected ObjectArrayAssert<Object> invoke_api_method() {

@Override
protected void verify_internal_effects() {
verify(arrays).assertEndsWith(getInfo(assertions), getActual(assertions), array("Luke", "Yoda"));
verify(arrays).assertEndsWith(getInfo(assertions), getActual(assertions), "Luke", array("Yoda"));
}
}

0 comments on commit f3b181c

Please sign in to comment.