@@ -260,10 +289,14 @@ public SELF containsOnlyWhitespaces() {
* @since 3.11.0
*/
public SELF doesNotContainAnyWhitespaces() {
- strings.assertDoesNotContainAnyWhitespaces(info, actual);
+ assertDoesNotContainAnyWhitespaces(actual);
return myself;
}
+ private void assertDoesNotContainAnyWhitespaces(CharSequence actual) {
+ if (containsWhitespaces(actual)) throw assertionError(shouldNotContainAnyWhitespaces(actual));
+ }
+
/**
* Verifies that the actual {@code CharSequence} is either:
*
@@ -291,10 +324,14 @@ public SELF doesNotContainAnyWhitespaces() {
* @since 2.9.0 / 3.9.0
*/
public SELF doesNotContainOnlyWhitespaces() {
- strings.assertDoesNotContainOnlyWhitespaces(info, actual);
+ assertDoesNotContainOnlyWhitespaces(actual);
return myself;
}
+ private void assertDoesNotContainOnlyWhitespaces(CharSequence actual) {
+ if (containsOnlyWhitespaces(actual)) throw assertionError(shouldNotContainOnlyWhitespaces(actual));
+ }
+
/**
* Verifies that the actual {@code CharSequence} is blank, i.e. consists of one or more whitespace characters
* (according to {@link Character#isWhitespace(char)}).
@@ -317,10 +354,14 @@ public SELF doesNotContainOnlyWhitespaces() {
*/
@Deprecated
public SELF isJavaBlank() {
- strings.assertJavaBlank(info, actual);
+ assertJavaBlank(actual);
return myself;
}
+ private void assertJavaBlank(CharSequence actual) {
+ if (!containsOnlyWhitespaces(actual)) throw assertionError(shouldBeBlank(actual));
+ }
+
/**
* Verifies that the actual {@code CharSequence} is not blank, i.e. either is {@code null}, empty or
* contains at least one non-whitespace character (according to {@link Character#isWhitespace(char)}).
@@ -344,10 +385,14 @@ public SELF isJavaBlank() {
*/
@Deprecated
public SELF isNotJavaBlank() {
- strings.assertNotJavaBlank(info, actual);
+ assertNotJavaBlank(actual);
return myself;
}
+ private void assertNotJavaBlank(CharSequence actual) {
+ if (containsOnlyWhitespaces(actual)) throw assertionError(shouldNotBeBlank(actual));
+ }
+
/**
* Verifies that the actual {@code CharSequence} has the expected length using the {@code length()} method.
*
@@ -624,7 +669,6 @@ public SELF isNotEqualToIgnoringCase(CharSequence expected) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual {@code CharSequence} contains non-digit characters or is {@code null}.
*/
- @Deprecated
public SELF containsOnlyDigits() {
strings.assertContainsOnlyDigits(info, actual);
return myself;
@@ -873,10 +917,24 @@ public SELF containsIgnoringCase(CharSequence sequence) {
* @throws AssertionError if the actual {@code CharSequence} does not contain all the given values.
*/
public SELF containsIgnoringWhitespaces(CharSequence... values) {
- strings.assertContainsIgnoringWhitespaces(info, actual, values);
+ assertContainsIgnoringWhitespaces(values);
return myself;
}
+ private void assertContainsIgnoringWhitespaces(CharSequence... values) {
+ doCommonCheckForCharSequence(info, actual, values);
+ String actualWithoutWhitespace = removeAllWhitespaces(actual);
+ Set notFound = stream(values).map(Strings::removeAllWhitespaces)
+ .filter(value -> !objects.getComparisonStrategy()
+ .stringContains(actualWithoutWhitespace, value))
+ .collect(toCollection(LinkedHashSet::new));
+ if (notFound.isEmpty()) return;
+ if (values.length == 1) {
+ throw assertionError(shouldContainIgnoringWhitespaces(actual, values[0], objects.getComparisonStrategy()));
+ }
+ throw assertionError(shouldContainIgnoringWhitespaces(actual, values, notFound, objects.getComparisonStrategy()));
+ }
+
/**
* Verifies that the actual {@code CharSequence} contains all the given values ignoring new line differences.
*
@@ -1564,7 +1622,7 @@ public SELF isNotEqualToIgnoringWhitespace(CharSequence expected) {
* To be exact, the following rules are applied:
*
* all leading and trailing whitespace of both actual and expected strings are ignored
- * any remaining whitespace, appearing within either string, is collapsed to a single space before comparison
+ * any remaining whitespace (including non-breaking spaces), appearing within either string, is collapsed to a single space before comparison
*
*
* Example:
@@ -1576,6 +1634,7 @@ public SELF isNotEqualToIgnoringWhitespace(CharSequence expected) {
* .isEqualToNormalizingWhitespace(" Game of Thrones ")
* .isEqualToNormalizingWhitespace("Game of\tThrones")
* .isEqualToNormalizingWhitespace("Game of Thrones");
+ * .isEqualToNormalizingWhitespace("Game\u00A0of Thrones");
*
* // assertions will fail
* assertThat("Game of Thrones").isEqualToNormalizingWhitespace("Game ofThrones");
@@ -1599,7 +1658,7 @@ public SELF isEqualToNormalizingWhitespace(CharSequence expected) {
* To be exact, the following rules are applied:
*
* all leading and trailing whitespace of both actual and expected strings are ignored
- * any remaining whitespace, appearing within either string, is collapsed to a single space before comparison
+ * any remaining whitespace (including non-breaking spaces), appearing within either string, is collapsed to a single space before comparison
*
*
* Example:
@@ -1628,11 +1687,11 @@ public SELF isNotEqualToNormalizingWhitespace(CharSequence expected) {
/**
* Verifies that the actual {@code CharSequence} is equal to the given one, after the punctuation
- * of both strings have been normalized.
+ * of both strings has been normalized.
*
* To be exact, the following rules are applied:
*
- * All punctuation of actual and expected strings are ignored and whitespaces are normalized
+ * All punctuation of actual and expected strings are ignored and whitespaces (including non-breaking spaces) are normalized
* Punctuation is any of the following character {@code !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~}
*
*
@@ -1957,7 +2016,7 @@ public SELF isEqualToNormalizingUnicode(CharSequence expected) {
* @throws AssertionError if the actual {@code CharSequence} is not alphabetic.
* @see java.util.regex.Pattern
*/
- public SELF isAlphabetic(){
+ public SELF isAlphabetic() {
isNotNull();
if (!Pattern.matches("\\p{Alpha}+", actual)) throwAssertionError(shouldBeAlphabetic(actual));
return myself;
@@ -1983,7 +2042,7 @@ public SELF isAlphabetic(){
* @throws AssertionError if the actual {@code CharSequence} is not alphanumeric.
* @see java.util.regex.Pattern
*/
- public SELF isAlphanumeric(){
+ public SELF isAlphanumeric() {
isNotNull();
if (!Pattern.matches("\\p{Alnum}+", actual)) throwAssertionError(shouldBeAlphanumeric(actual));
return myself;
@@ -2009,7 +2068,7 @@ public SELF isAlphanumeric(){
* @throws AssertionError if the actual {@code CharSequence} is not ASCII.
* @see java.util.regex.Pattern
*/
- public SELF isASCII(){
+ public SELF isASCII() {
isNotNull();
if (!Pattern.matches("\\p{ASCII}+", actual)) throwAssertionError(shouldBeASCII(actual));
return myself;
@@ -2035,7 +2094,7 @@ public SELF isASCII(){
* @throws AssertionError if the actual {@code CharSequence} is not hexadecimal.
* @see java.util.regex.Pattern
*/
- public SELF isHexadecimal(){
+ public SELF isHexadecimal() {
isNotNull();
if (!Pattern.matches("\\p{XDigit}+", actual)) throwAssertionError(shouldBeHexadecimal(actual));
return myself;
@@ -2062,7 +2121,7 @@ public SELF isHexadecimal(){
* @throws AssertionError if the actual {@code CharSequence} is not printable.
* @see java.util.regex.Pattern
*/
- public SELF isPrintable(){
+ public SELF isPrintable() {
isNotNull();
if (!Pattern.matches("\\p{Print}+", actual)) throwAssertionError(shouldBePrintable(actual));
return myself;
@@ -2088,10 +2147,34 @@ public SELF isPrintable(){
* @throws AssertionError if the actual {@code CharSequence} is not visible.
* @see java.util.regex.Pattern
*/
- public SELF isVisible(){
+ public SELF isVisible() {
isNotNull();
if (!Pattern.matches("\\p{Graph}+", actual)) throwAssertionError(shouldBeVisible(actual));
return myself;
}
+ private static boolean isBlank(CharSequence actual) {
+ return isNullOrEmpty(actual) || strictlyContainsWhitespaces(actual);
+ }
+
+ private static boolean containsOnlyWhitespaces(CharSequence actual) {
+ return !isNullOrEmpty(actual) && strictlyContainsWhitespaces(actual);
+ }
+
+ private static boolean containsWhitespaces(CharSequence actual) {
+ return !isNullOrEmpty(actual) && containsOneOrMoreWhitespaces(actual);
+ }
+
+ private static boolean isNullOrEmpty(CharSequence actual) {
+ return actual == null || actual.length() == 0;
+ }
+
+ private static boolean strictlyContainsWhitespaces(CharSequence actual) {
+ return actual.chars().allMatch(Character::isWhitespace);
+ }
+
+ private static boolean containsOneOrMoreWhitespaces(CharSequence actual) {
+ return actual.chars().anyMatch(Character::isWhitespace);
+ }
+
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractClassAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractClassAssert.java
index 8f6a4456b5..28d7aa8854 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractClassAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractClassAssert.java
@@ -12,12 +12,40 @@
*/
package org.assertj.core.api;
+import static java.util.Objects.requireNonNull;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldBeFinal;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldBePackagePrivate;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldBeProtected;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldBePublic;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldBeStatic;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldNotBeFinal;
+import static org.assertj.core.error.ClassModifierShouldBe.shouldNotBeStatic;
+import static org.assertj.core.error.ShouldBeAbstract.shouldBeAbstract;
+import static org.assertj.core.error.ShouldBeAnnotation.shouldBeAnnotation;
+import static org.assertj.core.error.ShouldBeAnnotation.shouldNotBeAnnotation;
import static org.assertj.core.error.ShouldBeAssignableTo.shouldBeAssignableTo;
+import static org.assertj.core.error.ShouldBeInterface.shouldBeInterface;
+import static org.assertj.core.error.ShouldBeInterface.shouldNotBeInterface;
+import static org.assertj.core.error.ShouldBePrimitive.shouldBePrimitive;
+import static org.assertj.core.error.ShouldBeRecord.shouldBeRecord;
+import static org.assertj.core.error.ShouldBeRecord.shouldNotBeRecord;
+import static org.assertj.core.error.ShouldBeSealed.shouldBeSealed;
+import static org.assertj.core.error.ShouldBeSealed.shouldNotBeSealed;
+import static org.assertj.core.error.ShouldHaveNoPackage.shouldHaveNoPackage;
+import static org.assertj.core.error.ShouldHaveNoSuperclass.shouldHaveNoSuperclass;
+import static org.assertj.core.error.ShouldHavePackage.shouldHavePackage;
+import static org.assertj.core.error.ShouldHaveRecordComponents.shouldHaveRecordComponents;
+import static org.assertj.core.error.ShouldHaveSuperclass.shouldHaveSuperclass;
import static org.assertj.core.error.ShouldNotBeNull.shouldNotBeNull;
+import static org.assertj.core.error.ShouldNotBePrimitive.shouldNotBePrimitive;
import static org.assertj.core.util.Arrays.array;
+import static org.assertj.core.util.Sets.newLinkedHashSet;
import java.lang.annotation.Annotation;
-import java.util.Objects;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Set;
import org.assertj.core.internal.Classes;
@@ -94,7 +122,7 @@ public SELF isAssignableTo(Class> other) {
}
private void assertIsAssignableTo(Class> other) {
- Objects.requireNonNull(other, shouldNotBeNull("other")::create);
+ requireNonNull(other, shouldNotBeNull("other")::create);
if (!other.isAssignableFrom(actual)) throw assertionError(shouldBeAssignableTo(actual, other));
}
@@ -116,10 +144,15 @@ private void assertIsAssignableTo(Class> other) {
* @throws AssertionError if the actual {@code Class} is not an interface.
*/
public SELF isNotInterface() {
- classes.assertIsNotInterface(info, actual);
+ isNotNull();
+ assertIsNotInterface();
return myself;
}
+ private void assertIsNotInterface() {
+ if (actual.isInterface()) throw assertionError(shouldNotBeInterface(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is an interface.
*
@@ -138,10 +171,15 @@ public SELF isNotInterface() {
* @throws AssertionError if the actual {@code Class} is not an interface.
*/
public SELF isInterface() {
- classes.assertIsInterface(info, actual);
+ isNotNull();
+ assertIsInterface();
return myself;
}
+ private void assertIsInterface() {
+ if (!actual.isInterface()) throw assertionError(shouldBeInterface(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is abstract (has {@code abstract} modifier).
*
@@ -161,10 +199,15 @@ public SELF isInterface() {
* @since 3.12.0
*/
public SELF isAbstract() {
- classes.assertIsAbstract(info, actual);
+ isNotNull();
+ assertIsAbstract();
return myself;
}
+ private void assertIsAbstract() {
+ if (!Modifier.isAbstract(actual.getModifiers())) throw assertionError(shouldBeAbstract(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is an annotation.
*
@@ -184,10 +227,15 @@ public SELF isAbstract() {
* @throws AssertionError if the actual {@code Class} is not an annotation.
*/
public SELF isAnnotation() {
- classes.assertIsAnnotation(info, actual);
+ isNotNull();
+ assertIsAnnotation();
return myself;
}
+ private void assertIsAnnotation() {
+ if (!actual.isAnnotation()) throw assertionError(shouldBeAnnotation(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is not an annotation.
*
@@ -207,10 +255,148 @@ public SELF isAnnotation() {
* @throws AssertionError if the actual {@code Class} is an annotation.
*/
public SELF isNotAnnotation() {
- classes.assertIsNotAnnotation(info, actual);
+ isNotNull();
+ assertIsNotAnnotation();
return myself;
}
+ private void assertIsNotAnnotation() {
+ if (actual.isAnnotation()) throw assertionError(shouldNotBeAnnotation(actual));
+ }
+
+ /**
+ * Verifies that the actual {@code Class} is a record.
+ *
+ * Example:
+ *
public record Jedi(String name) {}
+ *
+ * // this assertion succeeds:
+ * assertThat(Jedi.class).isRecord();
+ *
+ * // this assertion fails:
+ * assertThat(String.class).isRecord();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is not a record.
+ *
+ * @since 3.25.0
+ */
+ public SELF isRecord() {
+ isNotNull();
+ assertIsRecord();
+ return myself;
+ }
+
+ private void assertIsRecord() {
+ if (!isRecord(actual)) throw assertionError(shouldBeRecord(actual));
+ }
+
+ /**
+ * Verifies that the actual {@code Class} is not a record.
+ *
+ * Example:
+ *
public record Jedi(String name) {}
+ *
+ * // this assertion succeeds:
+ * assertThat(String.class).isNotRecord();
+ *
+ * // this assertion fails:
+ * assertThat(Jedi.class).isNotRecord();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is a record.
+ *
+ * @since 3.25.0
+ */
+ public SELF isNotRecord() {
+ isNotNull();
+ assertIsNotRecord();
+ return myself;
+ }
+
+ private void assertIsNotRecord() {
+ if (isRecord(actual)) throw assertionError(shouldNotBeRecord(actual));
+ }
+
+ // TODO https://github.com/assertj/assertj/issues/3079
+ private static boolean isRecord(Class> actual) {
+ try {
+ Method isRecord = Class.class.getMethod("isRecord");
+ return (boolean) isRecord.invoke(actual);
+ } catch (NoSuchMethodException e) {
+ return false;
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Verifies that the actual {@code Class} has the given record components
+ *
+ * Example:
+ *
public class NotARecord {}
+ *
+ * public record MyRecord(String componentOne, String componentTwo) {}
+ *
+ * // these assertions succeed:
+ * assertThat(MyRecord.class).hasRecordComponents("componentOne");
+ * assertThat(MyRecord.class).hasRecordComponents("componentOne", "componentTwo");
+ *
+ * // these assertions fail:
+ * assertThat(NotARecord.class).hasRecordComponents("componentOne");
+ * assertThat(MyRecord.class).hasRecordComponents("componentOne", "unknownComponent");
+ *
+ * @param first the first record component name which must be in this class
+ * @param rest the remaining record component names which must be in this class
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is not a record.
+ * @throws AssertionError if the actual {@code Class} doesn't contain all the record component names.
+ *
+ * @since 3.25.0
+ */
+ public SELF hasRecordComponents(String first, String... rest) {
+ isRecord();
+ assertHasRecordComponents(first, rest);
+ return myself;
+ }
+
+ private void assertHasRecordComponents(String first, String[] rest) {
+ Set expectedRecordComponents = newLinkedHashSet();
+ expectedRecordComponents.add(first);
+ if (rest != null) {
+ Collections.addAll(expectedRecordComponents, rest);
+ }
+ Set missingRecordComponents = newLinkedHashSet();
+ Set actualRecordComponents = getRecordComponentNames(actual);
+
+ for (String name : expectedRecordComponents) {
+ if (!actualRecordComponents.contains(name)) {
+ missingRecordComponents.add(name);
+ }
+ }
+ if (!missingRecordComponents.isEmpty()) {
+ throw assertionError(shouldHaveRecordComponents(actual, expectedRecordComponents, missingRecordComponents));
+ }
+ }
+
+ private static Set getRecordComponentNames(Class> actual) {
+ try {
+ Method getRecordComponents = Class.class.getMethod("getRecordComponents");
+ Object[] recordComponents = (Object[]) getRecordComponents.invoke(actual);
+ Set recordComponentNames = newLinkedHashSet();
+ for (Object recordComponent : recordComponents) {
+ Method getName = recordComponent.getClass().getMethod("getName");
+ recordComponentNames.add((String) getName.invoke(recordComponent));
+ }
+ return recordComponentNames;
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
/**
* Verifies that the actual {@code Class} is final (has {@code final} modifier).
*
@@ -228,10 +414,15 @@ public SELF isNotAnnotation() {
* @throws AssertionError if the actual {@code Class} is not final.
*/
public SELF isFinal() {
- classes.assertIsFinal(info, actual);
+ isNotNull();
+ assertIsFinal();
return myself;
}
+ private void assertIsFinal() {
+ if (!Modifier.isFinal(actual.getModifiers())) throw assertionError(shouldBeFinal(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is not final (does not have {@code final} modifier).
*
@@ -249,10 +440,15 @@ public SELF isFinal() {
* @throws AssertionError if the actual {@code Class} is final.
*/
public SELF isNotFinal() {
- classes.assertIsNotFinal(info, actual);
+ isNotNull();
+ assertIsNotFinal();
return myself;
}
+ private void assertIsNotFinal() {
+ if (Modifier.isFinal(actual.getModifiers())) throw assertionError(shouldNotBeFinal(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is public (has {@code public} modifier).
*
@@ -273,10 +469,15 @@ public SELF isNotFinal() {
* @since 2.7.0 / 3.7.0
*/
public SELF isPublic() {
- classes.assertIsPublic(info, actual);
+ isNotNull();
+ assertIsPublic();
return myself;
}
+ private void assertIsPublic() {
+ if (!Modifier.isPublic(actual.getModifiers())) throw assertionError(shouldBePublic(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is protected (has {@code protected} modifier).
*
@@ -297,10 +498,15 @@ public SELF isPublic() {
* @since 2.7.0 / 3.7.0
*/
public SELF isProtected() {
- classes.assertIsProtected(info, actual);
+ isNotNull();
+ assertIsProtected();
return myself;
}
+ private void assertIsProtected() {
+ if (!Modifier.isProtected(actual.getModifiers())) throw assertionError(shouldBeProtected(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is package-private (has no modifier).
*
@@ -321,10 +527,18 @@ public SELF isProtected() {
* @since 3.15.0
*/
public SELF isPackagePrivate() {
- classes.assertIsPackagePrivate(info, actual);
+ isNotNull();
+ assertIsPackagePrivate();
return myself;
}
+ private void assertIsPackagePrivate() {
+ final int modifiers = actual.getModifiers();
+ if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || Modifier.isPrivate(modifiers)) {
+ throw assertionError(shouldBePackagePrivate(actual));
+ }
+ }
+
/**
* Verifies that the actual {@code Class} is static (has {@code static} modifier).
*
@@ -345,10 +559,15 @@ public SELF isPackagePrivate() {
* @since 3.23.0
*/
public SELF isStatic() {
- classes.assertIsStatic(info, actual);
+ isNotNull();
+ assertIsStatic();
return myself;
}
+ private void assertIsStatic() {
+ if (!Modifier.isStatic(actual.getModifiers())) throw assertionError(shouldBeStatic(actual));
+ }
+
/**
* Verifies that the actual {@code Class} is not static (does not have {@code static} modifier).
*
@@ -369,10 +588,15 @@ public SELF isStatic() {
* @since 3.23.0
*/
public SELF isNotStatic() {
- classes.assertIsNotStatic(info, actual);
+ isNotNull();
+ assertIsNotStatic();
return myself;
}
+ private void assertIsNotStatic() {
+ if (Modifier.isStatic(actual.getModifiers())) throw assertionError(shouldNotBeStatic(actual));
+ }
+
/**
* Verifies that the actual {@code Class} has the given {@code Annotation}s.
*
@@ -445,8 +669,8 @@ public SELF hasAnnotation(Class extends Annotation> annotation) {
/**
* Verifies that the actual {@code Class} has the given class as direct superclass (as in {@link Class#getSuperclass()}).
*
- * The {@code superclass} should always be not {@code null}, use {@link #hasNoSuperclass()} to verify the absence of
- * the superclass.
+ * The expected {@code superclass} should always be not {@code null}. To verify the absence of the superclass, use
+ * {@link #hasNoSuperclass()}.
*
* Example:
*
// this assertion succeeds:
@@ -473,10 +697,19 @@ public SELF hasAnnotation(Class extends Annotation> annotation) {
* @see #hasNoSuperclass()
*/
public SELF hasSuperclass(Class> superclass) {
- classes.assertHasSuperclass(info, actual, superclass);
+ isNotNull();
+ assertHasSuperclass(superclass);
return myself;
}
+ private void assertHasSuperclass(Class> superclass) {
+ requireNonNull(superclass, shouldNotBeNull("superclass")::create);
+ Class> actualSuperclass = actual.getSuperclass();
+ if (actualSuperclass == null || !actualSuperclass.equals(superclass)) {
+ throw assertionError(shouldHaveSuperclass(actual, superclass));
+ }
+ }
+
/**
* Verifies that the actual {@code Class} has no superclass (as in {@link Class#getSuperclass()}, when {@code null}
* is returned).
@@ -504,10 +737,15 @@ public SELF hasSuperclass(Class> superclass) {
* @see #hasSuperclass(Class)
*/
public SELF hasNoSuperclass() {
- classes.assertHasNoSuperclass(info, actual);
+ isNotNull();
+ assertHasNoSuperclass();
return myself;
}
+ private void assertHasNoSuperclass() {
+ if (actual.getSuperclass() != null) throw assertionError(shouldHaveNoSuperclass(actual));
+ }
+
/**
* @deprecated use {@link #hasPublicFields(String...)} instead.
* @param fields the fields who must be in the class.
@@ -743,7 +981,9 @@ public SELF hasPublicMethods(String... methodNames) {
/**
* Verifies that the actual {@code Class} has the given package name (as in {@link Class#getPackage()}).
- *
+ *
+ * The expected package name should always be not {@code null}. To verify the absence of the package, use
+ * {@link #hasNoPackage()}.
*
* Example:
*
package one.two;
@@ -758,21 +998,32 @@ public SELF hasPublicMethods(String... methodNames) {
* assertThat(MyClass.class).hasPackage("");
* assertThat(MyClass.class).hasPackage("java.lang");
*
- * @param packageName the package name the class should have
+ * @param expected the package name the class should have
* @return {@code this} assertions object
* @throws AssertionError if {@code actual} is {@code null}.
* @throws AssertionError if the actual {@code Class} does not have the given package.
*
* @since 3.18.0
*/
- public SELF hasPackage(String packageName) {
- classes.assertHasPackage(info, actual, packageName);
+ public SELF hasPackage(String expected) {
+ isNotNull();
+ assertHasPackage(expected);
return myself;
}
+ private void assertHasPackage(String packageName) {
+ requireNonNull(packageName, shouldNotBeNull("expected")::create);
+ Package actualPackage = actual.getPackage();
+ if (actualPackage == null || !actualPackage.getName().equals(packageName)) {
+ throw assertionError(shouldHavePackage(actual, packageName));
+ }
+ }
+
/**
* Verifies that the actual {@code Class} has the given package (as in {@link Class#getPackage()}).
- *
+ *
+ * The expected package should always be not {@code null}. To verify the absence of the package, use
+ * {@link #hasNoPackage()}.
*
* Example:
*
package one.two;
@@ -788,16 +1039,186 @@ public SELF hasPackage(String packageName) {
* assertThat(MyClass.class).hasPackage(Package.getPackage(""));
* assertThat(MyClass.class).hasPackage(Object.class.getPackage());
*
- * @param aPackage the package the class should have
+ * @param expected the package the class should have
* @return {@code this} assertions object
* @throws AssertionError if {@code actual} is {@code null}.
* @throws AssertionError if the actual {@code Class} does not have the given package.
*
* @since 3.18.0
+ * @see #hasPackage(String)
+ * @see #hasNoPackage()
+ */
+ public SELF hasPackage(Package expected) {
+ isNotNull();
+ assertHasPackage(expected);
+ return myself;
+ }
+
+ private void assertHasPackage(Package expected) {
+ requireNonNull(expected, shouldNotBeNull("expected")::create);
+ if (!expected.equals(actual.getPackage())) throw assertionError(shouldHavePackage(actual, expected));
+ }
+
+ /**
+ * Verifies that the actual {@code Class} has no package (as in {@link Class#getPackage()}, when {@code null}
+ * is returned).
+ *
+ * Example:
+ *
// this assertion succeeds as arrays have no package:
+ * assertThat(int[].class).hasNoPackage();
+ *
+ * // this assertion succeeds as primitive types have no package:
+ * assertThat(Integer.TYPE).hasNoPackage();
+ *
+ * // this assertion succeeds as void type has no package:
+ * assertThat(Void.TYPE).hasNoPackage();
+ *
+ * // this assertion fails as Object has java.lang as package:
+ * assertThat(Object.class).hasNoPackage();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} has a package.
+ *
+ * @since 3.25.0
+ * @see #hasPackage(Package)
+ * @see #hasPackage(String)
+ */
+ public SELF hasNoPackage() {
+ isNotNull();
+ assertHasNoPackage();
+ return myself;
+ }
+
+ private void assertHasNoPackage() {
+ if (actual.getPackage() != null) throw assertionError(shouldHaveNoPackage(actual));
+ }
+
+ /**
+ * Verifies that the actual {@code Class} is sealed.
+ *
+ * Example:
+ *
sealed class SealedClass permits NonSealedClass {}
+ *
+ * non-sealed class NonSealedClass extends SealedClass {}
+ *
+ * // this assertion succeeds:
+ * assertThat(SealedClass.class).isSealed();
+ *
+ * // this assertion fails:
+ * assertThat(NonSealedClass.class).isSealed();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is not sealed.
+ *
+ * @since 3.25.0
*/
- public SELF hasPackage(Package aPackage) {
- classes.assertHasPackage(info, actual, aPackage);
+ public SELF isSealed() {
+ isNotNull();
+ assertIsSealed();
+ return myself;
+ }
+
+ private void assertIsSealed() {
+ if (!isSealed(actual)) throw assertionError(shouldBeSealed(actual));
+ }
+
+ /**
+ * Verifies that the actual {@code Class} is not sealed.
+ *
+ * Example:
+ *
sealed class SealedClass permits NonSealedClass {}
+ *
+ * non-sealed class NonSealedClass extends SealedClass {}
+ *
+ * // this assertion succeeds:
+ * assertThat(NonSealedClass.class).isNotSealed();
+ *
+ * // this assertion fails:
+ * assertThat(SealedClass.class).isNotSealed();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is sealed.
+ *
+ * @since 3.25.0
+ */
+ public SELF isNotSealed() {
+ isNotNull();
+ assertIsNotSealed();
+ return myself;
+ }
+
+ private void assertIsNotSealed() {
+ if (isSealed(actual)) throw assertionError(shouldNotBeSealed(actual));
+ }
+
+ // TODO https://github.com/assertj/assertj/issues/3081
+ private static boolean isSealed(Class> actual) {
+ try {
+ Method isSealed = Class.class.getMethod("isSealed");
+ return (boolean) isSealed.invoke(actual);
+ } catch (NoSuchMethodException e) {
+ return false;
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Verifies that the actual {@code Class} is a primitive type.
+ *
+ * Example:
+ *
// these assertions succeed:
+ * assertThat(byte.class).isPrimitive();
+ * assertThat(short.class).isPrimitive();
+ * assertThat(int.class).isPrimitive();
+ * assertThat(long.class).isPrimitive();
+ * assertThat(float.class).isPrimitive();
+ * assertThat(double.class).isPrimitive();
+ * assertThat(boolean.class).isPrimitive();
+ * assertThat(char.class).isPrimitive();
+ *
+ * // this assertion fails as Object is not a primitive type:
+ * assertThat(Object.class).isPrimitive();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is not a primitive type.
+ * @see Class#isPrimitive()
+ */
+ public SELF isPrimitive() {
+ isNotNull();
+ if (!actual.isPrimitive()) throw assertionError(shouldBePrimitive(actual));
return myself;
}
+ /**
+ * Verifies that the actual {@code Class} is not a primitive type.
+ *
+ * Example:
+ *
// this assertion succeeds as Object is not a primitive type:
+ * assertThat(Object.class).isNotPrimitive();
+ *
+ * // these assertions fail:
+ * assertThat(byte.class).isNotPrimitive();
+ * assertThat(short.class).isNotPrimitive();
+ * assertThat(int.class).isNotPrimitive();
+ * assertThat(long.class).isNotPrimitive();
+ * assertThat(float.class).isNotPrimitive();
+ * assertThat(double.class).isNotPrimitive();
+ * assertThat(boolean.class).isNotPrimitive();
+ * assertThat(char.class).isNotPrimitive();
+ *
+ * @return {@code this} assertions object
+ * @throws AssertionError if {@code actual} is {@code null}.
+ * @throws AssertionError if the actual {@code Class} is a primitive type.
+ * @see Class#isPrimitive()
+ */
+ public SELF isNotPrimitive() {
+ isNotNull();
+ if (actual.isPrimitive()) throw assertionError(shouldNotBePrimitive(actual));
+ return myself;
+ }
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractCollectionAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractCollectionAssert.java
index bbf78c325e..9edc18e4ef 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractCollectionAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractCollectionAssert.java
@@ -76,14 +76,14 @@ public SELF isUnmodifiable() {
@SuppressWarnings("unchecked")
private void assertIsUnmodifiable() {
switch (actual.getClass().getName()) {
- case "java.util.Collections$EmptyList":
- case "java.util.Collections$EmptyNavigableSet":
- case "java.util.Collections$EmptySet":
- case "java.util.Collections$EmptySortedSet":
- case "java.util.Collections$SingletonList":
- case "java.util.Collections$SingletonSet":
- // immutable by contract, although not all methods throw UnsupportedOperationException
- return;
+ case "java.util.Collections$EmptyList":
+ case "java.util.Collections$EmptyNavigableSet":
+ case "java.util.Collections$EmptySet":
+ case "java.util.Collections$EmptySortedSet":
+ case "java.util.Collections$SingletonList":
+ case "java.util.Collections$SingletonSet":
+ // immutable by contract, although not all methods throw UnsupportedOperationException
+ return;
}
expectUnsupportedOperationException(() -> actual.add(null), "Collection.add(null)");
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java
index 39830303ca..2a26cb3759 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java
@@ -388,7 +388,7 @@ public SELF hasNotFailed() {
* assertThat(future).succeedsWithin(timeout);
*
* @param timeout the maximum time to wait
- * @return a new assertion object on the the future's result.
+ * @return a new assertion object on the future's result.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} does not succeed within the given timeout.
*/
@@ -405,6 +405,7 @@ private ObjectAssert internalSucceedsWithin(Duration timeout) {
protected ObjectAssert newObjectAssert(RESULT objectUnderTest) {
return new ObjectAssert<>(objectUnderTest);
}
+
/**
* Waits if necessary for at most the given time for this future to complete, and then returns its result for further assertions.
*
@@ -430,7 +431,7 @@ protected ObjectAssert newObjectAssert(RESULT objectUnderTest) {
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
- * @return a new assertion object on the the future's result.
+ * @return a new assertion object on the future's result.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} does not succeed within the given timeout.
*/
@@ -445,7 +446,7 @@ private ObjectAssert internalSucceedsWithin(long timeout, TimeUnit unit)
/**
* Waits if necessary for at most the given time for this future to complete, the {@link InstanceOfAssertFactory}
- * parameter is used to return assertions specific to the the future's result type.
+ * parameter is used to return assertions specific to the future's result type.
*
* If the future's result is not available for any reason an assertion error is thrown.
*
@@ -476,7 +477,7 @@ private ObjectAssert internalSucceedsWithin(long timeout, TimeUnit unit)
/**
* Waits if necessary for at most the given time for this future to complete, the {@link InstanceOfAssertFactory}
- * parameter is used to return assertions specific to the the future's result type.
+ * parameter is used to return assertions specific to the future's result type.
*
* If the future's result is not available for any reason an assertion error is thrown.
*
@@ -577,7 +578,7 @@ private ObjectAssert internalSucceedsWithin(long timeout, TimeUnit unit)
* assertThat(future).failsWithin(Duration.ofMillis(200));
*
* @param timeout the maximum time to wait
- * @return a new assertion instance on the the future's exception.
+ * @return a new assertion instance on the future's exception.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} succeeds within the given timeout.
* @since 3.18.0
@@ -612,7 +613,7 @@ public WithThrowable failsWithin(Duration timeout) {
*
* @param timeout the maximum time to wait
* @param unit the time unit
- * @return a new assertion instance on the the future's exception.
+ * @return a new assertion instance on the future's exception.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} succeeds within the given timeout.
* @since 3.18.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractDateAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractDateAssert.java
index eebb44062e..cb339c5094 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractDateAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractDateAssert.java
@@ -208,7 +208,10 @@ public SELF isEqualTo(Instant instant) {
* @throws AssertionError if actual and given Date represented as String are not equal ignoring hours, minutes,
* seconds and milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringHours(String dateAsString) {
return isEqualToIgnoringHours(parse(dateAsString));
}
@@ -224,7 +227,10 @@ public SELF isEqualToIgnoringHours(String dateAsString) {
* @return this assertion object.
* @throws AssertionError if actual {@code Date} and given {@code Instant} are not equal ignoring hours, minutes, seconds and milliseconds.
* @since 3.19.0
+ * @deprecated Use {@link #isCloseTo(Instant, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringHours(Instant instant) {
return isEqualToIgnoringHours(Date.from(instant));
}
@@ -248,7 +254,10 @@ public SELF isEqualToIgnoringHours(Instant instant) {
* @throws AssertionError if actual and given Date represented as String are not equal ignoring hours, minutes,
* seconds and milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringHours(Date date) {
dates.assertIsEqualWithPrecision(info, actual, date, HOURS);
return myself;
@@ -299,7 +308,10 @@ public SELF isEqualToIgnoringHours(Date date) {
* @throws AssertionError if actual and given Date represented as String are not equal ignoring minutes, seconds and
* milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMinutes(String dateAsString) {
return isEqualToIgnoringMinutes(parse(dateAsString));
}
@@ -315,7 +327,10 @@ public SELF isEqualToIgnoringMinutes(String dateAsString) {
* @return this assertion object.
* @throws AssertionError if actual {@code Date} and given {@code Instant} are not equal ignoring minutes, seconds and milliseconds.
* @since 3.19.0
+ * @deprecated Use {@link #isCloseTo(Instant, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMinutes(Instant instant) {
return isEqualToIgnoringMinutes(Date.from(instant));
}
@@ -340,7 +355,10 @@ public SELF isEqualToIgnoringMinutes(Instant instant) {
* @throws AssertionError if actual and given Date represented as String are not equal ignoring minutes, seconds and
* milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMinutes(Date date) {
dates.assertIsEqualWithPrecision(info, actual, date, MINUTES);
return myself;
@@ -393,7 +411,10 @@ public SELF isEqualToIgnoringMinutes(Date date) {
* @throws AssertionError if actual and given Date represented as String are not equal ignoring seconds and
* milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(String dateAsString) {
return isEqualToIgnoringSeconds(parse(dateAsString));
}
@@ -409,7 +430,10 @@ public SELF isEqualToIgnoringSeconds(String dateAsString) {
* @return this assertion object.
* @throws AssertionError if actual {@code Date} and given {@code Instant} are not equal ignoring seconds and milliseconds.
* @since 3.19.0
+ * @deprecated Use {@link #isCloseTo(Instant, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(Instant instant) {
return isEqualToIgnoringSeconds(Date.from(instant));
}
@@ -434,7 +458,10 @@ public SELF isEqualToIgnoringSeconds(Instant instant) {
* @throws AssertionError if actual and given Date represented as String are not equal ignoring seconds and
* milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(Date date) {
dates.assertIsEqualWithPrecision(info, actual, date, SECONDS);
return myself;
@@ -485,7 +512,10 @@ public SELF isEqualToIgnoringSeconds(Date date) {
* @return this assertion object.
* @throws AssertionError if actual and given Date represented as String are not equal ignoring milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMillis(String dateAsString) {
return isEqualToIgnoringMillis(parse(dateAsString));
}
@@ -501,7 +531,10 @@ public SELF isEqualToIgnoringMillis(String dateAsString) {
* @return this assertion object.
* @throws AssertionError if actual {@code Date} and given {@code Instant} are not equal ignoring milliseconds.
* @since 3.19.0
+ * @deprecated Use {@link #isCloseTo(Instant, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMillis(Instant instant) {
return isEqualToIgnoringMillis(Date.from(instant));
}
@@ -525,7 +558,10 @@ public SELF isEqualToIgnoringMillis(Instant instant) {
* @return this assertion object.
* @throws AssertionError if actual and given Date represented as String are not equal ignoring milliseconds.
* @throws AssertionError if the given date as String could not be converted to a Date.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMillis(Date date) {
dates.assertIsEqualWithPrecision(info, actual, date, MILLISECONDS);
return myself;
@@ -1856,8 +1892,9 @@ public SELF isToday() {
* Verifies that the actual {@code Date} is strictly in the future.
*
* Example:
- *
// assertion will fail
- * assertThat(theTwoTowers.getReleaseDate()).isInTheFuture();
+ * Date now = new Date();
+ * // assertion succeeds:
+ * assertThat(new Date(now.getTime() + 1000)).isInTheFuture();
*
* @return this assertion object.
* @throws AssertionError if the actual {@code Date} is {@code null}.
@@ -2594,7 +2631,10 @@ public SELF isInSameHourWindowAs(String dateAsString) {
* @throws NullPointerException if {@code Date} parameter is {@code null}.
* @throws AssertionError if the actual {@code Date} is {@code null}.
* @throws AssertionError if actual and given {@code Date} are not in the same hour, day, month and year.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isInSameHourAs(Date other) {
dates.assertIsInSameHourAs(info, actual, other);
return myself;
@@ -2638,7 +2678,10 @@ public SELF isInSameHourAs(Date other) {
* @throws NullPointerException if dateAsString parameter is {@code null}.
* @throws AssertionError if the actual {@code Date} is {@code null}.
* @throws AssertionError if actual and given {@code Date} are not in the same hour.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isInSameHourAs(String dateAsString) {
return isInSameHourAs(parse(dateAsString));
}
@@ -2790,7 +2833,10 @@ public SELF isInSameMinuteWindowAs(String dateAsString) {
* @throws NullPointerException if {@code Date} parameter is {@code null}.
* @throws AssertionError if the actual {@code Date} is {@code null}.
* @throws AssertionError if actual and given {@code Date} are not in the same minute.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isInSameMinuteAs(Date other) {
dates.assertIsInSameMinuteAs(info, actual, other);
return myself;
@@ -2834,7 +2880,10 @@ public SELF isInSameMinuteAs(Date other) {
* @throws NullPointerException if dateAsString parameter is {@code null}.
* @throws AssertionError if the actual {@code Date} is {@code null}.
* @throws AssertionError if actual and given {@code Date} are not in the same minute.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isInSameMinuteAs(String dateAsString) {
return isInSameMinuteAs(parse(dateAsString));
}
@@ -3000,7 +3049,10 @@ public SELF isInSameSecondWindowAs(String dateAsString) {
* @throws NullPointerException if {@code Date} parameter is {@code null}.
* @throws AssertionError if the actual {@code Date} is {@code null}.
* @throws AssertionError if actual and given {@code Date} are not in the same second.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isInSameSecondAs(Date other) {
dates.assertIsInSameSecondAs(info, actual, other);
return myself;
@@ -3041,7 +3093,10 @@ public SELF isInSameSecondAs(Date other) {
*
* @param dateAsString the given Date represented as String.
* @return this assertion object.
+ * @deprecated Use {@link #isCloseTo(Date, long)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isInSameSecondAs(String dateAsString) {
return isInSameSecondAs(parse(dateAsString));
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractDoubleArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractDoubleArrayAssert.java
index 73df4b01f1..e66a502c8d 100755
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractDoubleArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractDoubleArrayAssert.java
@@ -216,7 +216,7 @@ public SELF hasSameSizeAs(Iterable> other) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -241,7 +241,7 @@ public SELF contains(double... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -270,7 +270,7 @@ public SELF contains(Double[] values) {
* @param precision the precision under which the value may vary
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -296,7 +296,7 @@ public SELF contains(double[] values, Offset precision) {
* @param precision the precision under which the value may vary
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -330,7 +330,7 @@ public SELF contains(Double[] values, Offset precision) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -356,7 +356,7 @@ public SELF containsOnly(double... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -387,7 +387,7 @@ public SELF containsOnly(Double[] values) {
* @param precision the precision under which the value may vary
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -415,7 +415,7 @@ public SELF containsOnly(double[] values, Offset precision) {
* @param precision the precision under which the value may vary
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -447,7 +447,7 @@ public SELF containsOnly(Double[] values, Offset precision) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -472,7 +472,7 @@ public SELF containsOnlyOnce(double... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -501,7 +501,7 @@ public SELF containsOnlyOnce(Double[] values) {
* @param precision the precision under which the value may vary
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -527,7 +527,7 @@ public SELF containsOnlyOnce(double[] values, Offset precision) {
* @param precision the precision under which the value may vary
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -563,6 +563,7 @@ public SELF containsOnlyOnce(Double[] values, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(double... sequence) {
@@ -585,6 +586,7 @@ public SELF containsSequence(double... sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -615,6 +617,7 @@ public SELF containsSequence(Double[] sequence) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(double[] sequence, Offset precision) {
@@ -642,6 +645,7 @@ public SELF containsSequence(double[] sequence, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -675,6 +679,7 @@ public SELF containsSequence(Double[] sequence, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(double... subsequence) {
@@ -697,6 +702,7 @@ public SELF containsSubsequence(double... subsequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -727,6 +733,7 @@ public SELF containsSubsequence(Double[] subsequence) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(double[] subsequence, Offset precision) {
@@ -754,6 +761,7 @@ public SELF containsSubsequence(double[] subsequence, Offset precision)
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -1058,7 +1066,7 @@ public SELF doesNotHaveDuplicates(Offset precision) {
* @param sequence the sequence of values to look for.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
*/
@@ -1082,7 +1090,7 @@ public SELF startsWith(double... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
* @since 3.19.0
@@ -1114,7 +1122,7 @@ public SELF startsWith(Double[] sequence) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -1143,7 +1151,7 @@ public SELF startsWith(double[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -1174,7 +1182,6 @@ public SELF startsWith(Double[] values, Offset precision) {
* @param sequence the sequence of values to look for.
* @return {@code 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -1198,7 +1205,6 @@ public SELF endsWith(double... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -1230,7 +1236,6 @@ public SELF endsWith(Double[] sequence) {
* @param precision the precision under which the values may vary.
* @return {@code 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -1259,7 +1264,6 @@ public SELF endsWith(double[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -1320,6 +1324,7 @@ public SELF usingDefaultElementComparator() {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -1343,6 +1348,7 @@ public SELF containsExactly(double... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -1371,6 +1377,7 @@ public SELF containsExactly(Double[] values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 2.6.0 / 3.6.0
@@ -1397,6 +1404,7 @@ public SELF containsExactlyInAnyOrder(double... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 3.19.0
@@ -1428,6 +1436,7 @@ public SELF containsExactlyInAnyOrder(Double[] values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values within the specified precision
* with same order, i.e. the actual group contains some or none of the given values, or the actual group contains
* more values than the given ones or values are the same but the order is not.
@@ -1457,6 +1466,7 @@ public SELF containsExactly(double[] values, Offset precision) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values within the specified precision
* with same order, i.e. the actual group contains some or none of the given values, or the actual group contains
* more values than the given ones or values are the same but the order is not.
@@ -1498,7 +1508,7 @@ public SELF usingComparatorWithPrecision(Double precision) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 2.9.0 / 3.9.0
@@ -1525,7 +1535,7 @@ public SELF containsAnyOf(double... values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 3.19.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractFileAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractFileAssert.java
index 15f3848d28..ddf299856a 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractFileAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractFileAssert.java
@@ -740,7 +740,7 @@ public SELF hasParent(String expected) {
* @return {@code this} assertion object.
* @throws NullPointerException if the expected extension is {@code null}.
* @throws AssertionError if the actual {@code File} is {@code null}.
- * @throws AssertionError if the actual {@code File} is not a file (ie a directory).
+ * @throws AssertionError if the actual {@code File} is not a file (i.e., a directory).
* @throws AssertionError if the actual {@code File} does not have the expected extension.
*
* @see Filename extension
@@ -1433,7 +1433,7 @@ public AbstractStringAssert> content(Charset charset) {
*
* @return {@code this} assertion object.
* @throws AssertionError if the actual {@code File} is {@code null}.
- * @throws AssertionError if the actual {@code File} is not a file (ie a directory or does not exist).
+ * @throws AssertionError if the actual {@code File} is not a file (i.e., a directory or does not exist).
* @throws AssertionError if the actual {@code File} does have an extension.
*/
public SELF hasNoExtension() {
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractFloatArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractFloatArrayAssert.java
index 0a63329a56..15ee6e8b6c 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractFloatArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractFloatArrayAssert.java
@@ -216,7 +216,7 @@ public SELF hasSameSizeAs(Iterable> other) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -240,7 +240,7 @@ public SELF contains(float... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -269,7 +269,7 @@ public SELF contains(Float[] values) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -295,7 +295,7 @@ public SELF contains(float[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -329,7 +329,7 @@ public SELF contains(Float[] values, Offset precision) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -355,7 +355,7 @@ public SELF containsOnly(float... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -386,7 +386,7 @@ public SELF containsOnly(Float[] values) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -414,7 +414,7 @@ public SELF containsOnly(float[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -446,7 +446,7 @@ public SELF containsOnly(Float[] values, Offset precision) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -471,7 +471,7 @@ public SELF containsOnlyOnce(float... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -500,7 +500,7 @@ public SELF containsOnlyOnce(Float[] values) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -526,7 +526,7 @@ public SELF containsOnlyOnce(float[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -562,6 +562,7 @@ public SELF containsOnlyOnce(Float[] values, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(float... sequence) {
@@ -584,6 +585,7 @@ public SELF containsSequence(float... sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -614,6 +616,7 @@ public SELF containsSequence(Float[] sequence) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(float[] sequence, Offset precision) {
@@ -641,6 +644,7 @@ public SELF containsSequence(float[] sequence, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -674,6 +678,7 @@ public SELF containsSequence(Float[] sequence, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(float... subsequence) {
@@ -696,6 +701,7 @@ public SELF containsSubsequence(float... subsequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -726,6 +732,7 @@ public SELF containsSubsequence(Float[] subsequence) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(float[] subsequence, Offset precision) {
@@ -753,6 +760,7 @@ public SELF containsSubsequence(float[] subsequence, Offset precision) {
* @return {@code this} assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -868,7 +876,7 @@ public SELF doesNotContain(float... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array contains any of the given values.
* @since 3.19.0
@@ -1057,7 +1065,7 @@ public SELF doesNotHaveDuplicates(Offset precision) {
* @param sequence the sequence of values to look for.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
*/
@@ -1081,7 +1089,7 @@ public SELF startsWith(float... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
* @since 3.19.0
@@ -1113,7 +1121,7 @@ public SELF startsWith(Float[] sequence) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -1142,7 +1150,7 @@ public SELF startsWith(float[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -1173,7 +1181,6 @@ public SELF startsWith(Float[] values, Offset precision) {
* @param sequence the sequence of values to look for.
* @return {@code 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -1197,7 +1204,6 @@ public SELF endsWith(float... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -1229,7 +1235,6 @@ public SELF endsWith(Float[] sequence) {
* @param precision the precision under which the values may vary.
* @return {@code 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -1258,7 +1263,6 @@ public SELF endsWith(float[] values, Offset precision) {
* @param precision the precision under which the values may vary.
* @return {@code 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -1318,6 +1322,7 @@ public SELF usingDefaultElementComparator() {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -1341,6 +1346,7 @@ public SELF containsExactly(float... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -1373,6 +1379,7 @@ public SELF containsExactly(Float[] values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values within the specified precision
* with same order, i.e. the actual group contains some or none of the given values, or the actual group contains
* more values than the given ones or values are the same but the order is not.
@@ -1402,6 +1409,7 @@ public SELF containsExactly(float[] values, Offset precision) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values within the specified precision
* with same order, i.e. the actual group contains some or none of the given values, or the actual group contains
* more values than the given ones or values are the same but the order is not.
@@ -1428,6 +1436,7 @@ public SELF containsExactly(Float[] values, Offset precision) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 2.6.0 / 3.6.0
@@ -1454,6 +1463,7 @@ public SELF containsExactlyInAnyOrder(float... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 3.19.0
@@ -1496,7 +1506,7 @@ public SELF usingComparatorWithPrecision(Float precision) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 2.9.0 / 3.9.0
@@ -1523,7 +1533,7 @@ public SELF containsAnyOf(float... values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 3.19.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractFutureAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractFutureAssert.java
index ff5efd648d..81cbacdeb2 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractFutureAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractFutureAssert.java
@@ -207,7 +207,7 @@ public SELF isNotDone() {
* assertThat(future).succeedsWithin(timeout);
*
* @param timeout the maximum time to wait
- * @return a new assertion object on the the future's result.
+ * @return a new assertion object on the future's result.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} does not succeed within the given timeout.
* @since 3.17.0
@@ -253,7 +253,7 @@ public ObjectAssert succeedsWithin(Duration timeout) {
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
- * @return a new assertion object on the the future's result.
+ * @return a new assertion object on the future's result.
* @throws AssertionError if the actual {@code Future} is {@code null}.
* @throws AssertionError if the actual {@code Future} does not succeed within the given timeout.
* @since 3.17.0
@@ -264,7 +264,7 @@ public ObjectAssert succeedsWithin(long timeout, TimeUnit unit) {
/**
* Waits if necessary for at most the given time for this future to complete, the {@link InstanceOfAssertFactory}
- * parameter is used to return assertions specific to the the future's result type.
+ * parameter is used to return assertions specific to the future's result type.
*
* If the future's result is not available for any reason an assertion error is thrown.
*
@@ -315,7 +315,7 @@ public ObjectAssert succeedsWithin(long timeout, TimeUnit unit) {
/**
* Waits if necessary for at most the given time for this future to complete, the {@link InstanceOfAssertFactory}
- * parameter is used to return assertions specific to the the future's result type.
+ * parameter is used to return assertions specific to the future's result type.
*
* If the future's result is not available for any reason an assertion error is thrown.
*
@@ -393,7 +393,7 @@ public ObjectAssert succeedsWithin(long timeout, TimeUnit unit) {
* assertThat(future).failsWithin(Duration.ofMillis(200));
*
* @param timeout the maximum time to wait
- * @return a new assertion instance on the the future's exception.
+ * @return a new assertion instance on the future's exception.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} succeeds within the given timeout.
* @since 3.18.0
@@ -433,7 +433,7 @@ public WithThrowable failsWithin(Duration timeout) {
*
* @param timeout the maximum time to wait
* @param unit the time unit
- * @return a new assertion instance on the the future's exception.
+ * @return a new assertion instance on the future's exception.
* @throws AssertionError if the actual {@code CompletableFuture} is {@code null}.
* @throws AssertionError if the actual {@code CompletableFuture} succeeds within the given timeout.
* @since 3.18.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractInputStreamAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractInputStreamAssert.java
index 190d3b095f..8342d3b9cf 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractInputStreamAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractInputStreamAssert.java
@@ -14,10 +14,13 @@
import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.error.ShouldBeEmpty.shouldBeEmpty;
+import static org.assertj.core.error.ShouldNotBeEmpty.shouldNotBeEmpty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
@@ -132,14 +135,23 @@ public SELF hasSameContentAs(InputStream expected) {
* @return {@code this} assertion object.
* @throws NullPointerException if the given {@code InputStream} is {@code null}.
* @throws AssertionError if the content of the actual {@code InputStream} is not empty.
- * @throws InputStreamsException if an I/O error occurs.
+ * @throws UncheckedIOException if an I/O error occurs.
* @since 3.17.0
*/
public SELF isEmpty() {
- inputStreams.assertIsEmpty(info, actual);
+ isNotNull();
+ assertIsEmpty();
return myself;
}
+ private void assertIsEmpty() {
+ try {
+ if (actual.read() != -1) throw assertionError(shouldBeEmpty(actual));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
/**
* Verifies that the content of the actual {@code InputStream} is not empty.
*
@@ -155,14 +167,23 @@ public SELF isEmpty() {
* @return {@code this} assertion object.
* @throws NullPointerException if the given {@code InputStream} is {@code null}.
* @throws AssertionError if the content of the actual {@code InputStream} is empty.
- * @throws InputStreamsException if an I/O error occurs.
+ * @throws UncheckedIOException if an I/O error occurs.
* @since 3.17.0
*/
public SELF isNotEmpty() {
- inputStreams.assertIsNotEmpty(info, actual);
+ isNotNull();
+ assertIsNotEmpty();
return myself;
}
+ private void assertIsNotEmpty() {
+ try {
+ if (actual.read() == -1) throw assertionError(shouldNotBeEmpty());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
/**
* Verifies that the content of the actual {@code InputStream} is equal to the given {@code String}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractInstantAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractInstantAssert.java
index 50d1abb4ac..149ebd3d30 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractInstantAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractInstantAssert.java
@@ -16,6 +16,8 @@
import static org.assertj.core.error.ShouldBeAfterOrEqualTo.shouldBeAfterOrEqualTo;
import static org.assertj.core.error.ShouldBeBefore.shouldBeBefore;
import static org.assertj.core.error.ShouldBeBeforeOrEqualTo.shouldBeBeforeOrEqualTo;
+import static org.assertj.core.error.ShouldBeInTheFuture.shouldBeInTheFuture;
+import static org.assertj.core.error.ShouldBeInThePast.shouldBeInThePast;
import static org.assertj.core.util.Preconditions.checkArgument;
import java.time.Instant;
@@ -328,6 +330,44 @@ public SELF isNotIn(String... instantsAsString) {
return isNotIn(convertToInstantArray(instantsAsString));
}
+ /**
+ * Verifies that the actual {@code Instant} is strictly in the past.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(Instant.now().minusSeconds(60)).isInThePast();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code Instant} is {@code null}.
+ * @throws AssertionError if the actual {@code Instant} is not in the past.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInThePast() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isBefore(Instant.now())) throw Failures.instance().failure(info, shouldBeInThePast(actual));
+ return myself;
+ }
+
+ /**
+ * Verifies that the actual {@code Instant} is strictly in the future.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(Instant.now().plusSeconds(60)).isInTheFuture();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code Instant} is {@code null}.
+ * @throws AssertionError if the actual {@code Instant} is not in the future.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInTheFuture() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isAfter(Instant.now())) throw Failures.instance().failure(info, shouldBeInTheFuture(actual));
+ return myself;
+ }
+
/**
* Verifies that the actual {@link Instant} is in the [start, end] period (start and end included).
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractIntArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractIntArrayAssert.java
index ded5ef8bb8..e6d700cae5 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractIntArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractIntArrayAssert.java
@@ -190,7 +190,7 @@ public SELF hasSameSizeAs(Iterable> other) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -215,7 +215,7 @@ public SELF contains(int... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -242,7 +242,7 @@ public SELF contains(Integer[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -268,7 +268,7 @@ public SELF containsOnly(int... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -295,7 +295,7 @@ public SELF containsOnly(Integer[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more than once these values.
@@ -320,7 +320,7 @@ public SELF containsOnlyOnce(int... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -347,6 +347,7 @@ public SELF containsOnlyOnce(Integer[] values) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(int... sequence) {
@@ -369,6 +370,7 @@ public SELF containsSequence(int... sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -393,6 +395,7 @@ public SELF containsSequence(Integer[] sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(int... subsequence) {
@@ -415,6 +418,7 @@ public SELF containsSubsequence(int... subsequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -554,7 +558,7 @@ public SELF doesNotHaveDuplicates() {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
*/
@@ -578,7 +582,7 @@ public SELF startsWith(int... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
* @since 3.19.0
@@ -604,7 +608,6 @@ public SELF startsWith(Integer[] sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -628,7 +631,6 @@ public SELF endsWith(int... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -685,6 +687,7 @@ public SELF usingDefaultElementComparator() {
* @return {@code this} assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given values with same order, i.e. the actual array
* contains some or none of the given values, or the actual array contains more values than the given ones
* or values are the same but the order is not.
@@ -708,6 +711,7 @@ public SELF containsExactly(int... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -736,6 +740,7 @@ public SELF containsExactly(Integer[] values) {
* @return {@code this} assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array
* contains some or none of the given values, or the actual array contains more values than the given ones.
* @since 2.6.0 / 3.6.0
@@ -762,6 +767,7 @@ public SELF containsExactlyInAnyOrder(int... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 3.19.0
@@ -792,7 +798,7 @@ public SELF containsExactlyInAnyOrder(Integer[] values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 2.9.0 / 3.9.0
@@ -822,7 +828,7 @@ public SELF containsAnyOf(int... values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 3.19.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractIterableAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractIterableAssert.java
index d67a94ebac..540c4de78e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractIterableAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractIterableAssert.java
@@ -2623,10 +2623,16 @@ public RecursiveComparisonAssert> usingRecursiveComparison(RecursiveComparison
* {@link java.util.function.Predicate} applied to them (including primitive fields), no fields are excluded, but:
*
* The recursion does not enter into Java Class Library types (java.*, javax.*)
- * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but the collection/array itself)
+ * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but not the collection/array itself)
* The {@link java.util.function.Predicate} is applied to {@link java.util.Map} values but not the map itself or its keys
* The {@link java.util.function.Predicate} is applied to {@link java.util.Optional} and primitive optional values
*
+ * You can change how the recursive assertion deals with arrays, collections, maps and optionals, see:
+ *
+ * {@link RecursiveAssertionAssert#withCollectionAssertionPolicy(RecursiveAssertionConfiguration.CollectionAssertionPolicy)} for collections and arrays
+ * {@link RecursiveAssertionAssert#withMapAssertionPolicy(RecursiveAssertionConfiguration.MapAssertionPolicy)} for maps
+ * {@link RecursiveAssertionAssert#withOptionalAssertionPolicy(RecursiveAssertionConfiguration.OptionalAssertionPolicy)} for optionals
+ *
*
* It is possible to assert several predicates over the object graph in a row.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateAssert.java
index 9fca1e646b..885fe20331 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateAssert.java
@@ -16,6 +16,8 @@
import static org.assertj.core.error.ShouldBeAfterOrEqualTo.shouldBeAfterOrEqualTo;
import static org.assertj.core.error.ShouldBeBefore.shouldBeBefore;
import static org.assertj.core.error.ShouldBeBeforeOrEqualTo.shouldBeBeforeOrEqualTo;
+import static org.assertj.core.error.ShouldBeInTheFuture.shouldBeInTheFuture;
+import static org.assertj.core.error.ShouldBeInThePast.shouldBeInThePast;
import static org.assertj.core.error.ShouldBeToday.shouldBeToday;
import static org.assertj.core.error.ShouldHaveDateField.shouldHaveDateField;
import static org.assertj.core.error.ShouldHaveDateField.shouldHaveMonth;
@@ -311,11 +313,30 @@ public SELF isNotIn(String... localDatesAsString) {
return isNotIn(convertToLocalDateArray(localDatesAsString));
}
+ /**
+ * Verifies that the actual {@code LocalDate} is strictly in the past.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(LocalDate.now().minusDays(1)).isInThePast();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code LocalDate} is {@code null}.
+ * @throws AssertionError if the actual {@code LocalDate} is not in the past.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInThePast() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isBefore(LocalDate.now())) throw Failures.instance().failure(info, shouldBeInThePast(actual));
+ return myself;
+ }
+
/**
* Verifies that the actual {@code LocalDate} is today, that is matching current year, month and day.
*
* Example:
- *
// assertion will pass
+ * // assertion succeeds:
* assertThat(LocalDate.now()).isToday();
*
* // assertion will fail
@@ -331,6 +352,25 @@ public SELF isToday() {
return myself;
}
+ /**
+ * Verifies that the actual {@code LocalDate} is strictly in the future.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(LocalDate.now().plusDays(1)).isInTheFuture();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code LocalDate} is {@code null}.
+ * @throws AssertionError if the actual {@code LocalDate} is not in the future.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInTheFuture() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isAfter(LocalDate.now())) throw Failures.instance().failure(info, shouldBeInTheFuture(actual));
+ return myself;
+ }
+
/**
* Verifies that the actual {@link LocalDate} is in the [start, end] period (start and end included).
*
@@ -482,7 +522,6 @@ public SELF hasYear(int year) {
return myself;
}
-
/**
* Verifies that actual {@code LocalDate} is in the given month.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateTimeAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateTimeAssert.java
index 022c9cae4c..39264e5067 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateTimeAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalDateTimeAssert.java
@@ -18,6 +18,8 @@
import static org.assertj.core.error.ShouldBeEqualIgnoringMinutes.shouldBeEqualIgnoringMinutes;
import static org.assertj.core.error.ShouldBeEqualIgnoringNanos.shouldBeEqualIgnoringNanos;
import static org.assertj.core.error.ShouldBeEqualIgnoringSeconds.shouldBeEqualIgnoringSeconds;
+import static org.assertj.core.error.ShouldBeInTheFuture.shouldBeInTheFuture;
+import static org.assertj.core.error.ShouldBeInThePast.shouldBeInThePast;
import static org.assertj.core.error.ShouldHaveDateField.shouldHaveDateField;
import static org.assertj.core.error.ShouldHaveDateField.shouldHaveMonth;
import static org.assertj.core.util.Preconditions.checkArgument;
@@ -30,6 +32,7 @@
import java.util.Arrays;
import java.util.Comparator;
+import org.assertj.core.data.TemporalOffset;
import org.assertj.core.data.TemporalUnitOffset;
import org.assertj.core.internal.ChronoLocalDateTimeComparator;
import org.assertj.core.internal.Comparables;
@@ -525,9 +528,11 @@ public SELF isEqualToIgnoringNanos(LocalDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code LocalDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code LocalDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code LocalDateTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code LocalDateTime} is are not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(LocalDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(LocalDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertLocalDateTimeParameterIsNotNull(other);
@@ -562,9 +567,11 @@ public SELF isEqualToIgnoringSeconds(LocalDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code LocalDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code LocalDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code LocalDateTime} is are not equal ignoring minute, second and nanosecond
- * fields.
+ * @throws AssertionError if the actual {@code LocalDateTime} is are not equal ignoring minute, second and nanosecond fields.
+ * @deprecated Use {@link #isCloseTo(LocalDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMinutes(LocalDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertLocalDateTimeParameterIsNotNull(other);
@@ -599,9 +606,11 @@ public SELF isEqualToIgnoringMinutes(LocalDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code LocalDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code LocalDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code LocalDateTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code LocalDateTime} is are not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(LocalDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringHours(LocalDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertLocalDateTimeParameterIsNotNull(other);
@@ -611,6 +620,44 @@ public SELF isEqualToIgnoringHours(LocalDateTime other) {
return myself;
}
+ /**
+ * Verifies that the actual {@code LocalDateTime} is strictly in the past.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(LocalDateTime.now().minusMinutes(1)).isInThePast();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code LocalDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code LocalDateTime} is not in the past.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInThePast() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isBefore(LocalDateTime.now())) throw Failures.instance().failure(info, shouldBeInThePast(actual));
+ return myself;
+ }
+
+ /**
+ * Verifies that the actual {@code LocalDateTime} is strictly in the future.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(LocalDateTime.now().plusMinutes(1)).isInTheFuture();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code LocalDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code LocalDateTime} is not in the future.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInTheFuture() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isAfter(LocalDateTime.now())) throw Failures.instance().failure(info, shouldBeInTheFuture(actual));
+ return myself;
+ }
+
/**
* Verifies that the actual {@link LocalDateTime} is in the [start, end] period (start and end included) according to the {@link ChronoLocalDateTime#timeLineOrder()} comparator.
*
@@ -947,6 +994,32 @@ public SELF hasNano(int nano) {
return myself;
}
+ /**
+ * Verifies that the actual {@link LocalDateTime} is close to the other according to the given {@link TemporalOffset}.
+ *
+ * You can build the offset parameter using {@link Assertions#within(long, TemporalUnit)} or {@link Assertions#byLessThan(long, TemporalUnit)}.
+ *
+ * Example:
+ *
LocalDateTime localDateTime = LocalDateTime.now();
+ *
+ * // assertion succeeds:
+ * assertThat(localDateTime).isCloseTo(localDateTime.plusHours(1), within(32, ChronoUnit.MINUTES));
+ *
+ * // assertion fails:
+ * assertThat(localDateTime).isCloseTo(localDateTime.plusHours(1), within(10, ChronoUnit.SECONDS));
+ *
+ * @param other the localDateTime to compare actual to
+ * @param offset the offset used for comparison
+ * @return this assertion object
+ * @throws NullPointerException if {@code LocalDateTime} or {@code TemporalOffset} parameter is {@code null}.
+ * @throws AssertionError if the actual {@code LocalDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code LocalDateTime} is not close to the given one for a provided offset.
+ */
+ @Override
+ public SELF isCloseTo(LocalDateTime other, TemporalOffset super LocalDateTime> offset) {
+ return super.isCloseTo(other, offset);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalTimeAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalTimeAssert.java
index 67298a5cf8..a99cf73521 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalTimeAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractLocalTimeAssert.java
@@ -26,6 +26,7 @@
import java.time.format.DateTimeParseException;
import java.util.Arrays;
+import org.assertj.core.data.TemporalOffset;
import org.assertj.core.internal.Failures;
import org.assertj.core.internal.Objects;
@@ -369,7 +370,10 @@ private static void assertLocalTimeParameterIsNotNull(LocalTime other) {
* @throws AssertionError if the actual {@code LocalTime} is {@code null}.
* @throws IllegalArgumentException if other {@code LocalTime} is {@code null}.
* @throws AssertionError if the actual {@code LocalTime} is are not equal with nanoseconds ignored.
+ * @deprecated Use {@link #isCloseTo(LocalTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringNanos(LocalTime other) {
Objects.instance().assertNotNull(info, actual);
assertLocalTimeParameterIsNotNull(other);
@@ -404,9 +408,11 @@ public SELF isEqualToIgnoringNanos(LocalTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code LocalTime} is {@code null}.
* @throws IllegalArgumentException if other {@code LocalTime} is {@code null}.
- * @throws AssertionError if the actual {@code LocalTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code LocalTime} is are not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(LocalTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(LocalTime other) {
Objects.instance().assertNotNull(info, actual);
assertLocalTimeParameterIsNotNull(other);
@@ -599,12 +605,11 @@ public SELF isStrictlyBetween(String startExclusive, String endExclusive) {
public SELF hasHour(int hour) {
Objects.instance().assertNotNull(info, actual);
if (actual.getHour() != hour) {
- throw Failures.instance().failure(info, shouldHaveDateField(actual,"hour", hour));
+ throw Failures.instance().failure(info, shouldHaveDateField(actual, "hour", hour));
}
return myself;
}
-
/**
* Verifies that actual {@code LocalTime} is in the given minute.
*
@@ -625,7 +630,7 @@ public SELF hasHour(int hour) {
public SELF hasMinute(int minute) {
Objects.instance().assertNotNull(info, actual);
if (actual.getMinute() != minute) {
- throw Failures.instance().failure(info, shouldHaveDateField(actual,"minute", minute));
+ throw Failures.instance().failure(info, shouldHaveDateField(actual, "minute", minute));
}
return myself;
}
@@ -650,7 +655,7 @@ public SELF hasMinute(int minute) {
public SELF hasSecond(int second) {
Objects.instance().assertNotNull(info, actual);
if (actual.getSecond() != second) {
- throw Failures.instance().failure(info, shouldHaveDateField(actual,"second", second));
+ throw Failures.instance().failure(info, shouldHaveDateField(actual, "second", second));
}
return myself;
}
@@ -680,6 +685,12 @@ public SELF hasNano(int nano) {
return myself;
}
+ @Override
+ public SELF isCloseTo(LocalTime other, TemporalOffset super LocalTime> offset) {
+ // overridden for javadoc link
+ return super.isCloseTo(other, offset);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractLongArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractLongArrayAssert.java
index 17d5c06d90..b695202d58 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractLongArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractLongArrayAssert.java
@@ -190,7 +190,7 @@ public SELF hasSameSizeAs(Iterable> other) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -215,7 +215,7 @@ public SELF contains(long... values) {
* @param values the given {@code Long} array of values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -242,7 +242,7 @@ public SELF contains(Long[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -268,7 +268,7 @@ public SELF containsOnly(long... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -295,7 +295,7 @@ public SELF containsOnly(Long[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -320,7 +320,7 @@ public SELF containsOnlyOnce(long... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -347,6 +347,7 @@ public SELF containsOnlyOnce(Long[] values) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(long... sequence) {
@@ -369,6 +370,7 @@ public SELF containsSequence(long... sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -393,6 +395,7 @@ public SELF containsSequence(Long[] sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(long... subsequence) {
@@ -415,6 +418,7 @@ public SELF containsSubsequence(long... subsequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -554,7 +558,7 @@ public SELF doesNotHaveDuplicates() {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
*/
@@ -578,7 +582,7 @@ public SELF startsWith(long... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
* @since 3.19.0
@@ -604,7 +608,6 @@ public SELF startsWith(Long[] sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -628,7 +631,6 @@ public SELF endsWith(long... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -685,6 +687,7 @@ public SELF usingDefaultElementComparator() {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -710,6 +713,7 @@ public SELF containsExactly(long... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -738,6 +742,7 @@ public SELF containsExactly(Long[] values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 2.6.0 / 3.6.0
@@ -764,6 +769,7 @@ public SELF containsExactlyInAnyOrder(long... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 3.19.0
@@ -794,7 +800,7 @@ public SELF containsExactlyInAnyOrder(Long[] values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 2.9.0 / 3.9.0
@@ -824,7 +830,7 @@ public SELF containsAnyOf(long... values) {
* @param values the array of values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 3.19.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractMapAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractMapAssert.java
index 3c59cad6b6..abba372457 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractMapAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractMapAssert.java
@@ -1428,18 +1428,19 @@ public SELF isUnmodifiable() {
@SuppressWarnings("unchecked")
private void assertIsUnmodifiable() {
switch (actual.getClass().getName()) {
- case "java.util.Collections$EmptyNavigableMap":
- case "java.util.Collections$EmptyMap":
- case "java.util.Collections$EmptySortedMap":
- case "java.util.Collections$SingletonMap":
- // unmodifiable by contract, although not all methods throw UnsupportedOperationException
- return;
+ case "java.util.Collections$EmptyNavigableMap":
+ case "java.util.Collections$EmptyMap":
+ case "java.util.Collections$EmptySortedMap":
+ case "java.util.Collections$SingletonMap":
+ // unmodifiable by contract, although not all methods throw UnsupportedOperationException
+ return;
}
expectUnsupportedOperationException(() -> actual.clear(), "Map.clear()");
expectUnsupportedOperationException(() -> actual.compute(null, (k, v) -> v), "Map.compute(null, (k, v) -> v)");
expectUnsupportedOperationException(() -> actual.computeIfAbsent(null, k -> null), "Map.computeIfAbsent(null, k -> null)");
- expectUnsupportedOperationException(() -> actual.computeIfPresent(null, (k, v) -> v), "Map.computeIfPresent(null, (k, v) -> v)");
+ expectUnsupportedOperationException(() -> actual.computeIfPresent(null, (k, v) -> v),
+ "Map.computeIfPresent(null, (k, v) -> v)");
expectUnsupportedOperationException(() -> actual.merge(null, null, (v1, v2) -> v1), "Map.merge(null, null, (v1, v2) -> v1))");
expectUnsupportedOperationException(() -> actual.put(null, null), "Map.put(null, null)");
expectUnsupportedOperationException(() -> actual.putAll(new HashMap<>()), "Map.putAll(new HashMap<>())");
@@ -1451,7 +1452,7 @@ private void assertIsUnmodifiable() {
expectUnsupportedOperationException(() -> actual.replaceAll((k, v) -> v), "Map.replaceAll((k, v) -> v)");
if (actual instanceof NavigableMap) {
- NavigableMap navigableMap = (NavigableMap ) actual;
+ NavigableMap navigableMap = (NavigableMap) actual;
expectUnsupportedOperationException(() -> navigableMap.pollFirstEntry(), "NavigableMap.pollFirstEntry()");
expectUnsupportedOperationException(() -> navigableMap.pollLastEntry(), "NavigableMap.pollLastEntry()");
}
@@ -2116,10 +2117,16 @@ public RecursiveComparisonAssert> usingRecursiveComparison(RecursiveComparison
* {@link java.util.function.Predicate} applied to them (including primitive fields), no fields are excluded, but:
*
* The recursion does not enter into Java Class Library types (java.*, javax.*)
- * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but the collection/array itself)
+ * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but not the collection/array itself)
* The {@link java.util.function.Predicate} is applied to {@link java.util.Map} values but not the map itself or its keys
* The {@link java.util.function.Predicate} is applied to {@link java.util.Optional} and primitive optional values
*
+ * You can change how the recursive assertion deals with arrays, collections, maps and optionals, see:
+ *
+ * {@link RecursiveAssertionAssert#withCollectionAssertionPolicy(RecursiveAssertionConfiguration.CollectionAssertionPolicy)} for collections and arrays
+ * {@link RecursiveAssertionAssert#withMapAssertionPolicy(RecursiveAssertionConfiguration.MapAssertionPolicy)} for maps
+ * {@link RecursiveAssertionAssert#withOptionalAssertionPolicy(RecursiveAssertionConfiguration.OptionalAssertionPolicy)} for optionals
+ *
*
* It is possible to assert several predicates over the object graph in a row.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractMatcherAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractMatcherAssert.java
index 22335b1f52..7da3a8271f 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractMatcherAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractMatcherAssert.java
@@ -18,14 +18,12 @@
import org.assertj.core.internal.Failures;
-
/**
* Assertions for {@link java.util.regex.Matcher}
*
* @author Jiashu Zhang
*/
-public abstract class AbstractMatcherAssert> extends
- AbstractAssert {
+public abstract class AbstractMatcherAssert> extends AbstractAssert {
protected AbstractMatcherAssert(Matcher actual, Class> selfType) {
super(actual, selfType);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectArrayAssert.java
index aeb945b0f8..6da09c3d40 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectArrayAssert.java
@@ -346,7 +346,7 @@ public SELF hasSameSizeAs(Iterable> other) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -391,7 +391,7 @@ protected SELF containsForProxy(ELEMENT[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -559,7 +559,7 @@ public SELF hasSameElementsAs(Iterable extends ELEMENT> iterable) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more than once these values.
@@ -602,6 +602,7 @@ public SELF containsOnlyOnceElementsOf(Iterable extends ELEMENT> iterable) {
* @return {@code this} assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given values with same order, i.e. the actual array
* contains some or none of the given values, or the actual array contains more values than the given ones
* or values are the same but the order is not.
@@ -637,6 +638,7 @@ protected SELF containsExactlyForProxy(ELEMENT[] values) {
* @return {@code this} assertion object.
* @throws NullPointerException if the given argument is {@code null}.
* @throws AssertionError if the actual array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual arrray does not contain the given values, i.e. the actual array
* contains some or none of the given values, or the actual array contains more values than the given ones.
*/
@@ -702,6 +704,7 @@ public SELF containsExactlyElementsOf(Iterable extends ELEMENT> iterable) {
* @return this assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
@Override
@@ -738,6 +741,7 @@ protected SELF containsSequenceForProxy(ELEMENT[] sequence) {
* @return this assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty iterable and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
@Override
@@ -1152,7 +1156,7 @@ public SELF doesNotHaveDuplicates() {
* @param sequence the 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence of objects.
*/
@@ -3832,7 +3836,7 @@ protected SELF satisfiesOnlyOnceForProxy(Consumer super ELEMENT> requirements)
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 2.9.0 / 3.9.0
@@ -3871,7 +3875,7 @@ protected SELF containsAnyOfForProxy(ELEMENT[] values) {
* @param iterable the iterable whose at least one element is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the iterable of expected values is {@code null}.
- * @throws IllegalArgumentException if the iterable of expected values is empty and the array under test is not empty.
+ * @throws AssertionError if the iterable of expected values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of elements from the given {@code Iterable}.
* @since 2.9.0 / 3.9.0
@@ -4015,10 +4019,16 @@ public RecursiveComparisonAssert> usingRecursiveComparison(RecursiveComparison
* {@link java.util.function.Predicate} applied to them (including primitive fields), no fields are excluded, but:
*
* The recursion does not enter into Java Class Library types (java.*, javax.*)
- * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but the collection/array itself)
+ * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but not the collection/array itself)
* The {@link java.util.function.Predicate} is applied to {@link java.util.Map} values but not the map itself or its keys
* The {@link java.util.function.Predicate} is applied to {@link java.util.Optional} and primitive optional values
*
+ * You can change how the recursive assertion deals with arrays, collections, maps and optionals, see:
+ *
+ * {@link RecursiveAssertionAssert#withCollectionAssertionPolicy(RecursiveAssertionConfiguration.CollectionAssertionPolicy)} for collections and arrays
+ * {@link RecursiveAssertionAssert#withMapAssertionPolicy(RecursiveAssertionConfiguration.MapAssertionPolicy)} for maps
+ * {@link RecursiveAssertionAssert#withOptionalAssertionPolicy(RecursiveAssertionConfiguration.OptionalAssertionPolicy)} for optionals
+ *
*
* It is possible to assert several predicates over the object graph in a row.
*
@@ -4098,7 +4108,7 @@ public RecursiveAssertionAssert usingRecursiveAssertion() {
public RecursiveAssertionAssert usingRecursiveAssertion(RecursiveAssertionConfiguration recursiveAssertionConfiguration) {
return super.usingRecursiveAssertion(recursiveAssertionConfiguration);
}
-
+
/**
* Verifies that the array under test contains a single element and allows to perform assertions on that element.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectAssert.java
index a1aa1ed8a6..199d3df4de 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractObjectAssert.java
@@ -1096,12 +1096,14 @@ public SELF isEqualToComparingFieldByFieldRecursively(Object other) {
* @param from {@link Function} used to acquire the value to test from the object under test. Must not be {@code null}
* @param the expected value type the given {@code method} returns.
* @return {@code this} assertion object.
+ * @throws AssertionError if {@code actual} is {@code null}
* @throws NullPointerException if given {@code from} function is null
*
* @see #usingComparatorForType(Comparator, Class)
*/
public SELF returns(T expected, Function from) {
requireNonNull(from, "The given getter method/Function must not be null");
+ isNotNull();
Objects objects = getComparatorBasedObjectAssertions(expected);
objects.assertEqual(info, from.apply(actual), expected);
return myself;
@@ -1125,6 +1127,7 @@ public SELF returns(T expected, Function from) {
* @param from {@link Function} used to acquire the value to test from the object under test. Must not be {@code null}
* @param the expected value type the given {@code method} returns.
* @return {@code this} assertion object.
+ * @throws AssertionError if {@code actual} is {@code null}
* @throws NullPointerException if given {@code from} function is null
*
* @since 3.22.0
@@ -1132,6 +1135,7 @@ public SELF returns(T expected, Function from) {
*/
public SELF doesNotReturn(T expected, Function from) {
requireNonNull(from, "The given getter method/Function must not be null");
+ isNotNull();
Objects objects = getComparatorBasedObjectAssertions(expected);
objects.assertNotEqual(info, from.apply(actual), expected);
return myself;
@@ -1235,14 +1239,20 @@ public RecursiveComparisonAssert> usingRecursiveComparison(RecursiveComparison
*
* The recursive algorithm employs cycle detection, so object graphs with cyclic references can safely be asserted over without causing looping.
*
- * This method enables recursive asserting using default configuration, which means all fields of all objects have the
+ *
This method enables recursive asserting using default configuration, which means all fields of all objects have the
* {@link java.util.function.Predicate} applied to them (including primitive fields), no fields are excluded, but:
*
* The recursion does not enter into Java Class Library types (java.*, javax.*)
- * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but the collection/array itself)
+ * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but not the collection/array itself)
* The {@link java.util.function.Predicate} is applied to {@link java.util.Map} values but not the map itself or its keys
* The {@link java.util.function.Predicate} is applied to {@link java.util.Optional} and primitive optional values
*
+ * You can change how the recursive assertion deals with arrays, collections, maps and optionals, see:
+ *
+ * {@link RecursiveAssertionAssert#withCollectionAssertionPolicy(RecursiveAssertionConfiguration.CollectionAssertionPolicy)} for collections and arrays
+ * {@link RecursiveAssertionAssert#withMapAssertionPolicy(RecursiveAssertionConfiguration.MapAssertionPolicy)} for maps
+ * {@link RecursiveAssertionAssert#withOptionalAssertionPolicy(RecursiveAssertionConfiguration.OptionalAssertionPolicy)} for optionals
+ *
*
* It is possible to assert several predicates over the object graph in a row.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetDateTimeAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetDateTimeAssert.java
index ffee58a757..c404c6c55b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetDateTimeAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetDateTimeAssert.java
@@ -20,6 +20,8 @@
import static org.assertj.core.error.ShouldBeEqualIgnoringNanos.shouldBeEqualIgnoringNanos;
import static org.assertj.core.error.ShouldBeEqualIgnoringSeconds.shouldBeEqualIgnoringSeconds;
import static org.assertj.core.error.ShouldBeEqualIgnoringTimezone.shouldBeEqualIgnoringTimezone;
+import static org.assertj.core.error.ShouldBeInTheFuture.shouldBeInTheFuture;
+import static org.assertj.core.error.ShouldBeInThePast.shouldBeInThePast;
import static org.assertj.core.util.Preconditions.checkArgument;
import java.time.OffsetDateTime;
@@ -27,6 +29,7 @@
import java.time.temporal.TemporalUnit;
import java.util.Comparator;
+import org.assertj.core.data.TemporalOffset;
import org.assertj.core.data.TemporalUnitOffset;
import org.assertj.core.internal.Comparables;
import org.assertj.core.internal.ComparatorBasedComparisonStrategy;
@@ -408,6 +411,32 @@ public SELF isCloseToUtcNow(TemporalUnitOffset offset) {
return isCloseTo(now(systemUTC()), offset);
}
+ /**
+ * Verifies that the actual {@link OffsetDateTime} is close to the other according to the given {@link TemporalOffset}.
+ *
+ * You can build the offset parameter using {@link Assertions#within(long, TemporalUnit)} or {@link Assertions#byLessThan(long, TemporalUnit)}.
+ *
+ * Example:
+ *
OffsetDateTime offsetDateTime = OffsetDateTime.parse("2000-01-01T00:00:00Z");
+ *
+ * // assertions succeeds:
+ * assertThat(offsetDateTime).isCloseTo(offsetDateTime.plusHours(1), within(90, ChronoUnit.MINUTES));
+ *
+ * // assertions fails:
+ * assertThat(offsetDateTime).isCloseTo(offsetDateTime.plusHours(1), within(30, ChronoUnit.MINUTES));
+ * @param other the OffsetDateTime to compare actual to
+ * @param offset the offset used for comparison
+ * @return this assertion object
+ * @throws NullPointerException if {@code OffsetDateTime} or {@code TemporalOffset} parameter is {@code null}.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is not close to the given one for a provided offset.
+ */
+ @Override
+ public SELF isCloseTo(OffsetDateTime other, TemporalOffset super OffsetDateTime> offset) {
+ // overridden for javadoc
+ return super.isCloseTo(other, offset);
+ }
+
/**
* Same assertion as {@link #isEqualTo(Object)} (where Object is expected to be {@link java.time.OffsetDateTime}) but
* here you
@@ -446,7 +475,6 @@ public SELF isEqualTo(String dateTimeAsString) {
return isEqualTo(parse(dateTimeAsString));
}
-
/**
* Verifies that the actual {@code OffsetDateTime} is not equal to the given value according to the comparator in use.
*
@@ -593,7 +621,10 @@ public SELF isNotIn(String... dateTimesAsString) {
* @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code OffsetDateTime} is {@code null}.
* @throws AssertionError if the actual {@code OffsetDateTime} is are not equal with nanoseconds ignored.
+ * @deprecated Use {@link #isCloseTo(OffsetDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringNanos(OffsetDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertOffsetDateTimeParameterIsNotNull(other);
@@ -605,8 +636,7 @@ public SELF isEqualToIgnoringNanos(OffsetDateTime other) {
/**
* Verifies that actual and given {@code OffsetDateTime} have same year, month, day, hour, minute, second and
- * nanosecond fields,
- * (timezone fields are ignored in comparison).
+ * nanosecond fields (timezone fields are ignored in comparison).
*
* Code example :
*
// successful assertions
@@ -660,9 +690,11 @@ public SELF isEqualToIgnoringTimezone(OffsetDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code OffsetDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code OffsetDateTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is are not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(OffsetDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(OffsetDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertOffsetDateTimeParameterIsNotNull(other);
@@ -685,9 +717,9 @@ public SELF isEqualToIgnoringSeconds(OffsetDateTime other) {
*
* Code example :
*
// successful assertions
- * OffsetDateTime OffsetDateTime1 = OffsetDateTime.of(2000, 1, 1, 23, 50, 0, 0, ZoneOffset.UTC);
- * OffsetDateTime OffsetDateTime2 = OffsetDateTime.of(2000, 1, 1, 23, 00, 2, 7, ZoneOffset.UTC);
- * assertThat(OffsetDateTime1).isEqualToIgnoringMinutes(OffsetDateTime2);
+ * OffsetDateTime offsetDateTime1 = OffsetDateTime.of(2000, 1, 1, 23, 50, 0, 0, ZoneOffset.UTC);
+ * OffsetDateTime offsetDateTime2 = OffsetDateTime.of(2000, 1, 1, 23, 00, 2, 7, ZoneOffset.UTC);
+ * assertThat(offsetDateTime1).isEqualToIgnoringMinutes(offsetDateTime2);
*
* // failing assertions (even if time difference is only 1ms)
* OffsetDateTime OffsetDateTimeA = OffsetDateTime.of(2000, 1, 1, 01, 00, 00, 000, ZoneOffset.UTC);
@@ -698,9 +730,11 @@ public SELF isEqualToIgnoringSeconds(OffsetDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code OffsetDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code OffsetDateTime} is are not equal ignoring minute, second and nanosecond
- * fields.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is are not equal ignoring minute, second and nanosecond fields.
+ * @deprecated Use {@link #isCloseTo(OffsetDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringMinutes(OffsetDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertOffsetDateTimeParameterIsNotNull(other);
@@ -735,9 +769,11 @@ public SELF isEqualToIgnoringMinutes(OffsetDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code OffsetDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code OffsetDateTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is are not equal with hour, minute, second and nanosecond ignored.
+ * @deprecated Use {@link #isCloseTo(OffsetDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringHours(OffsetDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertOffsetDateTimeParameterIsNotNull(other);
@@ -747,6 +783,46 @@ public SELF isEqualToIgnoringHours(OffsetDateTime other) {
return myself;
}
+ /**
+ * Verifies that the actual {@code OffsetDateTime} is strictly in the past.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(OffsetDateTime.now().minusMinutes(1)).isInThePast();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is not in the past.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInThePast() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isBefore(OffsetDateTime.now(actual.getOffset())))
+ throw Failures.instance().failure(info, shouldBeInThePast(actual));
+ return myself;
+ }
+
+ /**
+ * Verifies that the actual {@code OffsetDateTime} is strictly in the future.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(OffsetDateTime.now().plusMinutes(1)).isInTheFuture();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code OffsetDateTime} is not in the future.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInTheFuture() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isAfter(OffsetDateTime.now(actual.getOffset())))
+ throw Failures.instance().failure(info, shouldBeInTheFuture(actual));
+ return myself;
+ }
+
/**
* Verifies that the actual {@link OffsetDateTime} is in the [start, end] period (start and end included) according to the comparator in use.
*
@@ -778,8 +854,8 @@ public SELF isEqualToIgnoringHours(OffsetDateTime other) {
* .isBetween(parse("2010-01-01T01:00:00+01:00"),
* parse("2010-01-01T01:00:00+01:00"));
*
- * @param startExclusive the start value (exclusive), expected not to be null.
- * @param endExclusive the end value (exclusive), expected not to be null.
+ * @param startInclusive the start value (inclusive), expected not to be null.
+ * @param endInclusive the end value (inclusive), expected not to be null.
* @return this assertion object.
* @throws AssertionError if the actual value is {@code null}.
* @throws NullPointerException if start value is {@code null}.
@@ -788,8 +864,8 @@ public SELF isEqualToIgnoringHours(OffsetDateTime other) {
*
* @since 3.7.1
*/
- public SELF isBetween(OffsetDateTime startExclusive, OffsetDateTime endExclusive) {
- comparables.assertIsBetween(info, actual, startExclusive, endExclusive, true, true);
+ public SELF isBetween(OffsetDateTime startInclusive, OffsetDateTime endInclusive) {
+ comparables.assertIsBetween(info, actual, startInclusive, endInclusive, true, true);
return myself;
}
@@ -816,8 +892,8 @@ public SELF isBetween(OffsetDateTime startExclusive, OffsetDateTime endExclusive
* // assertion fails:
* assertThat(firstOfJanuary2000).isBetween("1999-01-01T00:00:01Z", "1999-12-31T23:59:59Z");
*
- * @param startExclusive the start value (exclusive), expected not to be null.
- * @param endExclusive the end value (exclusive), expected not to be null.
+ * @param startInclusive the start value (inclusive), expected not to be null.
+ * @param endInclusive the end value (inclusive), expected not to be null.
* @return this assertion object.
*
* @throws AssertionError if the actual value is {@code null}.
@@ -828,8 +904,8 @@ public SELF isBetween(OffsetDateTime startExclusive, OffsetDateTime endExclusive
*
* @since 3.7.1
*/
- public SELF isBetween(String startExclusive, String endExclusive) {
- return isBetween(parse(startExclusive), parse(endExclusive));
+ public SELF isBetween(String startInclusive, String endInclusive) {
+ return isBetween(parse(startInclusive), parse(endInclusive));
}
/**
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetTimeAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetTimeAssert.java
index 046beaedd0..abab91739e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetTimeAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractOffsetTimeAssert.java
@@ -24,7 +24,9 @@
import java.time.OffsetTime;
import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalUnit;
+import org.assertj.core.data.TemporalOffset;
import org.assertj.core.internal.Failures;
import org.assertj.core.internal.Objects;
@@ -385,7 +387,10 @@ private static void assertOffsetTimeParameterIsNotNull(OffsetTime other) {
* @throws AssertionError if the actual {@code OffsetTime} is {@code null}.
* @throws IllegalArgumentException if other {@code OffsetTime} is {@code null}.
* @throws AssertionError if the actual {@code OffsetTime} is not equal with nanoseconds ignored.
+ * @deprecated Use {@link #isCloseTo(OffsetTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringNanos(OffsetTime other) {
Objects.instance().assertNotNull(info, actual);
assertOffsetTimeParameterIsNotNull(other);
@@ -421,9 +426,11 @@ public SELF isEqualToIgnoringNanos(OffsetTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code OffsetTime} is {@code null}.
* @throws IllegalArgumentException if other {@code OffsetTime} is {@code null}.
- * @throws AssertionError if the actual {@code OffsetTime} is not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code OffsetTime} is not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(OffsetTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(OffsetTime other) {
Objects.instance().assertNotNull(info, actual);
assertOffsetTimeParameterIsNotNull(other);
@@ -625,6 +632,33 @@ public SELF isStrictlyBetween(String startExclusive, String endExclusive) {
return isStrictlyBetween(parse(startExclusive), parse(endExclusive));
}
+ /**
+ * Verifies that the actual {@link OffsetTime} is close to the other according to the given {@link TemporalOffset}.
+ *
+ * You can build the offset parameter using {@link Assertions#within(long, TemporalUnit)} or {@link Assertions#byLessThan(long, TemporalUnit)}.
+ *
+ * Example:
+ *
OffsetTime oneAm = OffsetTime.parse("01:00:00+02:00");
+ *
+ * // assertion succeeds:
+ * assertThat(oneAm).isCloseTo(oneAm.plusHours(1), within(30, ChronoUnit.MINUTES));
+ *
+ * // assertion fails:
+ * assertThat(oneAm).isCloseTo(_07_42, within(10, ChronoUnit.SECONDS));
+ *
+ * @param other the offsetTime to compare actual to
+ * @param offset the offset used for comparison
+ * @return this assertion object
+ * @throws NullPointerException if {@code OffsetTime} or {@code TemporalOffset} parameter is {@code null}.
+ * @throws AssertionError if the actual {@code OffsetTime} is {@code null}.
+ * @throws AssertionError if the actual {@code OffsetTime} is not close to the given one within the provided offset.
+ */
+ @Override
+ public SELF isCloseTo(OffsetTime other, TemporalOffset super OffsetTime> offset) {
+ // overridden for javadoc
+ return super.isCloseTo(other, offset);
+ }
+
/**
* {@inheritDoc}
*/
@@ -639,8 +673,7 @@ protected OffsetTime parse(String offsetTimeAsString) {
*
* @param actual the actual OffsetTime. expected not be null
* @param other the other OffsetTime. expected not be null
- * @return true if both OffsetTime are in the same year, month and day of month, hour, minute and second, false
- * otherwise.
+ * @return true if both OffsetTime are in the same year, month and day of month, hour, minute and second, false otherwise.
*/
private static boolean areEqualIgnoringNanos(OffsetTime actual, OffsetTime other) {
return areEqualIgnoringSeconds(actual, other) && haveSameSecond(actual, other);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalAssert.java
index a9fb1d0596..edcbcebe6d 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalAssert.java
@@ -46,7 +46,7 @@
* @author Grzegorz Piwowarek
*/
// Deprecation is raised by JDK-17. IntelliJ thinks this is redundant when it is not.
-@SuppressWarnings({"deprecation", "RedundantSuppression"})
+@SuppressWarnings({ "deprecation", "RedundantSuppression" })
public abstract class AbstractOptionalAssert, VALUE> extends
AbstractAssert> {
@@ -280,7 +280,7 @@ public SELF containsInstanceOf(Class> clazz) {
*/
@Deprecated
@CheckReturnValue
- @SuppressWarnings({"DeprecatedIsStillUsed", "deprecation"})
+ @SuppressWarnings({ "DeprecatedIsStillUsed", "deprecation" })
public SELF usingFieldByFieldValueComparator() {
return usingValueComparator(new FieldByFieldComparator());
}
@@ -557,10 +557,16 @@ public RecursiveComparisonAssert> usingRecursiveComparison(RecursiveComparison
* {@link java.util.function.Predicate} applied to them (including primitive fields), no fields are excluded, but:
*
* The recursion does not enter into Java Class Library types (java.*, javax.*)
- * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but the collection/array itself)
+ * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but not the collection/array itself)
* The {@link java.util.function.Predicate} is applied to {@link java.util.Map} values but not the map itself or its keys
* The {@link java.util.function.Predicate} is applied to {@link java.util.Optional} and primitive optional values
*
+ * You can change how the recursive assertion deals with arrays, collections, maps and optionals, see:
+ *
+ * {@link RecursiveAssertionAssert#withCollectionAssertionPolicy(RecursiveAssertionConfiguration.CollectionAssertionPolicy)} for collections and arrays
+ * {@link RecursiveAssertionAssert#withMapAssertionPolicy(RecursiveAssertionConfiguration.MapAssertionPolicy)} for maps
+ * {@link RecursiveAssertionAssert#withOptionalAssertionPolicy(RecursiveAssertionConfiguration.OptionalAssertionPolicy)} for optionals
+ *
*
* It is possible to assert several predicates over the object graph in a row.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalDoubleAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalDoubleAssert.java
index 16ffbce319..40407931c3 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalDoubleAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalDoubleAssert.java
@@ -34,8 +34,8 @@
* @author Alexander Bischof
* @author Grzegorz Piwowarek
*/
-public abstract class AbstractOptionalDoubleAssert> extends
- AbstractAssert {
+public abstract class AbstractOptionalDoubleAssert>
+ extends AbstractAssert {
@VisibleForTesting
Doubles doubles = Doubles.instance();
@@ -77,7 +77,7 @@ public SELF isPresent() {
public SELF isNotPresent() {
return isEmpty();
}
-
+
/**
* Verifies that the actual {@link java.util.OptionalDouble} is empty.
*
@@ -203,7 +203,8 @@ public SELF hasValueCloseTo(Double expectedValue, Percentage percentage) {
try {
doubles.assertIsCloseToPercentage(info, actual.getAsDouble(), expectedValue, percentage);
} catch (AssertionError assertionError) {
- throwAssertionError(shouldHaveValueCloseToPercentage(actual, expectedValue, percentage, abs(expectedValue - actual.getAsDouble())));
+ throwAssertionError(shouldHaveValueCloseToPercentage(actual, expectedValue, percentage,
+ abs(expectedValue - actual.getAsDouble())));
}
return myself;
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalIntAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalIntAssert.java
index c063a50d58..2bf8e54b15 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalIntAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractOptionalIntAssert.java
@@ -72,7 +72,7 @@ public SELF isPresent() {
public SELF isNotPresent() {
return isEmpty();
}
-
+
/**
* Verifies that the actual {@link java.util.OptionalInt} is empty.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractShortArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractShortArrayAssert.java
index 673e7ca7d1..b2a4d21c4d 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractShortArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractShortArrayAssert.java
@@ -191,7 +191,7 @@ public SELF hasSameSizeAs(Iterable> other) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
*/
@@ -216,7 +216,7 @@ public SELF contains(short... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.19.0
@@ -243,7 +243,7 @@ public SELF contains(Short[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values.
* @since 3.16.0
@@ -269,7 +269,7 @@ public SELF contains(int... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -295,7 +295,7 @@ public SELF containsOnly(short... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -323,7 +323,7 @@ public SELF containsOnly(Short[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not contain the given values, i.e. the actual array contains some
* or none of the given values, or the actual array contains more values than the given ones.
@@ -349,7 +349,7 @@ public SELF containsOnly(int... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -374,7 +374,7 @@ public SELF containsOnlyOnce(short... values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -401,7 +401,7 @@ public SELF containsOnlyOnce(Short[] values) {
* @param values the given values.
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group contains some
* or none of the given values, or the actual group contains more than once these values.
@@ -427,6 +427,7 @@ public SELF containsOnlyOnce(int... values) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
*/
public SELF containsSequence(short... sequence) {
@@ -449,6 +450,7 @@ public SELF containsSequence(short... sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.19.0
*/
@@ -473,6 +475,7 @@ public SELF containsSequence(Short[] sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given sequence.
* @since 3.16.0
*/
@@ -496,6 +499,7 @@ public SELF containsSequence(int... sequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
*/
public SELF containsSubsequence(short... subsequence) {
@@ -518,6 +522,7 @@ public SELF containsSubsequence(short... subsequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.19.0
*/
@@ -542,6 +547,7 @@ public SELF containsSubsequence(Short[] subsequence) {
* @return myself assertion object.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the given array is {@code null}.
+ * @throws AssertionError if the given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array does not contain the given subsequence.
* @since 3.16.0
*/
@@ -755,7 +761,7 @@ public SELF doesNotHaveDuplicates() {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
*/
@@ -779,7 +785,7 @@ public SELF startsWith(short... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
* @since 3.19.0
@@ -805,7 +811,7 @@ public SELF startsWith(Short[] sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual array is {@code null}.
* @throws AssertionError if the actual array does not start with the given sequence.
* @since 3.16.0
@@ -830,7 +836,6 @@ public SELF startsWith(int... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
*/
@@ -854,7 +859,6 @@ public SELF endsWith(short... sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.19.0
@@ -880,7 +884,6 @@ public SELF endsWith(Short[] sequence) {
* @param sequence the sequence of values to look for.
* @return myself 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 array is {@code null}.
* @throws AssertionError if the actual array does not end with the given sequence.
* @since 3.16.0
@@ -936,6 +939,7 @@ public SELF usingDefaultElementComparator() {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -961,6 +965,7 @@ public SELF containsExactly(short... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -988,6 +993,7 @@ public SELF containsExactly(Short[] values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values with same order, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones
* or values are the same but the order is not.
@@ -1015,6 +1021,7 @@ public SELF containsExactly(int... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 2.6.0 / 3.6.0
@@ -1041,6 +1048,7 @@ public SELF containsExactlyInAnyOrder(short... values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 3.19.0
@@ -1068,6 +1076,7 @@ public SELF containsExactlyInAnyOrder(Short[] values) {
* @return {@code 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 given argument is an empty array and the actual array is not empty.
* @throws AssertionError if the actual group does not contain the given values, i.e. the actual group
* contains some or none of the given values, or the actual group contains more values than the given ones.
* @since 3.16.0
@@ -1097,7 +1106,7 @@ public SELF containsExactlyInAnyOrder(int... values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 2.9.0 / 3.9.0
@@ -1127,7 +1136,7 @@ public SELF containsAnyOf(short... values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 3.19.0
@@ -1158,7 +1167,7 @@ public SELF containsAnyOf(Short[] values) {
* @param values the values whose at least one which is expected to be in the array under test.
* @return {@code this} assertion object.
* @throws NullPointerException if the array of values is {@code null}.
- * @throws IllegalArgumentException if the array of values is empty and the array under test is not empty.
+ * @throws AssertionError if the array of values is empty and the array under test is not empty.
* @throws AssertionError if the array under test is {@code null}.
* @throws AssertionError if the array under test does not contain any of the given {@code values}.
* @since 3.16.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractStringAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractStringAssert.java
index 4d89bf5c59..0dcad5786b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractStringAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractStringAssert.java
@@ -14,17 +14,28 @@
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
+import static org.assertj.core.error.ShouldBeNumeric.shouldBeNumeric;
+import static org.assertj.core.error.ShouldBeNumeric.NumericType.BYTE;
+import static org.assertj.core.error.ShouldBeNumeric.NumericType.DOUBLE;
+import static org.assertj.core.error.ShouldBeNumeric.NumericType.FLOAT;
+import static org.assertj.core.error.ShouldBeNumeric.NumericType.INTEGER;
+import static org.assertj.core.error.ShouldBeNumeric.NumericType.LONG;
+import static org.assertj.core.error.ShouldBeNumeric.NumericType.SHORT;
import java.util.Base64;
import java.util.Comparator;
import org.assertj.core.internal.Comparables;
import org.assertj.core.internal.ComparatorBasedComparisonStrategy;
+import org.assertj.core.internal.Failures;
import org.assertj.core.util.CheckReturnValue;
import org.assertj.core.util.VisibleForTesting;
public class AbstractStringAssert> extends AbstractCharSequenceAssert {
+ @VisibleForTesting
+ Failures failures = Failures.instance();
+
protected AbstractStringAssert(String actual, Class> selfType) {
super(actual, selfType);
}
@@ -42,7 +53,7 @@ protected AbstractStringAssert(String actual, Class> selfType) {
* assertThat("abc").isLessThan("bcd")
* .isLessThan("b")
* .isLessThan("abca")
- * .usingComparator(CASE_INSENSITIVE)
+ * .usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isLessThan("BCD");
*
* // assertions fail
@@ -73,7 +84,7 @@ public SELF isLessThan(String other) {
* .isLessThanOrEqualTo("abc")
* .isLessThanOrEqualTo("b")
* .isLessThanOrEqualTo("abca")
- * .usingComparator(CASE_INSENSITIVE)
+ * .usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isLessThanOrEqualTo("ABC");
*
* // assertions fail
@@ -102,7 +113,7 @@ public SELF isLessThanOrEqualTo(String other) {
* assertThat("xyz").isGreaterThan("abc")
* .isGreaterThan("xy")
* .isGreaterThan("ABC");
- * assertThat("XYZ").usingComparator(CASE_INSENSITIVE)
+ * assertThat("XYZ").usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isGreaterThan("abc");
*
* // assertions fail
@@ -133,7 +144,7 @@ public SELF isGreaterThan(String other) {
* .isGreaterThanOrEqualTo("xyz")
* .isGreaterThanOrEqualTo("xy")
* .isGreaterThanOrEqualTo("ABC");
- * assertThat("XYZ").usingComparator(CASE_INSENSITIVE)
+ * assertThat("XYZ").usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isGreaterThanOrEqualTo("abc");
*
* // assertions fail
@@ -164,7 +175,7 @@ public SELF isGreaterThanOrEqualTo(String other) {
* .isBetween("aa", "ab")
* .isBetween("ab", "ab")
* .isBetween("a", "c")
- * .usingComparator(CASE_INSENSITIVE)
+ * .usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isBetween("AA", "AC");
*
* // assertions fail
@@ -195,7 +206,7 @@ public SELF isBetween(String startInclusive, String endInclusive) {
* // assertions succeed
* assertThat("ab").isStrictlyBetween("aa", "ac")
* .isStrictlyBetween("a", "c")
- * .usingComparator(CASE_INSENSITIVE)
+ * .usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isStrictlyBetween("AA", "AC");
*
* // assertions fail
@@ -303,7 +314,7 @@ public AbstractByteArrayAssert> decodedAsBase64() {
*
* Examples :
*
// assertions succeed
- * assertThat("abc").usingComparator(CASE_INSENSITIVE)
+ * assertThat("abc").usingComparator(String.CASE_INSENSITIVE_ORDER)
* .isEqualTo("Abc")
* .isEqualTo("ABC");
*
@@ -328,7 +339,7 @@ public SELF usingComparator(Comparator super String> customComparator) {
*
* Examples :
*
// assertions succeed
- * assertThat("abc").usingComparator(CASE_INSENSITIVE, "String case insensitive comparator")
+ * assertThat("abc").usingComparator(String.CASE_INSENSITIVE_ORDER, "String case insensitive comparator")
* .isEqualTo("Abc")
* .isEqualTo("ABC");
*
@@ -402,4 +413,174 @@ public SELF isEqualTo(String expectedStringTemplate, Object... args) {
public SELF isEqualTo(String expected) {
return super.isEqualTo(expected);
}
+
+ /**
+ * Parses the actual value as boolean, the parsed boolean becoming the new value under test.
+ *
+ * Note that only when the string is equal to the string "true", ignoring case, and can have leading and trailing space, the parsed value will be {@code true}.
+ * Otherwise, the value will be {@code false}.
+ *
+ * Examples:
+ *
assertThat("truE").asBoolean().isTrue();
+ * assertThat("false").asBoolean().isFalse();
+ * assertThat("foo bar").asBoolean().isFalse();
+ * assertThat((String) null).asBoolean().isFalse();
+ *
+ * @return a new {@link BooleanAssert} instance whose value under test is the result of the parse.
+ *
+ * @since 3.25.0
+ */
+ public AbstractBooleanAssert> asBoolean() {
+ return InstanceOfAssertFactories.BOOLEAN.createAssert(Boolean.parseBoolean(actual)).withAssertionState(myself);
+ }
+
+ /**
+ * Parses the actual value as byte, using radix 10, the parsed byte becoming the new value under test.
+ *
+ * Examples:
+ *
+ * // assertion succeeds:
+ * assertThat("127").asByte().isEqualTo((byte) 127);
+ *
+ * // assertions fail as the actual value is null or not a valid byte
+ * assertThat((String) null).asByte();
+ * assertThat("1L").asByte();
+ *
+ * @return a new {@link ByteAssert} instance whose value under test is the result of the parse.
+ * @throws AssertionError if the actual value is null or not a valid byte.
+ *
+ * @since 3.25.0
+ */
+ public AbstractByteAssert> asByte() {
+ try {
+ return InstanceOfAssertFactories.BYTE.createAssert(Byte.parseByte(actual)).withAssertionState(myself);
+ } catch (NumberFormatException e) {
+ throw failures.failure(info, shouldBeNumeric(actual, BYTE));
+ }
+ }
+
+ /**
+ * Parses the actual value as short, using radix 10, the parsed short becoming the new value under test.
+ *
+ * Examples:
+ *
+ * // assertion succeeds:
+ * assertThat("32767").asShort().isEqualTo((short) 32767);
+ *
+ * // assertions fail as the actual value is null or not a valid short:
+ * assertThat((String) null).asShort();
+ * assertThat("-32769").asShort();
+ *
+ * @return a new {@link ShortAssert} instance whose value under test is the result of the parse.
+ * @throws AssertionError if the actual value is null or not a valid short.
+ *
+ * @since 3.25.0
+ */
+ public AbstractShortAssert> asShort() {
+ try {
+ return InstanceOfAssertFactories.SHORT.createAssert(Short.parseShort(actual)).withAssertionState(myself);
+ } catch (NumberFormatException e) {
+ throw failures.failure(info, shouldBeNumeric(actual, SHORT));
+ }
+ }
+
+ /**
+ * Parses the actual value as integer, using radix 10, the parsed integer becoming the new value under test.
+ *
+ * Examples:
+ *
+ * // assertion succeeds:
+ * assertThat("2147483647").asInt().isEqualTo(2147483647);
+ *
+ * // assertions fail as the actual value is null or not a valid int:
+ * assertThat((String) null).asInt();
+ * assertThat("1e100").asInt();
+ *
+ * @return a new {@link IntegerAssert} instance whose value under test is the result of the parse.
+ * @throws AssertionError if the actual value is null or not a valid int.
+ *
+ * @since 3.25.0
+ */
+ public AbstractIntegerAssert> asInt() {
+ try {
+ return InstanceOfAssertFactories.INTEGER.createAssert(Integer.parseInt(actual)).withAssertionState(myself);
+ } catch (NumberFormatException e) {
+ throw failures.failure(info, shouldBeNumeric(actual, INTEGER));
+ }
+ }
+
+ /**
+ * Parses the actual value as long, using radix 10, the parsed long becoming the new value under test.
+ *
+ * Examples:
+ *
+ * // assertion succeeds:
+ * assertThat("1").asLong().isEqualTo(1L);
+ *
+ * // assertions fail as the actual value is null or not a long:
+ * assertThat((String) null).asLong();
+ * assertThat("1e100").asLong();
+ *
+ * @return a new {@link LongAssert} instance whose value under test is the result of the parse.
+ * @throws AssertionError if the actual value is null or not a valid long.
+ *
+ * @since 3.25.0
+ */
+ public AbstractLongAssert> asLong() {
+ try {
+ return InstanceOfAssertFactories.LONG.createAssert(Long.parseLong(actual)).withAssertionState(myself);
+ } catch (NumberFormatException e) {
+ throw failures.failure(info, shouldBeNumeric(actual, LONG));
+ }
+ }
+
+ /**
+ * Parses the actual value as float, the parsed float becoming the new value under test.
+ *
+ * Examples:
+ *
+ * // assertion succeeds:
+ * assertThat("1.2f").asFloat().isCloseTo(1.2f, withinPercentage(0.01));
+ *
+ * // assertions fail as the actual value is null or not a float:
+ * assertThat((String) null).asFloat();
+ * assertThat("foo").asFloat();
+ *
+ * @return a new {@link FloatAssert} instance whose value under test is the result of the parse.
+ * @throws AssertionError if the actual value is null or not a parseable float.
+ *
+ * @since 3.25.0
+ */
+ public AbstractFloatAssert> asFloat() {
+ try {
+ return InstanceOfAssertFactories.FLOAT.createAssert(Float.parseFloat(actual)).withAssertionState(myself);
+ } catch (NumberFormatException | NullPointerException e) {
+ throw failures.failure(info, shouldBeNumeric(actual, FLOAT));
+ }
+ }
+
+ /**
+ * Parses the actual value as double, the parsed double becoming the new value under test.
+ *
+ * Examples:
+ *
+ * // assertion succeeds:
+ * assertThat("1.2e308").asDouble().isCloseTo(1.2e308, withinPercentage(0.001));
+ *
+ * // assertions fail as the actual value is null or not a double:
+ * assertThat((String) null).asDouble();
+ * assertThat("foo").asDouble();
+ *
+ * @return a new {@link DoubleAssert} instance whose value under test is the result of the parse.
+ * @throws AssertionError if the actual value is null or not a parseable double.
+ *
+ * @since 3.25.0
+ */
+ public AbstractDoubleAssert> asDouble() {
+ try {
+ return InstanceOfAssertFactories.DOUBLE.createAssert(Double.parseDouble(actual)).withAssertionState(myself);
+ } catch (NumberFormatException | NullPointerException e) {
+ throw failures.failure(info, shouldBeNumeric(actual, DOUBLE));
+ }
+ }
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractTemporalAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractTemporalAssert.java
index 634e204244..7945024f5b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractTemporalAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractTemporalAssert.java
@@ -61,19 +61,20 @@ protected TEMPORAL getActual() {
* LocalTime _07_10 = LocalTime.of(7, 10);
* LocalTime _07_42 = LocalTime.of(7, 42);
*
- * // assertions will pass
+ * // assertions succeed:
* assertThat(_07_10).isCloseTo(_07_42, within(1, ChronoUnit.HOURS));
* assertThat(_07_10).isCloseTo(_07_42, within(32, ChronoUnit.MINUTES));
*
- * // assertions will fail
+ * // assertions fail:
* assertThat(_07_10).isCloseTo(_07_42, byLessThan(32, ChronoUnit.MINUTES));
* assertThat(_07_10).isCloseTo(_07_42, within(10, ChronoUnit.SECONDS));
+ *
* @param other the temporal to compare actual to
* @param offset the offset used for comparison
* @return this assertion object
* @throws NullPointerException if {@code Temporal} or {@code TemporalOffset} parameter is {@code null}.
* @throws AssertionError if the actual {@code Temporal} is {@code null}.
- * @throws AssertionError if the actual {@code Temporal} is not close to the given for a provided offset.
+ * @throws AssertionError if the actual {@code Temporal} is not close to the given one for a provided offset.
*/
public SELF isCloseTo(TEMPORAL other, TemporalOffset super TEMPORAL> offset) {
Objects.instance().assertNotNull(info, actual);
@@ -101,7 +102,7 @@ public SELF isCloseTo(TEMPORAL other, TemporalOffset super TEMPORAL> offset) {
* @throws AssertionError if the actual {@code Temporal} is {@code null}.
* @throws NullPointerException if temporal string representation or {@code TemporalOffset} parameter is {@code null}.
* @throws AssertionError if the actual {@code Temporal} is {@code null}.
- * @throws AssertionError if the actual {@code Temporal} is not close to the given for a provided offset.
+ * @throws AssertionError if the actual {@code Temporal} is not close to the given within the provided offset.
*/
public SELF isCloseTo(String otherAsString, TemporalOffset super TEMPORAL> offset) {
requireNonNull(otherAsString, "The String representing of the temporal object to compare actual with should not be null");
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractUriAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractUriAssert.java
index 4fc0b27eb0..d0226d9e46 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractUriAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractUriAssert.java
@@ -64,7 +64,7 @@ public SELF hasPath(String expected) {
* assertThat(new URI("http://helloworld.org/france")).hasNoPath();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a path.
+ * @throws AssertionError if {@code actual} has a path.
*/
public SELF hasNoPath() {
uris.assertHasPath(info, actual, null);
@@ -103,7 +103,7 @@ public SELF hasPort(int expected) {
* assertThat(new URI("http://helloworld.org:8080")).hasNoPort();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a port.
+ * @throws AssertionError if {@code actual} has a port.
*/
public SELF hasNoPort() {
uris.assertHasPort(info, actual, -1);
@@ -143,7 +143,7 @@ public SELF hasHost(String expected) {
* assertThat(new URI("http://helloworld.org:8080/index.html")).hasNoHost();
*
* @return {@code this} assertion object.
- * @throws AssertionError if actual has a host.
+ * @throws AssertionError if {@code actual} has a host.
* @since 3.22.0
*/
public SELF hasNoHost() {
@@ -203,7 +203,7 @@ public SELF hasFragment(String expected) {
* assertThat(new URI("http://helloworld.org:8080/index.html#print")).hasNoFragment();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a fragment.
+ * @throws AssertionError if {@code actual} has a fragment.
*/
public SELF hasNoFragment() {
uris.assertHasFragment(info, actual, null);
@@ -241,7 +241,7 @@ public SELF hasQuery(String expected) {
* assertThat(new URI("http://www.helloworld.org/index.html?type=test")).hasNoQuery();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a query.
+ * @throws AssertionError if {@code actual} has a query.
*/
public SELF hasNoQuery() {
uris.assertHasQuery(info, actual, null);
@@ -300,7 +300,7 @@ public SELF hasUserInfo(String expected) {
* assertThat(new URI("http://test:pass@www.helloworld.org/index.html")).hasNoUserInfo();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has some userinfo.
+ * @throws AssertionError if {@code actual} has some userinfo.
*/
public SELF hasNoUserInfo() {
uris.assertHasUserInfo(info, actual, null);
@@ -373,7 +373,7 @@ public SELF hasParameter(String name, String value) {
* assertThat(new URI("http://www.helloworld.org/index.html?sad=much")).hasNoParameters();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a parameter.
+ * @throws AssertionError if {@code actual} has a parameter.
* @throws IllegalArgumentException if the query string contains an invalid escape sequence.
*
* @since 2.5.0 / 3.5.0
@@ -398,7 +398,7 @@ public SELF hasNoParameters() {
*
* @param name the name of the parameter expected to be absent.
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has the expected parameter.
+ * @throws AssertionError if {@code actual} has the expected parameter.
* @throws IllegalArgumentException if the query string contains an invalid escape sequence.
*/
public SELF hasNoParameter(String name) {
@@ -425,7 +425,7 @@ public SELF hasNoParameter(String name) {
* @param name the name of the parameter expected to be absent.
* @param value the value of the parameter expected to be absent.
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has the expected parameter.
+ * @throws AssertionError if {@code actual} has the expected parameter.
* @throws IllegalArgumentException if the query string contains an invalid escape sequence.
*/
public SELF hasNoParameter(String name, String value) {
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractUrlAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractUrlAssert.java
index da67838917..666a07086c 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractUrlAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractUrlAssert.java
@@ -88,7 +88,7 @@ public SELF hasPath(String expected) {
* assertThat(new URL("http://helloworld.org/france")).hasNoPath();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a path.
+ * @throws AssertionError if {@code actual} has a path.
*/
public SELF hasNoPath() {
urls.assertHasPath(info, actual, "");
@@ -126,7 +126,7 @@ public SELF hasPort(int expected) {
* assertThat(new URL("http://helloworld.org:8080")).hasNoPort();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a port.
+ * @throws AssertionError if {@code actual} has a port.
*/
public SELF hasNoPort() {
urls.assertHasPort(info, actual, -1);
@@ -226,7 +226,7 @@ public SELF hasQuery(String expected) {
* assertThat(new URL("http://www.helloworld.org/index.html?type=test")).hasNoQuery();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a query.
+ * @throws AssertionError if {@code actual} has a query.
*/
public SELF hasNoQuery() {
urls.assertHasQuery(info, actual, null);
@@ -264,7 +264,7 @@ public SELF hasAnchor(String expected) {
* assertThat(new URL("http://www.helloworld.org/news.html#sport")).hasNoAnchor();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a anchor.
+ * @throws AssertionError if {@code actual} has a anchor.
*/
public SELF hasNoAnchor() {
urls.assertHasAnchor(info, actual, null);
@@ -304,7 +304,7 @@ public SELF hasUserInfo(String expected) {
* assertThat(new URL("http://test:pass@www.helloworld.org/index.html")).hasNoUserInfo();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has some userinfo.
+ * @throws AssertionError if {@code actual} has some userinfo.
*/
public SELF hasNoUserInfo() {
urls.assertHasUserInfo(info, actual, null);
@@ -373,7 +373,7 @@ public SELF hasParameter(String name, String value) {
* assertThat(new URL("http://www.helloworld.org/index.html?sad=much")).hasNoParameters();
*
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has a parameter.
+ * @throws AssertionError if {@code actual} has a parameter.
* @throws IllegalArgumentException if the query string contains an invalid escape sequence.
*
* @since 2.5.0 / 3.5.0
@@ -398,7 +398,7 @@ public SELF hasNoParameters() {
*
* @param name the name of the parameter expected to be absent.
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has the expected parameter.
+ * @throws AssertionError if {@code actual} has the expected parameter.
* @throws IllegalArgumentException if the query string contains an invalid escape sequence.
*
* @since 2.5.0 / 3.5.0
@@ -427,7 +427,7 @@ public SELF hasNoParameter(String name) {
* @param name the name of the parameter expected to be absent.
* @param value the value of the parameter expected to be absent.
* @return {@code this} assertion object.
- * @throws AssertionError if the actual has the expected parameter.
+ * @throws AssertionError if {@code actual} has the expected parameter.
* @throws IllegalArgumentException if the query string contains an invalid escape sequence.
*
* @since 2.5.0 / 3.5.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractZonedDateTimeAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractZonedDateTimeAssert.java
index 5e92ee7dad..c7a4ddd5bb 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AbstractZonedDateTimeAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractZonedDateTimeAssert.java
@@ -16,14 +16,18 @@
import static org.assertj.core.error.ShouldBeEqualIgnoringMinutes.shouldBeEqualIgnoringMinutes;
import static org.assertj.core.error.ShouldBeEqualIgnoringNanos.shouldBeEqualIgnoringNanos;
import static org.assertj.core.error.ShouldBeEqualIgnoringSeconds.shouldBeEqualIgnoringSeconds;
+import static org.assertj.core.error.ShouldBeInTheFuture.shouldBeInTheFuture;
+import static org.assertj.core.error.ShouldBeInThePast.shouldBeInThePast;
import static org.assertj.core.util.Preconditions.checkArgument;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalUnit;
import java.util.Comparator;
+import org.assertj.core.data.TemporalOffset;
import org.assertj.core.internal.ChronoZonedDateTimeByInstantComparator;
import org.assertj.core.internal.Comparables;
import org.assertj.core.internal.ComparatorBasedComparisonStrategy;
@@ -346,6 +350,8 @@ public SELF isAfter(String dateTimeAsString) {
* @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code ZonedDateTime} is {@code null}.
* @throws AssertionError if the actual {@code ZonedDateTime} is are not equal with nanoseconds ignored.
+ * @deprecated Use {@link #isCloseTo(ZonedDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
public SELF isEqualToIgnoringNanos(ZonedDateTime other) {
Objects.instance().assertNotNull(info, actual);
@@ -384,9 +390,11 @@ public SELF isEqualToIgnoringNanos(ZonedDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code ZonedDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code ZonedDateTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is are not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(ZonedDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
+ @Deprecated
public SELF isEqualToIgnoringSeconds(ZonedDateTime other) {
Objects.instance().assertNotNull(info, actual);
assertDateTimeParameterIsNotNull(other);
@@ -424,8 +432,9 @@ public SELF isEqualToIgnoringSeconds(ZonedDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code ZonedDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code ZonedDateTime} is are not equal ignoring minute, second and nanosecond
- * fields.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is are not equal ignoring minute, second and nanosecond fields.
+ * @deprecated Use {@link #isCloseTo(ZonedDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
public SELF isEqualToIgnoringMinutes(ZonedDateTime other) {
Objects.instance().assertNotNull(info, actual);
@@ -464,8 +473,9 @@ public SELF isEqualToIgnoringMinutes(ZonedDateTime other) {
* @return this assertion object.
* @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
* @throws IllegalArgumentException if other {@code ZonedDateTime} is {@code null}.
- * @throws AssertionError if the actual {@code ZonedDateTime} is are not equal with second and nanosecond fields
- * ignored.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is are not equal with second and nanosecond fields ignored.
+ * @deprecated Use {@link #isCloseTo(ZonedDateTime, TemporalOffset)} instead, although not exactly the same semantics,
+ * this is the right way to compare with a given precision.
*/
public SELF isEqualToIgnoringHours(ZonedDateTime other) {
Objects.instance().assertNotNull(info, actual);
@@ -699,6 +709,46 @@ public SELF isNotIn(String... dateTimesAsString) {
return isNotIn(convertToDateTimeArray(dateTimesAsString));
}
+ /**
+ * Verifies that the actual {@code ZonedDateTime} is strictly in the past.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(ZonedDateTime.now().minusMinutes(1)).isInThePast();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is not in the past.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInThePast() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isBefore(ZonedDateTime.now(actual.getZone())))
+ throw Failures.instance().failure(info, shouldBeInThePast(actual));
+ return myself;
+ }
+
+ /**
+ * Verifies that the actual {@code ZonedDateTime} is strictly in the future.
+ *
+ * Example:
+ *
// assertion succeeds:
+ * assertThat(ZonedDateTime.now().plusMinutes(1)).isInTheFuture();
+ *
+ * @return this assertion object.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is not in the future.
+ *
+ * @since 3.25.0
+ */
+ public SELF isInTheFuture() {
+ Objects.instance().assertNotNull(info, actual);
+ if (!actual.isAfter(ZonedDateTime.now(actual.getZone())))
+ throw Failures.instance().failure(info, shouldBeInTheFuture(actual));
+ return myself;
+ }
+
/**
* Verifies that the actual {@link ZonedDateTime} is in the [start, end] period (start and end included) according to
* the comparator in use.
@@ -864,6 +914,32 @@ public SELF isStrictlyBetween(String startExclusive, String endExclusive) {
return isStrictlyBetween(parse(startExclusive), parse(endExclusive));
}
+ /**
+ * Verifies that the actual {@link ZonedDateTime} is close to the other according to the given {@link TemporalOffset}.
+ *
+ * You can build the offset parameter using {@link Assertions#within(long, TemporalUnit)} or {@link Assertions#byLessThan(long, TemporalUnit)}.
+ *
+ * Example:
+ *
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2000-01-01T00:00:00Z");
+ *
+ * // assertions succeeds:
+ * assertThat(zonedDateTime).isCloseTo(zonedDateTime.plusHours(1), within(90, ChronoUnit.MINUTES));
+ *
+ * // assertions fails:
+ * assertThat(zonedDateTime).isCloseTo(zonedDateTime.plusHours(1), within(30, ChronoUnit.MINUTES));
+ * @param other the ZonedDateTime to compare actual to
+ * @param offset the offset used for comparison
+ * @return this assertion object
+ * @throws NullPointerException if {@code ZonedDateTime} or {@code TemporalOffset} parameter is {@code null}.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is {@code null}.
+ * @throws AssertionError if the actual {@code ZonedDateTime} is not close to the given one for a provided offset.
+ */
+ @Override
+ public SELF isCloseTo(ZonedDateTime other, TemporalOffset super ZonedDateTime> offset) {
+ // overridden for javadoc
+ return super.isCloseTo(other, offset);
+ }
+
/** {@inheritDoc} */
@Override
@CheckReturnValue
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Assert.java b/assertj-core/src/main/java/org/assertj/core/api/Assert.java
index 5a39b73b30..aa4e56feaf 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Assert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Assert.java
@@ -105,7 +105,7 @@ public interface Assert, ACTUAL> extends Descr
SELF isNotNull();
/**
- * Verifies that the actual value is the same as the given one, ie using == comparison.
+ * Verifies that the actual value is the same as the given one, i.e., using == comparison.
*
* Example:
*
// Name is a class with first and last fields, two Names are equals if both first and last are equals.
@@ -127,7 +127,7 @@ public interface Assert, ACTUAL> extends Descr
SELF isSameAs(Object expected);
/**
- * Verifies that the actual value is not the same as the given one, ie using == comparison.
+ * Verifies that the actual value is not the same as the given one, i.e., using == comparison.
*
* Example:
*
// Name is a class with first and last fields, two Names are equals if both first and last are equals.
@@ -448,7 +448,7 @@ public interface Assert, ACTUAL> extends Descr
*
* @param other the object to check type against.
* @return this assertion object.
- * @throws AssertionError if the actual has not the same type has the given object.
+ * @throws AssertionError if {@code actual} has not the same type as the given object.
* @throws NullPointerException if the actual value is null.
* @throws NullPointerException if the given object is null.
*/
@@ -472,6 +472,25 @@ public interface Assert, ACTUAL> extends Descr
*/
SELF hasToString(String expectedToString);
+ /**
+ * Verifies that actual {@code actual.toString()} is equal to the given {@code String} when
+ * {@link String#format formatted} with the given arguments.
+ *
+ * Example:
+ *
Foo foo = new Foo();
+ * FooWrapper wrapper = new FooWrapper(foo);
+ *
+ * assertThat(wrapper).hasToString("FooWrapper[foo=%s]", foo);
+ *
+ * @param expectedStringTemplate the format string to use.
+ * @param args the arguments to interpolate into the format string.
+ * @return this assertion object.
+ * @throws AssertionError if {@code actual.toString()} result is not equal to the given {@code String}.
+ * @throws AssertionError if actual is {@code null}.
+ * @since 3.25.0
+ */
+ SELF hasToString(String expectedStringTemplate, Object... args);
+
/**
* Verifies that actual {@code actual.toString()} is not equal to the given {@code String}.
*
@@ -490,6 +509,25 @@ public interface Assert, ACTUAL> extends Descr
*/
SELF doesNotHaveToString(String otherToString);
+ /**
+ * Verifies that actual {@code actual.toString()} is not equal to the given {@code String}.
+ *
+ * Example:
+ *
Foo foo = new Foo();
+ * Bar bar = new Bar();
+ * FooBarWrapper wrapper = new FooBarWrapper(bar);
+ *
+ * assertThat(wrapper).doesNotHaveToString("FooBarWrapper[%s]", foo);
+ *
+ * @param expectedStringTemplate the format string to use.
+ * @param args the arguments to interpolate into the format string.
+ * @return this assertion object.
+ * @throws AssertionError if {@code actual.toString()} result is equal to the given {@code String}.
+ * @throws AssertionError if actual is {@code null}.
+ * @since 3.25.0
+ */
+ SELF doesNotHaveToString(String expectedStringTemplate, Object... args);
+
/**
* Verifies that the actual value does not have the same class as the given object.
*
@@ -505,7 +543,7 @@ public interface Assert, ACTUAL> extends Descr
*
* @param other the object to check type against.
* @return this assertion object.
- * @throws AssertionError if the actual has the same type has the given object.
+ * @throws AssertionError if {@code actual} has the same type as the given object.
* @throws NullPointerException if the actual value is null.
* @throws NullPointerException if the given object is null.
*/
@@ -612,7 +650,9 @@ public interface Assert, ACTUAL> extends Descr
* assertThat(unsortedListAsObject).asList().isSorted();
*
* @return a list assertion object
+ * @deprecated use {@link #asInstanceOf(InstanceOfAssertFactory) asInstanceOf(InstanceOfAssertFactories.LIST)} instead
*/
+ @Deprecated
AbstractListAssert, List>, Object, ObjectAssert> asList();
/**
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Assertions.java b/assertj-core/src/main/java/org/assertj/core/api/Assertions.java
index 6c87075e0f..4b530b6822 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Assertions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Assertions.java
@@ -12,6 +12,7 @@
*/
package org.assertj.core.api;
+import static java.lang.String.format;
import static org.assertj.core.configuration.ConfigurationProvider.CONFIGURATION_PROVIDER;
import static org.assertj.core.data.Percentage.withPercentage;
@@ -75,6 +76,7 @@
import java.util.stream.Stream;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
+import org.assertj.core.api.ThrowableAssert.ThrowingCallableWithValue;
import org.assertj.core.api.filter.FilterOperator;
import org.assertj.core.api.filter.Filters;
import org.assertj.core.api.filter.InFilter;
@@ -96,6 +98,7 @@
import org.assertj.core.description.Description;
import org.assertj.core.groups.Properties;
import org.assertj.core.groups.Tuple;
+import org.assertj.core.internal.Failures;
import org.assertj.core.presentation.BinaryRepresentation;
import org.assertj.core.presentation.HexadecimalRepresentation;
import org.assertj.core.presentation.Representation;
@@ -170,7 +173,6 @@ public static PredicateAssert assertThat(Predicate actual) {
return AssertionsForInterfaceTypes.assertThat(actual);
}
-
/**
* Create assertion for {@link Predicate}.
*
@@ -1210,6 +1212,26 @@ public static AbstractThrowableAssert, T> assertThat(T a
return assertThat(catchThrowable(shouldRaiseThrowable)).hasBeenThrown();
}
+ /**
+ * Similar to {@link #assertThatThrownBy(ThrowingCallable)}, but when the called code returns a value instead of
+ * throwing, the assertion error shows the returned value to help understand what went wrong.
+ *
+ * @param shouldRaiseThrowable The {@link ThrowingCallableWithValue} or lambda with the code that should raise the throwable.
+ * @return the created {@link ThrowableAssert}.
+ * @since 3.25.0
+ */
+ @CanIgnoreReturnValue
+ public static AbstractThrowableAssert, ? extends Throwable> assertThatThrownBy(
+ ThrowingCallableWithValue shouldRaiseThrowable) {
+ Object value;
+ try {
+ value = shouldRaiseThrowable.call();
+ } catch (Throwable throwable) {
+ return assertThat(throwable);
+ }
+ throw Failures.instance().failure(format("Expecting code to raise a throwable, but it returned [%s] instead", value));
+ }
+
/**
* Allows to capture and then assert on a {@link Throwable} like {@code assertThatThrownBy(ThrowingCallable)} but this method
* let you set the assertion description the same way you do with {@link AbstractAssert#as(String, Object...) as(String, Object...)}.
@@ -1248,6 +1270,26 @@ public static AbstractThrowableAssert, T> assertThat(T a
return assertThat(catchThrowable(shouldRaiseThrowable)).as(description, args).hasBeenThrown();
}
+ /**
+ * Similar to {@link #assertThatThrownBy(ThrowingCallable, String, Object...)}, but when the called code returns a value
+ * instead of throwing, the assertion error shows the returned value to help understand what went wrong.
+ *
+ * @param shouldRaiseThrowable The {@link ThrowingCallableWithValue} or lambda with the code that should raise the throwable.
+ * @return the created {@link ThrowableAssert}.
+ * @since 3.25.0
+ */
+ @CanIgnoreReturnValue
+ public static AbstractThrowableAssert, ? extends Throwable> assertThatThrownBy(ThrowingCallableWithValue shouldRaiseThrowable,
+ String description, Object... args) {
+ Object value;
+ try {
+ value = shouldRaiseThrowable.call();
+ } catch (Throwable throwable) {
+ return assertThat(throwable).as(description, args);
+ }
+ throw Failures.instance().failure(format("Expecting code to raise a throwable, but it returned [%s] instead", value));
+ }
+
/**
* Allows to capture and then assert on a {@link Throwable} (easier done with lambdas).
*
@@ -1568,7 +1610,8 @@ public static IOException catchIOException(ThrowingCallable shouldRaiseIOExcepti
* @since 3.22.0
*/
public static ReflectiveOperationException catchReflectiveOperationException(ThrowingCallable shouldRaiseReflectiveOperationException) {
- return AssertionsForClassTypes.catchThrowableOfType(shouldRaiseReflectiveOperationException, ReflectiveOperationException.class);
+ return AssertionsForClassTypes.catchThrowableOfType(shouldRaiseReflectiveOperationException,
+ ReflectiveOperationException.class);
}
/**
@@ -1710,7 +1753,7 @@ public static ThrowableTypeAssert assertThatIllegalStateE
*
* @since 3.23.0
*/
- public static ThrowableTypeAssert assertThatException(){
+ public static ThrowableTypeAssert assertThatException() {
return assertThatExceptionOfType(Exception.class);
}
@@ -1721,7 +1764,7 @@ public static ThrowableTypeAssert assertThatException(){
*
* @since 3.23.0
*/
- public static ThrowableTypeAssert assertThatRuntimeException(){
+ public static ThrowableTypeAssert assertThatRuntimeException() {
return assertThatExceptionOfType(RuntimeException.class);
}
@@ -1732,7 +1775,7 @@ public static ThrowableTypeAssert assertThatRuntimeException()
*
* @since 3.23.0
*/
- public static ThrowableTypeAssert assertThatReflectiveOperationException(){
+ public static ThrowableTypeAssert assertThatReflectiveOperationException() {
return assertThatExceptionOfType(ReflectiveOperationException.class);
}
@@ -1743,7 +1786,7 @@ public static ThrowableTypeAssert assertThatReflec
*
* @since 3.23.0
*/
- public static ThrowableTypeAssert assertThatIndexOutOfBoundsException(){
+ public static ThrowableTypeAssert assertThatIndexOutOfBoundsException() {
return assertThatExceptionOfType(IndexOutOfBoundsException.class);
}
@@ -2287,6 +2330,7 @@ public static Offset within(Long value) {
* @param unit the {@link TemporalUnit} of the offset
* @return the created {@code Offset}.
* @since 3.7.0
+ * @see #byLessThan(long, TemporalUnit)
*/
public static TemporalUnitOffset within(long value, TemporalUnit unit) {
return new TemporalUnitWithinOffset(value, unit);
@@ -2494,6 +2538,7 @@ public static Offset byLessThan(Long value) {
* @param unit the {@link TemporalUnit} of the offset.
* @return the created {@code Offset}.
* @since 3.7.0
+ * @see #within(long, TemporalUnit)
*/
public static TemporalUnitOffset byLessThan(long value, TemporalUnit unit) {
return new TemporalUnitLessThanOffset(value, unit);
@@ -2575,6 +2620,21 @@ public static Condition allOf(Iterable extends Condition super T>> co
return AllOf.allOf(conditions);
}
+ /**
+ * Create a new {@link ThrowingConsumer}
that delegates the evaluation of the
+ * given consumers to {@link AbstractAssert#satisfies(ThrowingConsumer[])}.
+ *
+ * @param the type of object the given consumers accept
+ * @param consumers the consumers to evaluate
+ * @return the {@code ThrowingConsumer} instance
+ *
+ * @since 3.25.0
+ */
+ @SafeVarargs
+ public static ThrowingConsumer allOf(ThrowingConsumer super T>... consumers) {
+ return actual -> assertThat(actual).satisfies(consumers);
+ }
+
/**
* Only delegate to {@link AnyOf#anyOf(Condition...)} so that Assertions offers a full feature entry point to all
* AssertJ features (but you can use {@link AnyOf} if you prefer).
@@ -2605,6 +2665,21 @@ public static Condition anyOf(Iterable extends Condition super T>> co
return AnyOf.anyOf(conditions);
}
+ /**
+ * Create a new {@link ThrowingConsumer}
that delegates the evaluation of the
+ * given consumers to {@link AbstractAssert#satisfiesAnyOf(ThrowingConsumer[])}.
+ *
+ * @param the type of object the given consumers accept
+ * @param consumers the consumers to evaluate
+ * @return the {@code ThrowingConsumer} instance
+ *
+ * @since 3.25.0
+ */
+ @SafeVarargs
+ public static ThrowingConsumer anyOf(ThrowingConsumer super T>... consumers) {
+ return actual -> assertThat(actual).satisfiesAnyOf(consumers);
+ }
+
/**
* Creates a new {@link DoesNotHave}
.
*
@@ -3169,6 +3244,20 @@ public static T assertThat(final AssertProvider component) {
return AssertionsForInterfaceTypes.assertThat(actual);
}
+ /**
+ * Creates a new instance of {@link CharSequenceAssert}
.
+ *
+ * Use this over {@link #assertThat(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides assertThat
for.
+ *
+ * @param actual the actual value.
+ * @return the created assertion object.
+ * @since 3.25.0
+ */
+ public static AbstractCharSequenceAssert, ? extends CharSequence> assertThatCharSequence(CharSequence actual) {
+ return assertThat(actual);
+ }
+
/**
* Creates a new instance of {@link CharSequenceAssert}
from a {@link StringBuilder}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AssumptionExceptionFactory.java b/assertj-core/src/main/java/org/assertj/core/api/AssumptionExceptionFactory.java
index aa18d1b557..045fb6b753 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AssumptionExceptionFactory.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AssumptionExceptionFactory.java
@@ -31,7 +31,7 @@ static RuntimeException assumptionNotMet(AssertionError assertionError) throws R
Class> assumptionExceptionClass = preferredAssumptionException.getAssumptionExceptionClass();
return buildAssumptionException(assumptionExceptionClass, assertionError);
}
-
+
@VisibleForTesting
public static PreferredAssumptionException getPreferredAssumptionException() {
return preferredAssumptionException;
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Assumptions.java b/assertj-core/src/main/java/org/assertj/core/api/Assumptions.java
index 4af114f028..3ff4e784f8 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Assumptions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Assumptions.java
@@ -339,6 +339,20 @@ public static Char2DArrayAssert assumeThat(char[][] actual) {
return asAssumption(CharSequenceAssert.class, CharSequence.class, actual);
}
+ /**
+ * Creates a new instance of {@link CharSequenceAssert}
assumption.
+ *
+ * Use this over {@link #assumeThat(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides assumeThat
for.
+ *
+ * @param actual the actual value.
+ * @return the created assumption for assertion object.
+ * @since 3.25.0
+ */
+ public static AbstractCharSequenceAssert, ? extends CharSequence> assumeThatCharSequence(CharSequence actual) {
+ return assumeThat(actual);
+ }
+
/**
* Creates a new instance of {@link CharSequenceAssert}
assumption from a {@link StringBuilder}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicBooleanAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicBooleanAssert.java
index 9d2df0e9fb..015d0c33e4 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicBooleanAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicBooleanAssert.java
@@ -93,7 +93,8 @@ public AtomicBooleanAssert usingComparator(Comparator super AtomicBoolean> cus
*/
@Override
@Deprecated
- public AtomicBooleanAssert usingComparator(Comparator super AtomicBoolean> customComparator, String customComparatorDescription) {
+ public AtomicBooleanAssert usingComparator(Comparator super AtomicBoolean> customComparator,
+ String customComparatorDescription) {
throw new UnsupportedOperationException("custom Comparator is not supported for AtomicBoolean comparison");
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerAssert.java
index be65e56e14..902fd237c4 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerAssert.java
@@ -387,7 +387,8 @@ public AtomicIntegerAssert usingComparator(Comparator super AtomicInteger> cus
@Override
@CheckReturnValue
- public AtomicIntegerAssert usingComparator(Comparator super AtomicInteger> customComparator, String customComparatorDescription) {
+ public AtomicIntegerAssert usingComparator(Comparator super AtomicInteger> customComparator,
+ String customComparatorDescription) {
integers = new Integers(new ComparatorBasedComparisonStrategy(customComparator, customComparatorDescription));
return super.usingComparator(customComparator, customComparatorDescription);
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerFieldUpdaterAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerFieldUpdaterAssert.java
index a1d8c28c08..51b915f182 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerFieldUpdaterAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicIntegerFieldUpdaterAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
@@ -25,12 +24,13 @@
* @author epeee
* @since 2.7.0 / 3.7.0
*/
-public class AtomicIntegerFieldUpdaterAssert extends AbstractAtomicFieldUpdaterAssert, Integer, AtomicIntegerFieldUpdater, OBJECT> {
+public class AtomicIntegerFieldUpdaterAssert extends
+ AbstractAtomicFieldUpdaterAssert, Integer, AtomicIntegerFieldUpdater, OBJECT> {
public AtomicIntegerFieldUpdaterAssert(AtomicIntegerFieldUpdater actual) {
super(actual, AtomicIntegerFieldUpdaterAssert.class, false);
}
-
+
/**
* Verifies that the actual atomic field updater contains the given value at the given object.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicLongFieldUpdaterAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicLongFieldUpdaterAssert.java
index 0c889e8129..abac39e7b7 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicLongFieldUpdaterAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicLongFieldUpdaterAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
@@ -25,7 +24,8 @@
* @author epeee
* @since 2.7.0 / 3.7.0
*/
-public class AtomicLongFieldUpdaterAssert extends AbstractAtomicFieldUpdaterAssert, Long, AtomicLongFieldUpdater, OBJECT> {
+public class AtomicLongFieldUpdaterAssert
+ extends AbstractAtomicFieldUpdaterAssert, Long, AtomicLongFieldUpdater, OBJECT> {
public AtomicLongFieldUpdaterAssert(AtomicLongFieldUpdater actual) {
super(actual, AtomicLongFieldUpdaterAssert.class, false);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicMarkableReferenceAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicMarkableReferenceAssert.java
index 775a6dea32..e67aa6a015 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicMarkableReferenceAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicMarkableReferenceAssert.java
@@ -53,7 +53,7 @@ public AtomicMarkableReferenceAssert(AtomicMarkableReference actual) {
public AtomicMarkableReferenceAssert hasReference(VALUE expectedValue) {
return super.hasReference(expectedValue);
}
-
+
@Override
protected VALUE getReference() {
return actual.getReference();
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceAssert.java
index 2a4b6b3928..41fcaab96b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceAssert.java
@@ -163,4 +163,39 @@ public AtomicReferenceAssert hasValueSatisfying(Consumer super V> requireme
return myself;
}
+ /**
+ * Verifies that the atomic under test has the {@code null} value.
+ *
+ * Example:
+ *
// assertion succeeds
+ * assertThat(new AtomicReference(null)).hasNullValue();
+ *
+ * // assertion fails
+ * assertThat(new AtomicReference("foo")).hasNullValue();
+ *
+ * @return {@code this} assertion object.
+ * @throws AssertionError if the atomic under test does not have the null value.
+ * @since 3.25.0
+ */
+ public AtomicReferenceAssert hasNullValue() {
+ return hasValue(null);
+ }
+
+ /**
+ * Verifies that the atomic under test does not have the {@code null} value.
+ *
+ * Example:
+ *
// assertion succeeds
+ * assertThat(new AtomicReference("foo")).doesNotHaveNullValue();
+ *
+ * // assertion fails
+ * assertThat(new AtomicReference(null)).doesNotHaveNullValue();
+ *
+ * @return {@code this} assertion object.
+ * @throws AssertionError if the atomic under test has the null value.
+ * @since 3.25.0
+ */
+ public AtomicReferenceAssert doesNotHaveNullValue() {
+ return doesNotHaveValue(null);
+ }
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceFieldUpdaterAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceFieldUpdaterAssert.java
index 55f31ce258..9601816752 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceFieldUpdaterAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicReferenceFieldUpdaterAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
@@ -26,7 +25,8 @@
* @author epeee
* @since 2.7.0 / 3.7.0
*/
-public class AtomicReferenceFieldUpdaterAssert extends AbstractAtomicFieldUpdaterAssert, FIELD, AtomicReferenceFieldUpdater, OBJECT> {
+public class AtomicReferenceFieldUpdaterAssert extends
+ AbstractAtomicFieldUpdaterAssert, FIELD, AtomicReferenceFieldUpdater, OBJECT> {
public AtomicReferenceFieldUpdaterAssert(AtomicReferenceFieldUpdater actual) {
super(actual, AtomicReferenceFieldUpdaterAssert.class, true);
@@ -57,7 +57,7 @@ public AtomicReferenceFieldUpdaterAssert(AtomicReferenceFieldUpdater hasValue(FIELD expectedValue, OBJECT obj) {
return super.hasValue(expectedValue, obj);
}
-
+
@Override
protected FIELD getActualValue(OBJECT obj) {
return actual.get(obj);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/AtomicStampedReferenceAssert.java b/assertj-core/src/main/java/org/assertj/core/api/AtomicStampedReferenceAssert.java
index 941f44501d..d3aa213222 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/AtomicStampedReferenceAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/AtomicStampedReferenceAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
import static org.assertj.core.error.ShouldHaveStamp.shouldHaveStamp;
import java.util.concurrent.atomic.AtomicStampedReference;
@@ -26,7 +25,8 @@
* @author epeee
* @since 2.7.0 / 3.7.0
*/
-public class AtomicStampedReferenceAssert extends AbstractAtomicReferenceAssert, VALUE, AtomicStampedReference> {
+public class AtomicStampedReferenceAssert
+ extends AbstractAtomicReferenceAssert, VALUE, AtomicStampedReference> {
public AtomicStampedReferenceAssert(AtomicStampedReference actual) {
super(actual, AtomicStampedReferenceAssert.class);
@@ -52,7 +52,7 @@ public AtomicStampedReferenceAssert(AtomicStampedReference actual) {
public AtomicStampedReferenceAssert hasReference(VALUE expectedValue) {
return super.hasReference(expectedValue);
}
-
+
@Override
protected VALUE getReference() {
return actual.getReference();
@@ -72,7 +72,7 @@ protected VALUE getReference() {
* @return this assertion object.
* @since 2.7.0 / 3.7.0
*/
- public AtomicStampedReferenceAssert hasStamp(int expectedStamp){
+ public AtomicStampedReferenceAssert hasStamp(int expectedStamp) {
int timestamp = actual.getStamp();
if (timestamp != expectedStamp) throwAssertionError(shouldHaveStamp(actual, expectedStamp));
return this;
diff --git a/assertj-core/src/main/java/org/assertj/core/api/BDDAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/BDDAssertions.java
index 9ed7c1e422..7bcd717e0c 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/BDDAssertions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/BDDAssertions.java
@@ -12,6 +12,8 @@
*/
package org.assertj.core.api;
+import static java.lang.String.format;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -72,6 +74,7 @@
import java.util.stream.Stream;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
+import org.assertj.core.api.ThrowableAssert.ThrowingCallableWithValue;
import org.assertj.core.api.filter.FilterOperator;
import org.assertj.core.api.filter.InFilter;
import org.assertj.core.api.filter.NotFilter;
@@ -89,6 +92,7 @@
import org.assertj.core.description.Description;
import org.assertj.core.groups.Properties;
import org.assertj.core.groups.Tuple;
+import org.assertj.core.internal.Failures;
import org.assertj.core.presentation.BinaryRepresentation;
import org.assertj.core.presentation.HexadecimalRepresentation;
import org.assertj.core.presentation.Representation;
@@ -1079,6 +1083,20 @@ public static Short2DArrayAssert then(short[][] actual) {
return assertThat(actual);
}
+ /**
+ * Creates a new instance of {@link org.assertj.core.api.CharSequenceAssert}
.
+ *
+ * Use this over {@link #then(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides then
for.
+ *
+ * @param actual the actual value.
+ * @return the created assertion object.
+ * @since 3.25.0
+ */
+ public static AbstractCharSequenceAssert, ? extends CharSequence> thenCharSequence(CharSequence actual) {
+ return then(actual);
+ }
+
/**
* Creates a new instance of {@link org.assertj.core.api.CharSequenceAssert}
from a {@link StringBuilder}.
*
@@ -1316,6 +1334,25 @@ public static AbstractThrowableAssert, T> then(T actual)
return assertThat(catchThrowable(shouldRaiseThrowable)).hasBeenThrown();
}
+ /**
+ * Similar to {@link #thenThrownBy(ThrowingCallable)}, but when the called code returns a value instead of
+ * throwing, the assertion error shows the returned value to help understand what went wrong.
+ *
+ * @param shouldRaiseThrowable The {@link ThrowingCallableWithValue} or lambda with the code that should raise the throwable.
+ * @return the created {@link ThrowableAssert}.
+ * @since 3.25.0
+ */
+ @CanIgnoreReturnValue
+ public static AbstractThrowableAssert, ? extends Throwable> thenThrownBy(ThrowingCallableWithValue shouldRaiseThrowable) {
+ Object value;
+ try {
+ value = shouldRaiseThrowable.call();
+ } catch (Throwable throwable) {
+ return assertThat(throwable);
+ }
+ throw Failures.instance().failure(format("Expecting code to raise a throwable, but it returned [%s] instead", value));
+ }
+
/**
* Allows to capture and then assert on a {@link Throwable} like {@code thenThrownBy(ThrowingCallable)} but this method
* let you set the assertion description the same way you do with {@link AbstractAssert#as(String, Object...) as(String, Object...)}.
@@ -1353,6 +1390,26 @@ public static AbstractThrowableAssert, T> then(T actual)
return assertThat(catchThrowable(shouldRaiseThrowable)).as(description, args).hasBeenThrown();
}
+ /**
+ * Similar to {@link #thenThrownBy(ThrowingCallable, String, Object...)}, but when the called code returns a value instead of
+ * throwing, the assertion error shows the returned value to help understand what went wrong.
+ *
+ * @param shouldRaiseThrowable The {@link ThrowingCallableWithValue} or lambda with the code that should raise the throwable.
+ * @return the created {@link ThrowableAssert}.
+ * @since 3.25.0
+ */
+ @CanIgnoreReturnValue
+ public static AbstractThrowableAssert, ? extends Throwable> thenThrownBy(ThrowingCallableWithValue shouldRaiseThrowable,
+ String description, Object... args) {
+ Object value;
+ try {
+ value = shouldRaiseThrowable.call();
+ } catch (Throwable throwable) {
+ return assertThat(throwable).as(description, args);
+ }
+ throw Failures.instance().failure(format("Expecting code to raise a throwable, but it returned [%s] instead", value));
+ }
+
/**
* Allows to capture and then assert on a {@link Throwable} more easily when used with Java 8 lambdas.
*
@@ -2058,7 +2115,8 @@ public static IOException catchIOException(ThrowingCallable shouldRaiseIOExcepti
* @since 3.22.0
*/
public static ReflectiveOperationException catchReflectiveOperationException(ThrowingCallable shouldRaiseReflectiveOperationException) {
- return AssertionsForClassTypes.catchThrowableOfType(shouldRaiseReflectiveOperationException, ReflectiveOperationException.class);
+ return AssertionsForClassTypes.catchThrowableOfType(shouldRaiseReflectiveOperationException,
+ ReflectiveOperationException.class);
}
/**
@@ -3091,6 +3149,21 @@ public static Condition allOf(Iterable extends Condition super T>> co
return Assertions.allOf(conditions);
}
+ /**
+ * Create a new {@link ThrowingConsumer}
that delegates the evaluation of the
+ * given consumers to {@link AbstractAssert#satisfies(ThrowingConsumer[])}.
+ *
+ * @param the type of object the given consumers accept
+ * @param consumers the consumers to evaluate
+ * @return the {@code ThrowingConsumer} instance
+ *
+ * @since 3.25.0
+ */
+ @SafeVarargs
+ public static ThrowingConsumer allOf(ThrowingConsumer super T>... consumers) {
+ return Assertions.allOf(consumers);
+ }
+
/**
* Only delegate to {@link AnyOf#anyOf(Condition...)} so that Assertions offers a full feature entry point to all
* AssertJ features (but you can use {@link AnyOf} if you prefer).
@@ -3125,6 +3198,21 @@ public static Condition anyOf(Iterable extends Condition super T>> co
return Assertions.anyOf(conditions);
}
+ /**
+ * Create a new {@link ThrowingConsumer}
that delegates the evaluation of the
+ * given consumers to {@link AbstractAssert#satisfiesAnyOf(ThrowingConsumer[])}.
+ *
+ * @param the type of object the given consumers accept
+ * @param consumers the consumers to evaluate
+ * @return the {@code ThrowingConsumer} instance
+ *
+ * @since 3.25.0
+ */
+ @SafeVarargs
+ public static ThrowingConsumer anyOf(ThrowingConsumer super T>... consumers) {
+ return Assertions.anyOf(consumers);
+ }
+
/**
* Creates a new {@link DoesNotHave}
.
*
@@ -3338,53 +3426,53 @@ public static List linesOf(File file, String charsetName) {
return Assertions.linesOf(file, charsetName);
}
- /**
- * Loads the text content of a file at a given path into a list of strings with the default charset, each string corresponding to a
- * line.
- * The line endings are either \n, \r or \r\n.
- *
- * @param path the path.
- * @return the content of the file at the given path.
- * @throws NullPointerException if the given charset is {@code null}.
- * @throws UncheckedIOException if an I/O exception occurs.
- *
- * @since 3.23.0
- */
- public static List linesOf(Path path) {
- return Assertions.linesOf(path, Charset.defaultCharset());
- }
-
- /**
- * Loads the text content of a file at a given path into a list of strings, each string corresponding to a line.
- * The line endings are either \n, \r or \r\n.
- *
- * @param path the path.
- * @param charset the character set to use.
- * @return the content of the file at the given path.
- * @throws NullPointerException if the given charset is {@code null}.
- * @throws UncheckedIOException if an I/O exception occurs.
- *
- * @since 3.23.0
- */
- public static List linesOf(Path path, Charset charset) {
- return Assertions.linesOf(path, charset);
- }
-
- /**
- * Loads the text content of a file at a given path into a list of strings, each string corresponding to a line. The line endings are
- * either \n, \r or \r\n.
- *
- * @param path the path.
- * @param charsetName the name of the character set to use.
- * @return the content of the file at the given path.
- * @throws NullPointerException if the given charset is {@code null}.
- * @throws UncheckedIOException if an I/O exception occurs.
- *
- * @since 3.23.0
- */
- public static List linesOf(Path path, String charsetName) {
- return Assertions.linesOf(path, charsetName);
- }
+ /**
+ * Loads the text content of a file at a given path into a list of strings with the default charset, each string corresponding to a
+ * line.
+ * The line endings are either \n, \r or \r\n.
+ *
+ * @param path the path.
+ * @return the content of the file at the given path.
+ * @throws NullPointerException if the given charset is {@code null}.
+ * @throws UncheckedIOException if an I/O exception occurs.
+ *
+ * @since 3.23.0
+ */
+ public static List linesOf(Path path) {
+ return Assertions.linesOf(path, Charset.defaultCharset());
+ }
+
+ /**
+ * Loads the text content of a file at a given path into a list of strings, each string corresponding to a line.
+ * The line endings are either \n, \r or \r\n.
+ *
+ * @param path the path.
+ * @param charset the character set to use.
+ * @return the content of the file at the given path.
+ * @throws NullPointerException if the given charset is {@code null}.
+ * @throws UncheckedIOException if an I/O exception occurs.
+ *
+ * @since 3.23.0
+ */
+ public static List linesOf(Path path, Charset charset) {
+ return Assertions.linesOf(path, charset);
+ }
+
+ /**
+ * Loads the text content of a file at a given path into a list of strings, each string corresponding to a line. The line endings are
+ * either \n, \r or \r\n.
+ *
+ * @param path the path.
+ * @param charsetName the name of the character set to use.
+ * @return the content of the file at the given path.
+ * @throws NullPointerException if the given charset is {@code null}.
+ * @throws UncheckedIOException if an I/O exception occurs.
+ *
+ * @since 3.23.0
+ */
+ public static List linesOf(Path path, String charsetName) {
+ return Assertions.linesOf(path, charsetName);
+ }
// --------------------------------------------------------------------------------------------------
// URL/Resource methods : not assertions but here to have a single entry point to all AssertJ features.
diff --git a/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java b/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java
index 4eb46b8c99..1a234cf06c 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java
@@ -1118,6 +1118,20 @@ public static Char2DArrayAssert given(char[][] actual) {
return assumeThat(actual);
}
+ /**
+ * Creates a new assumption's instance for a {@link CharSequence} value.
+ *
+ * Use this over {@link #given(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides given
for.
+ *
+ * @param actual the actual {@link CharSequence} value to be validated.
+ * @return the {@link AbstractCharSequenceAssert} assertion object to be used for assumptions.
+ * @since 3.25.0
+ */
+ public static AbstractCharSequenceAssert, ? extends CharSequence> givenCharSequence(CharSequence actual) {
+ return given(actual);
+ }
+
/**
* Creates a new assumption's instance for a {@link String} value.
*
@@ -2391,7 +2405,7 @@ public static AtomicIntegerArrayAssert given(AtomicIntegerArray actual) {
* // ...
*}
*
- * @param the type of the object holding the updatable field which gets updated by the the actual value.
+ * @param the type of the object holding the updatable field which gets updated by the actual value.
* @param actual the actual {@link AtomicIntegerFieldUpdater} value to be validated.
* @return the {@link AtomicIntegerFieldUpdaterAssert} assertion object to be used for assumptions.
* @since 3.14.0
@@ -2518,7 +2532,7 @@ public static AtomicLongArrayAssert given(AtomicLongArray actual) {
* // ...
*}
*
- * @param the type of the object holding the updatable field which gets updated by the the actual value.
+ * @param the type of the object holding the updatable field which gets updated by the actual value.
* @param actual the actual {@link AtomicLongFieldUpdater} value to be validated.
* @return the {@link AtomicLongFieldUpdaterAssert} assertion object to be used for assumptions.
* @since 3.14.0
@@ -2618,8 +2632,8 @@ public static AtomicReferenceArrayAssert given(AtomicReferenc
* // ...
*}
*
- * @param the type of the field which gets updated by the the actual updater.
- * @param the type of the object holding the updatable field which gets updated by the the actual updater.
+ * @param the type of the field which gets updated by the actual updater.
+ * @param the type of the object holding the updatable field which gets updated by the actual updater.
* @param actual the actual {@link AtomicReferenceFieldUpdater} value to be validated.
* @return the {@link AtomicReferenceFieldUpdaterAssert} assertion object to be used for assumptions.
* @since 3.14.0
diff --git a/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertionsProvider.java b/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertionsProvider.java
index b428379904..2121f8021e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertionsProvider.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertionsProvider.java
@@ -128,7 +128,6 @@ default MatcherAssert then(Matcher actual) {
return proxy(MatcherAssert.class, Matcher.class, actual);
}
-
/**
* Creates a new instance of {@link LocalDateAssert}
.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/BigDecimalAssert.java b/assertj-core/src/main/java/org/assertj/core/api/BigDecimalAssert.java
index 27aa765247..1165cf2d33 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/BigDecimalAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/BigDecimalAssert.java
@@ -14,7 +14,6 @@
import java.math.BigDecimal;
-
/**
* Assertion methods for {@link BigDecimal}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/BooleanArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/BooleanArrayAssert.java
index 1bb74b1c43..18ad477609 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/BooleanArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/BooleanArrayAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for arrays of {@code boolean}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Byte2DArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/Byte2DArrayAssert.java
index 8f72fcee9d..88828d1727 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Byte2DArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Byte2DArrayAssert.java
@@ -235,7 +235,6 @@ public Byte2DArrayAssert hasNumberOfRows(int expected) {
return myself;
}
-
/**
* Verifies that the actual {@code byte[][]} has the same dimensions as the given array.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ByteArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/ByteArrayAssert.java
index 8da81eda13..72aa168c17 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ByteArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ByteArrayAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for arrays of {@code byte}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Char2DArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/Char2DArrayAssert.java
index 0b5d1441ca..6514a43be7 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Char2DArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Char2DArrayAssert.java
@@ -190,7 +190,7 @@ public Char2DArrayAssert isNotEmpty() {
}
/**
- * Verifies that the actual {@code char[][]} has the the given dimensions.
+ * Verifies that the actual {@code char[][]} has the given dimensions.
*
* Example:
*
// assertion will pass
diff --git a/assertj-core/src/main/java/org/assertj/core/api/CharArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/CharArrayAssert.java
index 02fca6f66b..0bce9e60f2 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/CharArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/CharArrayAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for arrays of {@code char}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ClassAssert.java b/assertj-core/src/main/java/org/assertj/core/api/ClassAssert.java
index aaf23bc05f..a81d022caa 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ClassAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ClassAssert.java
@@ -26,5 +26,5 @@ public class ClassAssert extends AbstractClassAssert {
public ClassAssert(Class> actual) {
super(actual, ClassAssert.class);
}
-
+
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ClassBasedNavigableIterableAssert.java b/assertj-core/src/main/java/org/assertj/core/api/ClassBasedNavigableIterableAssert.java
index d44d092c9d..7604b497c4 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ClassBasedNavigableIterableAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ClassBasedNavigableIterableAssert.java
@@ -37,7 +37,6 @@ ClassBasedNavigableIterableAssert, ACTUAL, ELEMENT, ELEMENT_ASSERT> assertThat
// @format:on
-
public ClassBasedNavigableIterableAssert(ACTUAL actual, Class> selfType, Class assertClass) {
super(actual, selfType);
this.assertClass = assertClass;
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ClassLoadingStrategyFactory.java b/assertj-core/src/main/java/org/assertj/core/api/ClassLoadingStrategyFactory.java
index 622fcbf637..1767cdc93e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ClassLoadingStrategyFactory.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ClassLoadingStrategyFactory.java
@@ -59,7 +59,8 @@ static ClassLoadingStrategyPair classLoadingStrategy(Class> assertClass) {
} else if (ClassInjector.UsingLookup.isAvailable()) {
try {
return new ClassLoadingStrategyPair(assertClassLoader,
- ClassLoadingStrategy.UsingLookup.of(PRIVATE_LOOKUP_IN.invoke(null, assertClass, LOOKUP)));
+ ClassLoadingStrategy.UsingLookup.of(PRIVATE_LOOKUP_IN.invoke(null, assertClass,
+ LOOKUP)));
} catch (Exception e) {
throw new IllegalStateException("Could not access package of " + assertClass, e);
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/DateAssert.java b/assertj-core/src/main/java/org/assertj/core/api/DateAssert.java
index 5c4f4c1e13..ac501becaf 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/DateAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/DateAssert.java
@@ -15,7 +15,6 @@
import java.text.DateFormat;
import java.util.Date;
-
/**
* Assertions for {@link Date}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Descriptable.java b/assertj-core/src/main/java/org/assertj/core/api/Descriptable.java
index 31a37f5602..fa34290585 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Descriptable.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Descriptable.java
@@ -90,7 +90,7 @@ default SELF as(String description, Object... args) {
* @throws IllegalStateException if the descriptionSupplier is {@code null} when evaluated.
*/
default SELF as(final Supplier descriptionSupplier) {
- return describedAs(new LazyTextDescription(descriptionSupplier));
+ return describedAs(descriptionSupplier);
}
/**
@@ -131,6 +131,40 @@ default SELF describedAs(String description, Object... args) {
return describedAs(new TextDescription(description, args));
}
+ /**
+ * Lazily specifies the description of the assertion that is going to be called, the given description is not evaluated
+ * if the assertion succeeds.
+ *
+ * The description must be set before calling the assertion otherwise it is ignored as the failing assertion breaks
+ * the chained call by throwing an AssertionError.
+ *
+ * Example :
+ *
// set an incorrect age to Mr Frodo which we all know is 33 years old.
+ * frodo.setAge(50);
+ *
+ * // the lazy test description is not evaluated as the assertion succeeds
+ * assertThat(frodo.getAge()).as(() -> "check Frodo's age").isEqualTo(50);
+ *
+ * try
+ * {
+ * // the lazy test description is evaluated as the assertion fails
+ * assertThat(frodo.getAge()).as(() -> "check Frodo's age").isEqualTo(33);
+ * }
+ * catch (AssertionError e)
+ * {
+ * assertThat(e).hasMessage("[check Frodo's age]\n
+ * expected: 33\n
+ * but was: 50");
+ * }
+ *
+ * @param descriptionSupplier the description {@link Supplier}.
+ * @return {@code this} object.
+ * @throws IllegalStateException if the descriptionSupplier is {@code null} when evaluated.
+ */
+ default SELF describedAs(final Supplier descriptionSupplier) {
+ return describedAs(new LazyTextDescription(descriptionSupplier));
+ }
+
/**
* Sets the description of the assertion that is going to be called after.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/DoubleArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/DoubleArrayAssert.java
index aa0a15bca5..ea9ec8fbcc 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/DoubleArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/DoubleArrayAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for arrays of {@code double}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/DoubleAssert.java b/assertj-core/src/main/java/org/assertj/core/api/DoubleAssert.java
index 307f130536..cbf5da12f1 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/DoubleAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/DoubleAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for {@link Double}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/FileAssert.java b/assertj-core/src/main/java/org/assertj/core/api/FileAssert.java
index 842d457a2e..aec95b17a5 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/FileAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/FileAssert.java
@@ -14,7 +14,6 @@
import java.io.File;
-
/**
* Assertion methods for {@link File}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/FloatArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/FloatArrayAssert.java
index 28713a9c7c..796ce2817b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/FloatArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/FloatArrayAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for arrays of {@code float}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/FloatAssert.java b/assertj-core/src/main/java/org/assertj/core/api/FloatAssert.java
index 07e147b36c..bd97c4e83e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/FloatAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/FloatAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for floats.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/InputStreamAssert.java b/assertj-core/src/main/java/org/assertj/core/api/InputStreamAssert.java
index 8153fa0003..522b75c98b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/InputStreamAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/InputStreamAssert.java
@@ -14,7 +14,6 @@
import java.io.InputStream;
-
/**
* Assertion methods for {@link InputStream}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/InstantAssert.java b/assertj-core/src/main/java/org/assertj/core/api/InstantAssert.java
index 49790ae3a3..4f982a8e2a 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/InstantAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/InstantAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
import java.time.Instant;
/**
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Int2DArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/Int2DArrayAssert.java
index e06abad46a..72112318b8 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Int2DArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Int2DArrayAssert.java
@@ -125,7 +125,6 @@ public Int2DArrayAssert hasDimensions(int expectedFirstDimension, int expectedSe
return myself;
}
-
/**
* Verifies that the actual two-dimensional array has the given number of rows.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/IntArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/IntArrayAssert.java
index d78b8355d9..66e2911761 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/IntArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/IntArrayAssert.java
@@ -33,7 +33,7 @@ public class IntArrayAssert extends AbstractIntArrayAssert {
public IntArrayAssert(int[] actual) {
super(actual, IntArrayAssert.class);
}
-
+
public IntArrayAssert(AtomicIntegerArray actual) {
this(array(actual));
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/IterableAssert.java b/assertj-core/src/main/java/org/assertj/core/api/IterableAssert.java
index b6d8813128..f990955116 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/IterableAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/IterableAssert.java
@@ -49,7 +49,6 @@ public static IterableAssert assertThatIterable(Iterable ex
return new IterableAssert<>(actual);
}
-
static Iterable toIterable(Iterator iterator) {
return Streams.stream(iterator).collect(toList());
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/IterableSizeAssert.java b/assertj-core/src/main/java/org/assertj/core/api/IterableSizeAssert.java
index 0a6f1a1ec9..4d4cbd380a 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/IterableSizeAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/IterableSizeAssert.java
@@ -18,10 +18,10 @@
public class IterableSizeAssert extends AbstractIterableSizeAssert, Iterable extends T>, T, ObjectAssert> {
//@format:on
-
private AbstractIterableAssert, Iterable extends T>, T, ObjectAssert> source;
- public IterableSizeAssert(AbstractIterableAssert, Iterable extends T>, T, ObjectAssert> source, Integer i) {
+ public IterableSizeAssert(AbstractIterableAssert, Iterable extends T>, T, ObjectAssert> source,
+ Integer i) {
super(i, IterableSizeAssert.class);
this.source = source;
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Java6Assertions.java b/assertj-core/src/main/java/org/assertj/core/api/Java6Assertions.java
index 1b586a3e42..34ce04035e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Java6Assertions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Java6Assertions.java
@@ -1001,6 +1001,20 @@ public static Short2DArrayAssert assertThat(short[][] actual) {
return new CharSequenceAssert(actual);
}
+ /**
+ * Creates a new instance of {@link CharSequenceAssert}
.
+ *
+ * Use this over {@link #assertThat(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides assertThat
for.
+ *
+ * @param actual the actual value.
+ * @return the created assertion object.
+ * @since 3.25.0
+ */
+ public static AbstractCharSequenceAssert, ? extends CharSequence> assertThatCharSequence(CharSequence actual) {
+ return assertThat(actual);
+ }
+
/**
* Creates a new instance of {@link CharSequenceAssert}
from a {@link StringBuilder}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Java6BDDAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/Java6BDDAssertions.java
index d1344436b3..1ad69f1427 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Java6BDDAssertions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Java6BDDAssertions.java
@@ -56,7 +56,7 @@
@Deprecated
@CheckReturnValue
// Deprecation is raised by JDK-17. IntelliJ thinks this is redundant when it is not.
-@SuppressWarnings({"DeprecatedIsStillUsed", "deprecation", "RedundantSuppression"})
+@SuppressWarnings({ "DeprecatedIsStillUsed", "deprecation", "RedundantSuppression" })
public class Java6BDDAssertions {
/**
@@ -873,6 +873,20 @@ public static Short2DArrayAssert then(short[][] actual) {
return assertThat(actual);
}
+ /**
+ * Creates a new instance of {@link org.assertj.core.api.CharSequenceAssert}
.
+ *
+ * Use this over {@link #then(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides then
for.
+ *
+ * @param actual the actual value.
+ * @return the created assertion object.
+ * @since 3.25.0
+ */
+ public static AbstractCharSequenceAssert, ? extends CharSequence> thenCharSequence(CharSequence actual) {
+ return then(actual);
+ }
+
/**
* Creates a new instance of {@link org.assertj.core.api.CharSequenceAssert}
from a {@link StringBuilder}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Java6BDDSoftAssertionsProvider.java b/assertj-core/src/main/java/org/assertj/core/api/Java6BDDSoftAssertionsProvider.java
index f522612f9c..26aa42c2e5 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Java6BDDSoftAssertionsProvider.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Java6BDDSoftAssertionsProvider.java
@@ -650,6 +650,20 @@ default CharSequenceAssert then(CharSequence actual) {
return proxy(CharSequenceAssert.class, CharSequence.class, actual);
}
+ /**
+ * Creates a new instance of {@link CharSequenceAssert}
.
+ *
+ * Use this over {@link #then(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides then
for.
+ *
+ * @param actual the actual value.
+ * @return the created assertion object.
+ * @since 3.25.0
+ */
+ default CharSequenceAssert thenCharSequence(CharSequence actual) {
+ return then(actual);
+ }
+
/**
* Creates a new instance of {@link CharSequenceAssert}
from a {@link StringBuilder}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Java6StandardSoftAssertionsProvider.java b/assertj-core/src/main/java/org/assertj/core/api/Java6StandardSoftAssertionsProvider.java
index edb11e94a4..82a02ec041 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Java6StandardSoftAssertionsProvider.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Java6StandardSoftAssertionsProvider.java
@@ -645,6 +645,20 @@ default CharSequenceAssert assertThat(CharSequence actual) {
return proxy(CharSequenceAssert.class, CharSequence.class, actual);
}
+ /**
+ * Creates a new instance of {@link CharSequenceAssert}
.
+ *
+ * Use this over {@link #assertThat(CharSequence)} in case of ambiguous method resolution when the object under test
+ * implements several interfaces Assertj provides assertThat
for.
+ *
+ * @param actual the actual value.
+ * @return the created assertion object.
+ * @since 3.25.0
+ */
+ default CharSequenceAssert assertThatCharSequence(CharSequence actual) {
+ return assertThat(actual);
+ }
+
/**
* Creates a new instance of {@link CharSequenceAssert}
from a {@link StringBuilder}.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ListAssert.java b/assertj-core/src/main/java/org/assertj/core/api/ListAssert.java
index bcbf513766..914b2e04be 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ListAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ListAssert.java
@@ -63,6 +63,7 @@ public static ListAssert assertThatLongStream(LongStream actual) {
public static ListAssert assertThatIntStream(IntStream actual) {
return new ListAssert<>(actual);
}
+
public ListAssert(List extends ELEMENT> actual) {
super(actual, ListAssert.class, new ObjectAssertFactory<>());
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/LongAssert.java b/assertj-core/src/main/java/org/assertj/core/api/LongAssert.java
index 0973f079d7..64af85060f 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/LongAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/LongAssert.java
@@ -36,7 +36,7 @@ public LongAssert(Long actual) {
}
public LongAssert(AtomicLong actual) {
- this(actual == null ? null: actual.get());
+ this(actual == null ? null : actual.get());
}
-
+
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/MatcherAssert.java b/assertj-core/src/main/java/org/assertj/core/api/MatcherAssert.java
index 0388989094..3149dad17e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/MatcherAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/MatcherAssert.java
@@ -19,7 +19,7 @@
*
* @author Jiashu Zhang
*/
-public class MatcherAssert extends AbstractMatcherAssert{
+public class MatcherAssert extends AbstractMatcherAssert {
protected MatcherAssert(Matcher actual) {
super(actual, MatcherAssert.class);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/Object2DArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/Object2DArrayAssert.java
index d6a8052648..3a84d63376 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/Object2DArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/Object2DArrayAssert.java
@@ -192,7 +192,7 @@ public Object2DArrayAssert isNotEmpty() {
}
/**
- * Verifies that the actual {@code ELEMENT[][]} has the the given dimensions.
+ * Verifies that the actual {@code ELEMENT[][]} has the given dimensions.
*
* Example:
*
// assertion will pass
diff --git a/assertj-core/src/main/java/org/assertj/core/api/OptionalAssert.java b/assertj-core/src/main/java/org/assertj/core/api/OptionalAssert.java
index d60c7c6d30..6169ae0e0e 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/OptionalAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/OptionalAssert.java
@@ -23,7 +23,7 @@
public class OptionalAssert extends AbstractOptionalAssert, VALUE> {
protected OptionalAssert(Optional actual) {
- super(actual, OptionalAssert.class);
+ super(actual, OptionalAssert.class);
}
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/RecursiveAssertionAssert.java b/assertj-core/src/main/java/org/assertj/core/api/RecursiveAssertionAssert.java
index e7db733c60..0bc884a10b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/RecursiveAssertionAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/RecursiveAssertionAssert.java
@@ -55,10 +55,16 @@ public RecursiveAssertionAssert(Object o, RecursiveAssertionConfiguration recurs
* {@link java.util.function.Predicate} applied to them (including primitive fields), no fields are excluded, but:
*
* The recursion does not enter into Java Class Library types (java.*, javax.*)
- * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but the collection/array itself)
+ * The {@link java.util.function.Predicate} is applied to {@link java.util.Collection} and array elements (but not the collection/array itself)
* The {@link java.util.function.Predicate} is applied to {@link java.util.Map} values but not the map itself or its keys
* The {@link java.util.function.Predicate} is applied to {@link java.util.Optional} and primitive optional values
*
+ * You can change how the recursive assertion deals with arrays, collections, maps and optionals, see:
+ *
+ * {@link RecursiveAssertionAssert#withCollectionAssertionPolicy(RecursiveAssertionConfiguration.CollectionAssertionPolicy)} for collections and arrays
+ * {@link RecursiveAssertionAssert#withMapAssertionPolicy(RecursiveAssertionConfiguration.MapAssertionPolicy)} for maps
+ * {@link RecursiveAssertionAssert#withOptionalAssertionPolicy(RecursiveAssertionConfiguration.OptionalAssertionPolicy)} for optionals
+ *
*
* It is possible to assert several predicates over the object graph in a row.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/RecursiveComparisonAssert.java b/assertj-core/src/main/java/org/assertj/core/api/RecursiveComparisonAssert.java
index cc351b4297..5eb26c26ef 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/RecursiveComparisonAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/RecursiveComparisonAssert.java
@@ -494,16 +494,17 @@ public SELF comparingOnlyFields(String... fieldNamesToCompare) {
/**
* Makes the recursive comparison to only compare given actual fields of the specified types and their subfields (no other fields will be compared).
*
- * Specifying a field of type will make all its subfields to be compared, for example specifying the {@code Person} type will
+ * Specifying a compared type will make any fields of this type and its subfields to be compared, for example specifying the {@code Person} type will
* lead to compare {@code Person.name}, {@code Person.address} and all other {@code Person} fields.
* In case actual's field is null, expected's field type will be checked to match one of the given types (we assume actual and expected fields have the same type).
*
- * {@code ", "} can be combined with {@link #comparingOnlyFields(String...)} to compare fields of the given types or names (union of both sets of fields).
+ * {@code comparingOnlyFieldsOfTypes} can be combined with {@link #comparingOnlyFields(String...)} to compare fields of the given types or names (union of both sets of fields).
*
- * {@code ", "} can be also combined with ignoring fields or compare only fields by name methods to restrict further the fields actually compared,
+ * {@code comparingOnlyFieldsOfTypes} can be also combined with ignoring fields to restrict further the fields actually compared,
* the resulting compared fields = {specified compared fields of types} {@code -} {specified ignored fields}.
- * For example if the specified compared fields of types = {@code {String.class, Integer.class, Double.class}}, when there are fields String foo, {@code Integer baz} and {@code Double bar}
- * and the ignored fields = {"bar"} set with {@link RecursiveComparisonAssert#ignoringFields(String...)} that will remove {@code bar} field from comparison, then only {@code {foo, baz}} fields will be compared.
+ * For example, we specify the following compared types: {@code {String.class, Integer.class, Double.class}}, and the
+ * object to compare has fields {@code String foo}, {@code Integer baz} and {@code Double bar},
+ * if we ignore the {"bar"} field with {@link RecursiveComparisonAssert#ignoringFields(String...)} the comparison will only report differences on {@code {foo, baz}} fields..
*
* Usage example:
*
class Person {
@@ -532,13 +533,16 @@ public SELF comparingOnlyFields(String... fieldNamesToCompare) {
*
* // assertion succeeds as it only compared fields height and home.address.number since their types match compared types
* assertThat(sherlock).usingRecursiveComparison()
- * .", "(Integer.class, Double.class)
+ * .comparingOnlyFieldsOfTypes(Integer.class, Double.class)
* .isEqualTo(moriarty);
*
* // assertion fails as home.address.street fields differ (Home fields and its subfields were compared)
* assertThat(sherlock).usingRecursiveComparison()
- * .", "(Home.class)
+ * .comparingOnlyFieldsOfTypes(Home.class)
* .isEqualTo(moriarty);
+ *
+ * Note that the recursive comparison checks whether the fields actually exist and throws an {@link IllegalArgumentException} if some of them don't,
+ * this is done to catch typos.
*
* @param typesToCompare the types to compare in the recursive comparison.
* @return this {@link RecursiveComparisonAssert} to chain other methods.
@@ -1144,6 +1148,10 @@ public SELF ignoringOverriddenEqualsForFieldsMatchingRegexes(String... regexes)
/**
* Makes the recursive comparison to ignore collection order in all fields in the object under test.
*
+ * Important: ignoring collection order has a high performance cost because each element of the actual collection must
+ * be compared to each element of the expected collection which is a O(n²) operation. For example with a collection of 100
+ * elements, the number of comparisons is 100x100 = 10 000!
+ *
* Example:
*
class Person {
* String name;
@@ -1178,6 +1186,10 @@ public SELF ignoringCollectionOrder() {
/**
* Makes the recursive comparison to ignore collection order in the object under test specified fields. Nested fields can be specified like this: {@code home.address.street}.
*
+ * Important: ignoring collection order has a high performance cost because each element of the actual collection must
+ * be compared to each element of the expected collection which is a O(n²) operation. For example with a collection of 100
+ * elements, the number of comparisons is 100x100 = 10 000!
+ *
* Example:
*
class Person {
* String name;
@@ -1222,6 +1234,10 @@ public SELF ignoringCollectionOrderInFields(String... fieldsToIgnoreCollectionOr
* Nested fields can be specified by using dots like this: {@code home\.address\.street} ({@code \} is used to escape
* dots since they have a special meaning in regexes).
*
+ * Important: ignoring collection order has a high performance cost because each element of the actual collection must
+ * be compared to each element of the expected collection which is a O(n²) operation. For example with a collection of 100
+ * elements, the number of comparisons is 100x100 = 10 000!
+ *
* Example:
*
class Person {
* String name;
@@ -1692,6 +1708,43 @@ public SELF withIntrospectionStrategy(RecursiveComparisonIntrospectionStrategy i
return myself;
}
+ /**
+ * Allows the recursive comparison to compare an enum field against a String field and vice versa.
+ *
+ * Example:
+ *
LightString actual = new LightString("GREEN");
+ * Light expected = new Light(GREEN);
+ *
+ * // compares "GREEN" to GREEN
+ * assertThat(actual).usingRecursiveComparison()
+ * .withEnumStringComparison()
+ * .isEqualTo(expected);
+ *
+ * // compares GREEN to "GREEN"
+ * assertThat(expected).usingRecursiveComparison()
+ * .withEnumStringComparison()
+ * .isEqualTo(actual);
+ * where {@code Light} and {@code LightString} are defined as:
+ * class Light {
+ * Color color;
+ * Light(Color value) {
+ * this.color = value;
+ * }
+ * }
+ *
+ * class LightString {
+ * String color;
+ * LightString(String value) {
+ * this.color = value;
+ * }
+ * }
+ */
+ @CheckReturnValue
+ public SELF withEnumStringComparison() {
+ recursiveComparisonConfiguration.allowComparingEnumAgainstString(true);
+ return myself;
+ }
+
SELF withTypeComparators(TypeComparators typeComparators) {
Optional.ofNullable(typeComparators)
.map(TypeComparators::comparatorByTypes)
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ShortArrayAssert.java b/assertj-core/src/main/java/org/assertj/core/api/ShortArrayAssert.java
index 7bcc81ff78..6a80072484 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ShortArrayAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ShortArrayAssert.java
@@ -12,7 +12,6 @@
*/
package org.assertj.core.api;
-
/**
* Assertion methods for arrays of {@code short}s.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/SoftAssertionsStatement.java b/assertj-core/src/main/java/org/assertj/core/api/SoftAssertionsStatement.java
index 0724b6211a..4e99d21285 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/SoftAssertionsStatement.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/SoftAssertionsStatement.java
@@ -46,10 +46,10 @@ public void evaluate() throws Throwable {
// failed to throw MultipleFailuresError -> throw MultipleFailureException instead
// This new ArrayList() is necessary due to the incompatible type signatures between
// MultipleFailureException.assertEmpty() (takes a List) and errors
- // (which is a List). Ideally assertEmpty() should have been a
+ // (which is a List). Ideally assertEmpty() should have been a
// List extends Throwable>.
MultipleFailureException.assertEmpty(new ArrayList<>(errors));
}
};
}
-}
\ No newline at end of file
+}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/SoftProxies.java b/assertj-core/src/main/java/org/assertj/core/api/SoftProxies.java
index 27621de4f3..3e743e38fd 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/SoftProxies.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/SoftProxies.java
@@ -44,8 +44,14 @@ class SoftProxies {
private static final Junction METHODS_CHANGING_THE_OBJECT_UNDER_TEST = methodsChangingTheObjectUnderTestNamed("asBase64Decoded",
"asBase64Encoded",
+ "asBoolean",
+ "asByte",
+ "asDouble",
+ "asFloat",
"asInstanceOf",
- "asList",
+ "asInt",
+ "asLong",
+ "asShort",
"asString",
"asHexString",
"binaryContent",
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ThrowableAssert.java b/assertj-core/src/main/java/org/assertj/core/api/ThrowableAssert.java
index 95fa1f5163..569c292ad4 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ThrowableAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ThrowableAssert.java
@@ -33,6 +33,10 @@ public interface ThrowingCallable {
void call() throws Throwable;
}
+ public interface ThrowingCallableWithValue {
+ Object call() throws Throwable;
+ }
+
public ThrowableAssert(ACTUAL actual) {
super(actual, ThrowableAssert.class);
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/ThrowingConsumer.java b/assertj-core/src/main/java/org/assertj/core/api/ThrowingConsumer.java
index 721c781e2e..0ca117d9a8 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/ThrowingConsumer.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/ThrowingConsumer.java
@@ -17,7 +17,8 @@
/**
* {@link Consumer} that deals with checked exceptions by rethrowing them as {@link RuntimeException}.
*
- * More precisely, {@link RuntimeException} and {@link AssertionError} are rethrown as they are while any other {@link Throwable} are rethrown as {@link RuntimeException}.
+ * More precisely, {@link RuntimeException} and {@link AssertionError} are rethrown as they are,
+ * while any other {@link Throwable} is wrapped in a {@link RuntimeException} and rethrown.
*
* @param consumed type
*/
@@ -36,4 +37,5 @@ default void accept(final T input) {
}
void acceptThrows(T input) throws Throwable;
+
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/UriAssert.java b/assertj-core/src/main/java/org/assertj/core/api/UriAssert.java
index 687b04ebd7..ba76ab287a 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/UriAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/UriAssert.java
@@ -23,8 +23,7 @@ public class UriAssert extends AbstractUriAssert {
*
* @param actual the url to test
*/
- public UriAssert(URI actual)
- {
+ public UriAssert(URI actual) {
super(actual, UriAssert.class);
}
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/UrlAssert.java b/assertj-core/src/main/java/org/assertj/core/api/UrlAssert.java
index 6fa7432979..06ea04f32d 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/UrlAssert.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/UrlAssert.java
@@ -23,8 +23,7 @@ public class UrlAssert extends AbstractUrlAssert {
*
* @param actual the url to test
*/
- public UrlAssert(URL actual)
- {
+ public UrlAssert(URL actual) {
super(actual, UrlAssert.class);
}
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/WithAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/WithAssertions.java
index 511bc1ebc5..6f0564ebf0 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/WithAssertions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/WithAssertions.java
@@ -238,6 +238,21 @@ default Condition allOf(final Condition super T>... conditions) {
return Assertions.allOf(conditions);
}
+ /**
+ * Create a new {@link ThrowingConsumer}
that delegates the evaluation of the
+ * given consumers to {@link AbstractAssert#satisfies(ThrowingConsumer[])}.
+ *
+ * @param the type of object the given consumers accept
+ * @param consumers the consumers to evaluate
+ * @return the {@code ThrowingConsumer} instance
+ *
+ * @since 3.25.0
+ */
+ @SuppressWarnings("unchecked")
+ default ThrowingConsumer allOf(ThrowingConsumer super T>... consumers) {
+ return Assertions.allOf(consumers);
+ }
+
/**
* Creates a new instance of {@link ObjectArrayAssert}
.
*
@@ -1966,6 +1981,21 @@ default Condition anyOf(final Condition super T>... conditions) {
return Assertions.anyOf(conditions);
}
+ /**
+ * Create a new {@link ThrowingConsumer}
that delegates the evaluation of the
+ * given consumers to {@link AbstractAssert#satisfiesAnyOf(ThrowingConsumer[])}.
+ *
+ * @param the type of object the given consumers accept
+ * @param consumers the consumers to evaluate
+ * @return the {@code ThrowingConsumer} instance
+ *
+ * @since 3.25.0
+ */
+ @SuppressWarnings("unchecked")
+ default ThrowingConsumer anyOf(ThrowingConsumer super T>... consumers) {
+ return Assertions.anyOf(consumers);
+ }
+
/**
* Creates a new {@link DoesNotHave}
.
*
@@ -3030,7 +3060,8 @@ default IOException catchIOException(ThrowingCallable shouldRaiseIOException) {
* @since 3.22.0
*/
default ReflectiveOperationException catchReflectiveOperationException(ThrowingCallable shouldRaiseReflectiveOperationException) {
- return AssertionsForClassTypes.catchThrowableOfType(shouldRaiseReflectiveOperationException, ReflectiveOperationException.class);
+ return AssertionsForClassTypes.catchThrowableOfType(shouldRaiseReflectiveOperationException,
+ ReflectiveOperationException.class);
}
/**
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/AbstractRecursiveOperationConfiguration.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/AbstractRecursiveOperationConfiguration.java
index 00596a5c06..5b4b075c3b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/AbstractRecursiveOperationConfiguration.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/AbstractRecursiveOperationConfiguration.java
@@ -43,8 +43,7 @@ protected AbstractRecursiveOperationConfiguration(AbstractBuilder> builder) {
ignoreFieldsOfTypes(builder.ignoredTypes);
}
- protected AbstractRecursiveOperationConfiguration() {
- }
+ protected AbstractRecursiveOperationConfiguration() {}
/**
* Adds the given fields to the set of fields from the object under test to ignore in the recursive comparison.
@@ -146,7 +145,6 @@ public Set> getIgnoredTypes() {
return ignoredTypes;
}
-
protected void describeIgnoredFields(StringBuilder description) {
if (!getIgnoredFields().isEmpty())
description.append(format("- the following fields were ignored in the comparison: %s%n", describeIgnoredFields()));
@@ -177,12 +175,13 @@ protected static String join(Collection typesDescription) {
}
public boolean matchesAnIgnoredFieldRegex(FieldLocation fieldLocation) {
- return getIgnoredFieldsRegexes().stream()
- .anyMatch(regex -> regex.matcher(fieldLocation.getPathToUseInRules()).matches());
+ // checks parent fields as if a parent field is ignored all subfields (including this field location) should be too.
+ return getIgnoredFieldsRegexes().stream().anyMatch(fieldLocation::hierarchyMatchesRegex);
}
public boolean matchesAnIgnoredField(FieldLocation fieldLocation) {
- return getIgnoredFields().stream().anyMatch(fieldLocation::matches);
+ // checks parent fields as if a parent field is ignored all subfields (including this field location) should be too.
+ return getIgnoredFields().stream().anyMatch(fieldLocation::hierarchyMatches);
}
private String describeIgnoredFields() {
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionConfiguration.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionConfiguration.java
index ee5c8beb32..ce435d8df7 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionConfiguration.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionConfiguration.java
@@ -708,8 +708,7 @@ public enum MapAssertionPolicy {
/**
* Possible policies to use regarding optionals when recursively asserting over the fields of an object tree.
*/
- public enum OptionalAssertionPolicy
- {
+ public enum OptionalAssertionPolicy {
/**
* Apply the {@link Predicate} (recursively) to the value of the optional field but not the optional field.
*
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionDriver.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionDriver.java
index 6b85f10ab1..4b8e000183 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionDriver.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/assertion/RecursiveAssertionDriver.java
@@ -154,6 +154,9 @@ private void doRecursionForSpecialTypes(Predicate predicate, Object node
private void recurseIntoCollection(Predicate predicate, Collection> collection, FieldLocation fieldLocation) {
// TODO handle collection if needed by policy
+ if (collection == null) {
+ return; // no way to recursive into the collection, anyway the collection node has already been visited
+ }
int index = 0;
for (Object element : collection) {
assertRecursively(predicate, element, safeGetClass(element), fieldLocation.field(format(INDEX_FORMAT, index)));
@@ -162,6 +165,9 @@ private void recurseIntoCollection(Predicate predicate, Collection> co
}
private void recurseIntoArray(Predicate predicate, Object node, Class> nodeType, FieldLocation fieldLocation) {
+ if (node == null) {
+ return; // no way to recursive into the array, anyway the array node has already been visited
+ }
Class> arrayType = nodeType.getComponentType();
Object[] array = Arrays.asObjectArray(node);
for (int i = 0; i < array.length; i++) {
@@ -198,6 +204,9 @@ private void recurseIntoOptional(Predicate predicate, Object node, Field
private void recurseIntoMap(Predicate predicate, Map, ?> node, FieldLocation fieldLocation) {
// If we are here, we can assume the policy is not MAP_OBJECT_ONLY
// For both policies VALUES_ONLY and MAP_OBJECT_AND_ENTRIES we have to recurse over the values.
+ if (node == null) {
+ return; // no way to recursive into the map, anyway the map node has already been visited
+ }
recurseIntoMapValues(predicate, node, fieldLocation);
if (configuration.getMapAssertionPolicy() == MAP_OBJECT_AND_ENTRIES) {
recurseIntoMapKeys(predicate, node, fieldLocation);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingFields.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingFields.java
index 0bff80f90b..9b6b55e74d 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingFields.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingFields.java
@@ -12,11 +12,12 @@
*/
package org.assertj.core.api.recursive.comparison;
-import static org.assertj.core.internal.Objects.getFieldsNames;
-
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.assertj.core.internal.Objects;
import org.assertj.core.util.introspection.FieldSupport;
/**
@@ -27,9 +28,13 @@ public class ComparingFields implements RecursiveComparisonIntrospectionStrategy
public static final ComparingFields COMPARING_FIELDS = new ComparingFields();
+ // use ConcurrentHashMap in case this strategy instance is used in a multi-thread context
+ private final Map, Set> fieldNamesPerClass = new ConcurrentHashMap<>();
+
@Override
public Set getChildrenNodeNamesOf(Object node) {
- return node == null ? new HashSet<>() : getFieldsNames(node.getClass());
+ if (node == null) return new HashSet<>();
+ return fieldNamesPerClass.computeIfAbsent(node.getClass(), Objects::getFieldsNames);
}
@Override
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingNormalizedFields.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingNormalizedFields.java
index da128c278c..e779e7ab11 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingNormalizedFields.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingNormalizedFields.java
@@ -21,6 +21,7 @@
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.assertj.core.internal.Objects;
import org.assertj.core.util.introspection.IntrospectionError;
@@ -41,6 +42,9 @@ public abstract class ComparingNormalizedFields implements RecursiveComparisonIn
// original field name <-> normalized field name by node
private final Map> originalFieldNamesByNormalizedFieldNameByNode = new IdentityHashMap<>();
+ // use ConcurrentHashMap in case this strategy instance is used in a multi-thread context
+ private final Map, Set> fieldNamesPerClass = new ConcurrentHashMap<>();
+
/**
* Returns the normalized names of the children nodes of the given object that will be used in the recursive comparison.
*
@@ -55,7 +59,10 @@ public Set getChildrenNodeNamesOf(Object node) {
Set fieldsNames = Objects.getFieldsNames(node.getClass());
// we normalize fields so that we can compare actual and expected, for example if actual has a firstName field and expected
// a first_name field, we won't find firstName in expected unless we normalize it
- return fieldsNames.stream().map(fieldsName -> normalize(node, fieldsName)).collect(toSet());
+ return fieldNamesPerClass.computeIfAbsent(node.getClass(),
+ unused -> fieldsNames.stream()
+ .map(fieldsName -> normalize(node, fieldsName))
+ .collect(toSet()));
}
/**
@@ -124,15 +131,22 @@ public Object getChildNodeValue(String fieldName, Object instance) {
try {
return COMPARISON.getSimpleValue(fieldName, instance);
} catch (Exception e) {
- String originalFieldName = originalFieldNamesByNormalizedFieldNameByNode.get(instance).get(fieldName);
+ String originalFieldName = getOriginalFieldName(fieldName, instance);
try {
return COMPARISON.getSimpleValue(originalFieldName, instance);
} catch (Exception ex) {
- throw new IntrospectionError(format(NO_FIELD_FOUND, instance, fieldName, originalFieldName, ex));
+ throw new IntrospectionError(format(NO_FIELD_FOUND, instance, fieldName, originalFieldName), ex);
}
}
}
+ private String getOriginalFieldName(String fieldName, Object instance) {
+ // call getChildrenNodeNamesOf to populate originalFieldNamesByNormalizedFieldNameByNode, the recursive comparison
+ // should already do this if this is used outside then getChildNodeValue would fail
+ if (!originalFieldNamesByNormalizedFieldNameByNode.containsKey(instance)) getChildrenNodeNamesOf(instance);
+ return originalFieldNamesByNormalizedFieldNameByNode.get(instance).get(fieldName);
+ }
+
@Override
public String getDescription() {
return "comparing normalized fields";
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingProperties.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingProperties.java
index 7cedd4b5a1..6aae2ad518 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingProperties.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparingProperties.java
@@ -15,12 +15,15 @@
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.util.introspection.ClassUtils.isInJavaLangPackage;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.assertj.core.util.introspection.PropertySupport;
@@ -35,9 +38,13 @@ public class ComparingProperties implements RecursiveComparisonIntrospectionStra
private static final String GET_PREFIX = "get";
private static final String IS_PREFIX = "is";
+ // use ConcurrentHashMap in case this strategy instance is used in a multi-thread context
+ private final Map, Set> propertiesNamesPerClass = new ConcurrentHashMap<>();
+
@Override
public Set getChildrenNodeNamesOf(Object node) {
- return node == null ? new HashSet<>() : getPropertiesNamesOf(node.getClass());
+ if (node == null) return new HashSet<>();
+ return propertiesNamesPerClass.computeIfAbsent(node.getClass(), ComparingProperties::getPropertiesNamesOf);
}
@Override
@@ -65,31 +72,20 @@ private static String toPropertyName(String methodName) {
}
public static Set gettersIncludingInheritedOf(Class> clazz) {
- Set getters = gettersOf(clazz);
- // get fields declared in superClass
- Class> superClass = clazz.getSuperclass();
- while (superClass != null && !superClass.getName().startsWith("java.lang")) {
- getters.addAll(gettersOf(superClass));
- superClass = superClass.getSuperclass();
- }
- return getters;
+ return gettersOf(clazz);
}
private static Set gettersOf(Class> clazz) {
- return stream(clazz.getDeclaredMethods()).filter(method -> !isStatic(method))
- .filter(ComparingProperties::isPublic)
- .filter(ComparingProperties::isGetter)
- .collect(toCollection(LinkedHashSet::new));
+ return stream(clazz.getMethods()).filter(method -> !isInJavaLangPackage(method.getDeclaringClass()))
+ .filter(method -> !isStatic(method))
+ .filter(ComparingProperties::isGetter)
+ .collect(toCollection(LinkedHashSet::new));
}
private static boolean isStatic(Method method) {
return Modifier.isStatic(method.getModifiers());
}
- private static boolean isPublic(Method method) {
- return Modifier.isPublic(method.getModifiers());
- }
-
private static boolean isGetter(Method method) {
if (hasParameters(method)) return false;
return isRegularGetter(method) || isBooleanProperty(method);
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparisonDifference.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparisonDifference.java
index 4f506832d3..516d80df96 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparisonDifference.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/ComparisonDifference.java
@@ -92,6 +92,10 @@ public Optional getAdditionalInformation() {
return additionalInformation;
}
+ public List getDecomposedPath() {
+ return decomposedPath;
+ }
+
@Override
public String toString() {
return additionalInformation.isPresent()
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DefaultRecursiveComparisonIntrospectionStrategy.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DefaultRecursiveComparisonIntrospectionStrategy.java
index bbf833c1e8..2d63b6fcc7 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DefaultRecursiveComparisonIntrospectionStrategy.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DefaultRecursiveComparisonIntrospectionStrategy.java
@@ -15,7 +15,9 @@
import static org.assertj.core.util.introspection.PropertyOrFieldSupport.COMPARISON;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.assertj.core.internal.Objects;
import org.assertj.core.util.introspection.PropertyOrFieldSupport;
@@ -28,9 +30,15 @@
*/
public class DefaultRecursiveComparisonIntrospectionStrategy implements RecursiveComparisonIntrospectionStrategy {
+ // use ConcurrentHashMap in case this strategy instance is used in a multi-thread context
+ private final Map, Set> fieldNamesPerClass = new ConcurrentHashMap<>();
+
@Override
public Set getChildrenNodeNamesOf(Object node) {
- return node == null ? new HashSet<>() : Objects.getFieldsNames(node.getClass());
+ if (node == null) return new HashSet<>();
+ // Caches the names after getting them for efficiency, a node can be introspected multiple times for example if
+ // it belongs to an unordered collection as all actual elements are compared to all expected elements.
+ return fieldNamesPerClass.computeIfAbsent(node.getClass(), Objects::getFieldsNames);
}
@Override
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValue.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValue.java
index f43a6bc616..6bde4f1ba4 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValue.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValue.java
@@ -68,10 +68,22 @@ public DualValue(FieldLocation fieldLocation, Object actualFieldValue, Object ex
public boolean equals(Object other) {
if (!(other instanceof DualValue)) return false;
DualValue that = (DualValue) other;
- // it is critical to compare by reference when tracking visited dual values.
- // see should_fix_1854_minimal_test for an explanation
- // TODO add field location check?
- return actual == that.actual && expected == that.expected;
+ return actual == that.actual && expected == that.expected && fieldLocation.equals(that.fieldLocation);
+ }
+
+ /**
+ * If we want to detect potential cycles in the recursive comparison, we need to check if an object has already been visited.
+ *
+ * We must ignore the {@link FieldLocation} otherwise we would not find cycles. Let's take for example a {@code Person} class
+ * with a neighbor field. We have a cycle between the person instance and its neighbor instance, ex: Jack has Tim as neighbor
+ * and vice versa, when we navigate to Tim we find that its neighbor is Jack, we have already visited it but the location is
+ * different, Jack is both the root object and root.neighbor.neighbor (Jack=root, Tim=root.neighbor and Tim.neighbor=Jack)
+ *
+ * @param dualValue the {@link DualValue} to compare
+ * @return true if dual values references the same values (ignoring the field location)
+ */
+ public boolean sameValues(DualValue dualValue) {
+ return actual == dualValue.actual && expected == dualValue.expected;
}
@Override
@@ -111,7 +123,10 @@ public boolean hasSomeJavaTypeValue() {
private static boolean isJavaType(Object o) {
if (o == null) return false;
String className = o.getClass().getName();
- return className.startsWith("java.") || className.startsWith("sun.");
+ return className.startsWith("java.")
+ || className.startsWith("javax.")
+ || className.startsWith("sun.")
+ || className.startsWith("com.sun.");
}
public boolean isExpectedFieldAnArray() {
@@ -288,7 +303,7 @@ private static boolean isAnOrderedCollection(Object value) {
}
public boolean isExpectedAnEnum() {
- return expected.getClass().isEnum();
+ return expected != null && expected.getClass().isEnum();
}
public boolean isActualAnEnum() {
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValueDeque.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValueDeque.java
index e2f22ab305..ad7442fe2b 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValueDeque.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/DualValueDeque.java
@@ -28,13 +28,13 @@ public DualValueDeque(RecursiveComparisonConfiguration recursiveComparisonConfig
@Override
public boolean add(DualValue dualKey) {
- if (shouldIgnore(dualKey)) return false;
+ if (shouldNotEvaluate(dualKey)) return false;
return super.add(dualKey);
}
@Override
public void add(int index, DualValue dualKey) {
- if (shouldIgnore(dualKey)) return;
+ if (shouldNotEvaluate(dualKey)) return;
super.add(index, dualKey);
}
@@ -45,22 +45,31 @@ public boolean addAll(int index, Collection extends DualValue> collection) {
@Override
public void addFirst(DualValue dualKey) {
- if (shouldIgnore(dualKey)) return;
+ if (shouldNotEvaluate(dualKey)) return;
super.addFirst(dualKey);
}
@Override
public void addLast(DualValue dualKey) {
- if (shouldIgnore(dualKey)) return;
+ if (shouldNotEvaluate(dualKey)) return;
super.addLast(dualKey);
}
- private boolean shouldIgnore(DualValue dualKey) {
- return recursiveComparisonConfiguration.shouldIgnore(dualKey);
+ /**
+ * Decides whether the value needs to evaluated, note that we need to evaluate all values if we have
+ * compared types registered as a value could have fields of type to compare.
+ *
+ * For example if we want to compare Employee in a Company, we need to evaluate company as it holds a list of Employee.
+ *
+ * @param dualValue the value to check
+ * @return true if we want to register the value for evaluation, false otherwise
+ */
+ private boolean shouldNotEvaluate(DualValue dualValue) {
+ return recursiveComparisonConfiguration.shouldNotEvaluate(dualValue);
}
private boolean shouldAddDualKey(DualValue dualKey) {
- return !shouldIgnore(dualKey);
+ return !shouldNotEvaluate(dualKey);
}
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/FieldLocation.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/FieldLocation.java
index 03a8de5857..e430b695e7 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/FieldLocation.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/FieldLocation.java
@@ -16,11 +16,13 @@
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
import static org.assertj.core.util.Lists.list;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.regex.Pattern;
/**
* Represents the path to a given field. Immutable
@@ -30,25 +32,106 @@
public final class FieldLocation implements Comparable {
private final String pathToUseInRules;
- private final List decomposedPath; // TODO is it useful?
+ private final List decomposedPath;
+ private final List pathsHierarchyToUseInRules;
public FieldLocation(List path) {
decomposedPath = unmodifiableList(requireNonNull(path, "path cannot be null"));
pathToUseInRules = pathToUseInRules(decomposedPath);
+ pathsHierarchyToUseInRules = pathsHierarchyToUseInRules();
}
public FieldLocation(String s) {
this(list(s.split("\\.")));
}
- public boolean matches(FieldLocation field) {
- return pathToUseInRules.equals(field.pathToUseInRules);
+ public boolean exactlyMatches(FieldLocation field) {
+ return exactlyMatches(field.pathToUseInRules);
}
- public boolean matches(String fieldPath) {
+ public boolean exactlyMatches(String fieldPath) {
return pathToUseInRules.equals(fieldPath);
}
+ /**
+ * Reruns true if it exactly matches this field, false otherwise.
+ *
+ * @param fieldPath field path to check
+ * @return true if it exactly matches this field, false otherwise
+ * @deprecated use {@link #exactlyMatches(String)} instead.
+ */
+ @Deprecated
+ public boolean matches(String fieldPath) {
+ return exactlyMatches(fieldPath);
+ }
+
+ /**
+ * Reruns true if it exactly matches this field, false otherwise.
+ *
+ * @param field field to check
+ * @return true if it exactly matches this field, false otherwise
+ * @deprecated use {@link #exactlyMatches(String)} instead.
+ */
+ @Deprecated
+ public boolean matches(FieldLocation field) {
+ return exactlyMatches(field);
+ }
+
+ /**
+ * Checks whether this fieldLocation or any of its parents matches the given fieldPath.
+ *
+ * Examples:
+ *
+ * | fieldLocation | fieldPath | matches?
+ * -----------------------------------------------
+ * | name.first | "name" | true
+ * | name.first.nickname | "name" | true
+ * | name.first | "name.first" | true
+ * | name.first.nickname | "name.first" | true
+ * | name | "name" | true
+ * | name | "name.first" | false
+ * | person.name | "name" | false
+ * | names | "name" | false
+ * | nickname | "name" | false
+ * | name | "nickname" | false
+ * | first.nickname | "name" | false
+ *
+ *
+ * @param fieldPath the field path to test
+ * @return true if this fieldLocation is the given fieldPath or a child of it, false otherwise.
+ */
+ public boolean hierarchyMatches(String fieldPath) {
+ return pathsHierarchyToUseInRules.contains(fieldPath);
+ }
+
+ /**
+ * Checks whether this fieldLocation or any of its parents matches the given regex.
+ *
+ * Examples:
+ *
+ * | fieldLocation | regex | matches?
+ * -----------------------------------------------
+ * | name.first | "name" | true
+ * | name.first | "..me" | true
+ * | name.first.nickname | "name" | true
+ * | name.first | "name.first" | true
+ * | name.first.nickname | "name.first" | true
+ * | name | "name" | true
+ * | name | "name.first" | false
+ * | person.name | "name" | false
+ * | names | "name" | false
+ * | nickname | "name" | false
+ * | name | "nickname" | false
+ * | first.nickname | "name" | false
+ *
+ *
+ * @param regex the regex to test
+ * @return true this fieldLocation or any of its parent matches the given regex., false otherwise.
+ */
+ public boolean hierarchyMatchesRegex(Pattern regex) {
+ return pathsHierarchyToUseInRules.stream().anyMatch(path -> regex.matcher(path).matches());
+ }
+
public List getDecomposedPath() {
return decomposedPath;
}
@@ -74,17 +157,18 @@ public boolean equals(Object obj) {
if (!(obj instanceof FieldLocation)) return false;
FieldLocation that = (FieldLocation) obj;
return Objects.equals(pathToUseInRules, that.pathToUseInRules)
- && Objects.equals(decomposedPath, that.decomposedPath);
+ && Objects.equals(decomposedPath, that.decomposedPath)
+ && Objects.equals(pathsHierarchyToUseInRules, that.pathsHierarchyToUseInRules);
}
@Override
public int hashCode() {
- return Objects.hash(pathToUseInRules, decomposedPath);
+ return Objects.hash(pathToUseInRules, decomposedPath, pathsHierarchyToUseInRules);
}
@Override
public String toString() {
- return String.format("FieldLocation [pathToUseInRules=%s, decomposedPath=%s]", pathToUseInRules, decomposedPath);
+ return String.format("<%s>", pathToUseInRules);
}
public String shortDescription() {
@@ -108,6 +192,16 @@ public String getFieldName() {
return decomposedPath.get(decomposedPath.size() - 1);
}
+ public boolean isRoot() {
+ // root is the top level object compared or in case of the top level is a iterable/array the elements are considered as roots.
+ // we don't do it for optional it has a 'value' field so for the moment
+ return pathToUseInRules.isEmpty();
+ }
+
+ public boolean isTopLevelField() {
+ return !isRoot() && !pathToUseInRules.contains(".");
+ }
+
public static FieldLocation rootFieldLocation() {
return new FieldLocation(emptyList());
}
@@ -116,7 +210,8 @@ public static FieldLocation rootFieldLocation() {
* Returns true if this has the given parent (direct or indirect), false otherwise.
*
* Examples:
- *
| field | parent | hasParent?
+ *
+ * | field | parent | hasParent?
* -----------------------------------------------
* | "name.first" | "name" | true
* | "name.first.nickname" | "name" | true
@@ -140,7 +235,8 @@ public boolean hasParent(FieldLocation parent) {
* Returns true if this field has the given child (direct or indirect), false otherwise.
*
* Examples:
- *
| field | child | hasChild?
+ *
+ * | field | child | hasChild?
* -----------------------------------------------
* | "name" | "name.first" | true
* | "name" | "name.last" | true
@@ -160,4 +256,21 @@ public boolean hasChild(FieldLocation child) {
return child.hasParent(this);
}
+ private List pathsHierarchyToUseInRules() {
+ List fieldAndParentFields = list();
+ FieldLocation currentLocation = this;
+ while (!currentLocation.isRoot()) {
+ fieldAndParentFields.add(currentLocation);
+ currentLocation = currentLocation.parent();
+ }
+ return fieldAndParentFields.stream()
+ .map(fieldLocation -> fieldLocation.pathToUseInRules)
+ .collect(toList());
+ }
+
+ private FieldLocation parent() {
+ List parentPath = new ArrayList<>(decomposedPath);
+ parentPath.remove(decomposedPath.size() - 1);
+ return new FieldLocation(parentPath);
+ }
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparator.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparator.java
index 43c7ccf49a..26ec9d47d7 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparator.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparator.java
@@ -20,10 +20,10 @@
/**
* {@code Comparator} comparing objects recursively as in {@link org.assertj.core.api.RecursiveComparisonAssert}.
*
- * This comparator does not enforce any ordering, it just returns 0 if compared objects are equals according the recursive
- * comparison and a non 0 value otherwise.
- *
- * This comparator honors the {@link RecursiveComparisonConfiguration} passed at construction time.
+ * This comparator does not enforce any ordering and returns zero if the compared objects are equals,
+ * according to the recursive comparison, or a non-zero value otherwise.
+ *
+ * @since 3.24.0
*/
public class RecursiveComparator implements Comparator {
@@ -31,10 +31,21 @@ public class RecursiveComparator implements Comparator {
private final RecursiveComparisonDifferenceCalculator recursiveComparisonDifferenceCalculator;
/**
- * Returns a new {@code RecursiveComparator} that uses the given {@link RecursiveComparisonConfiguration} when comparing
- * objects with the recursive comparison.
+ * Returns a new {@code RecursiveComparator} that uses the default {@link RecursiveComparisonConfiguration}
+ * when comparing objects with the recursive comparison.
*
- * @param recursiveComparisonConfiguration the used {@code RecursiveComparisonConfiguration}
+ * @since 3.25.0
+ */
+ public RecursiveComparator() {
+ this.recursiveComparisonConfiguration = new RecursiveComparisonConfiguration();
+ this.recursiveComparisonDifferenceCalculator = new RecursiveComparisonDifferenceCalculator();
+ }
+
+ /**
+ * Returns a new {@code RecursiveComparator} that uses the given {@link RecursiveComparisonConfiguration}
+ * when comparing objects with the recursive comparison.
+ *
+ * @param recursiveComparisonConfiguration the {@code RecursiveComparisonConfiguration} instance to be used
*/
public RecursiveComparator(RecursiveComparisonConfiguration recursiveComparisonConfiguration) {
this.recursiveComparisonConfiguration = recursiveComparisonConfiguration;
@@ -45,12 +56,17 @@ private List determineDifferencesWith(Object actual, Objec
return recursiveComparisonDifferenceCalculator.determineDifferences(actual, expected, recursiveComparisonConfiguration);
}
+ public String getDescription() {
+ return format("RecursiveComparator a comparator based on the recursive comparison with the following configuration:%n%s",
+ recursiveComparisonConfiguration);
+ }
+
/**
- * Returns 0 if the arguments are recursively equal to each other, a non-zero otherwise (no ordering enforced).
+ * Returns zero if the arguments are recursively equal to each other, or non-zero otherwise (no ordering enforced).
*
* @param actual the object to compare to {@code other}
* @param other the object to compare to {@code actual}
- * @return 0 if the arguments are recursively equal to each other, a non-zero otherwise.
+ * @return zero if the arguments are recursively equal to each other, or non-zero otherwise.
*/
@Override
public int compare(Object actual, Object other) {
@@ -60,8 +76,4 @@ public int compare(Object actual, Object other) {
return -1;
}
- public String getDescription() {
- return format("RecursiveComparator a comparator based on the recursive comparison with the following configuration:%n%s",
- recursiveComparisonConfiguration);
- }
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonConfiguration.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonConfiguration.java
index 229e06dd17..98cc3d5994 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonConfiguration.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonConfiguration.java
@@ -19,6 +19,7 @@
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.assertj.core.configuration.ConfigurationProvider.CONFIGURATION_PROVIDER;
+import static org.assertj.core.data.MapEntry.entry;
import static org.assertj.core.internal.TypeComparators.defaultTypeComparators;
import static org.assertj.core.util.Lists.list;
import static org.assertj.core.util.Sets.newLinkedHashSet;
@@ -27,8 +28,11 @@
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import java.util.Set;
+import java.util.TreeMap;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -79,11 +83,17 @@ public class RecursiveComparisonConfiguration extends AbstractRecursiveOperation
private FieldMessages fieldMessages = new FieldMessages();
// track field locations of fields of type to compare, needed to compare child nodes
- // for example if we want to compare Person type, we must compare Person fields too event thought they are not of type Person
- private final Set fieldLocationOfFieldsOfTypesToCompare = new LinkedHashSet<>();
+ // for example if we want to compare Person type, we must compare Person fields too event though they are not of type Person
+ private final Set fieldLocationsToCompareBecauseOfTypesToCompare = new LinkedHashSet<>();
+
+ public void registerFieldLocationToCompareBecauseOfTypesToCompare(FieldLocation fieldLocation) {
+ fieldLocationsToCompareBecauseOfTypesToCompare.add(fieldLocation);
+ }
private RecursiveComparisonIntrospectionStrategy introspectionStrategy = DEFAULT_RECURSIVE_COMPARISON_INTROSPECTION_STRATEGY;
+ private boolean compareEnumAgainstString = false;
+
private RecursiveComparisonConfiguration(Builder builder) {
super(builder);
this.ignoreAllActualNullFields = builder.ignoreAllActualNullFields;
@@ -220,6 +230,9 @@ public void setIgnoreAllExpectedNullFields(boolean ignoreAllExpectedNullFields)
* on the other hand if you specify {@code person.name}, {@code person} won't be compared but {@code person.name} will be.
*
* See {@link RecursiveComparisonAssert#comparingOnlyFields(String...) RecursiveComparisonAssert#comparingOnlyFields(String...)} for examples.
+ *
+ * Note that the recursive comparison checks whether the fields actually exist and throws an {@link IllegalArgumentException} if some of them don't,
+ * this is done to catch typos.
*
* @param fieldNamesToCompare the fields of the object under test to compare in the comparison.
*/
@@ -227,7 +240,6 @@ public void compareOnlyFields(String... fieldNamesToCompare) {
Stream.of(fieldNamesToCompare).map(FieldLocation::new).forEach(comparedFields::add);
}
-
/**
* Adds the given fields of types and their subfields to the set of fields from the object under test to compare (fields of other types will not be compared).
*
@@ -251,6 +263,16 @@ public Set getComparedFields() {
return comparedFields;
}
+ boolean someComparedFieldsHaveBeenSpecified() {
+ return !comparedFields.isEmpty();
+ }
+
+ boolean isOrIsChildOfAnyComparedFields(FieldLocation currentFieldLocation) {
+ return comparedFields.stream()
+ .anyMatch(comparedField -> comparedField.equals(currentFieldLocation)
+ || comparedField.hasChild(currentFieldLocation));
+ }
+
/**
* Returns the set of type to compare from the object under test (fields of other types will not be compared).
*
@@ -322,6 +344,10 @@ boolean getIgnoreCollectionOrder() {
/**
* Sets whether to ignore collection order in the comparison.
*
+ * Important: ignoring collection order has a high performance cost because each element of the actual collection must
+ * be compared to each element of the expected collection which is a O(n²) operation. For example with a collection of 100
+ * elements, the number of comparisons is 100x100 = 10 000!
+ *
* See {@link RecursiveComparisonAssert#ignoringCollectionOrder()} for code examples.
*
* @param ignoreCollectionOrder whether to ignore collection order in the comparison.
@@ -333,6 +359,10 @@ public void ignoreCollectionOrder(boolean ignoreCollectionOrder) {
/**
* Adds the given fields to the list fields from the object under test to ignore collection order in the recursive comparison.
*
+ * Important: ignoring collection order has a high performance cost because each element of the actual collection must
+ * be compared to each element of the expected collection which is a O(n²) operation. For example with a collection of 100
+ * elements, the number of comparisons is 100x100 = 10 000!
+ *
* See {@link RecursiveComparisonAssert#ignoringCollectionOrderInFields(String...) RecursiveComparisonAssert#ignoringCollectionOrderInFields(String...)} for examples.
*
* @param fieldsToIgnoreCollectionOrder the fields of the object under test to ignore collection order in the comparison.
@@ -568,6 +598,21 @@ public void setIntrospectionStrategy(RecursiveComparisonIntrospectionStrategy in
this.introspectionStrategy = introspectionStrategy;
}
+ /**
+ * Allows the recursive comparison to compare an enum field against a string field.
+ *
+ * See {@link RecursiveComparisonAssert#withEnumStringComparison()} for code examples.
+ *
+ * @param compareEnumAgainstString whether to allow the recursive comparison to compare enum field against string field.
+ */
+ public void allowComparingEnumAgainstString(boolean compareEnumAgainstString) {
+ this.compareEnumAgainstString = compareEnumAgainstString;
+ }
+
+ public boolean isComparingEnumAgainstStringAllowed() {
+ return this.compareEnumAgainstString;
+ }
+
@Override
public String toString() {
return multiLineDescription(CONFIGURATION_PROVIDER.representation());
@@ -578,11 +623,10 @@ public int hashCode() {
return java.util.Objects.hash(fieldComparators, ignoreAllActualEmptyOptionalFields, ignoreAllActualNullFields,
ignoreAllExpectedNullFields, ignoreAllOverriddenEquals, ignoreCollectionOrder,
ignoredCollectionOrderInFields, ignoredCollectionOrderInFieldsMatchingRegexes,
- getIgnoredFields(),
- getIgnoredFieldsRegexes(), ignoredOverriddenEqualsForFields,
- ignoredOverriddenEqualsForTypes,
- ignoredOverriddenEqualsForFieldsMatchingRegexes, getIgnoredTypes(), strictTypeChecking,
- typeComparators, comparedFields, comparedTypes, fieldMessages, typeMessages);
+ getIgnoredFields(), getIgnoredFieldsRegexes(), ignoredOverriddenEqualsForFields,
+ ignoredOverriddenEqualsForTypes, ignoredOverriddenEqualsForFieldsMatchingRegexes,
+ getIgnoredTypes(), strictTypeChecking, typeComparators, comparedFields, comparedTypes,
+ fieldMessages, typeMessages, compareEnumAgainstString);
}
@Override
@@ -635,34 +679,48 @@ public String multiLineDescription(Representation representation) {
describeRegisteredErrorMessagesForFields(description);
describeRegisteredErrorMessagesForTypes(description);
describeIntrospectionStrategy(description);
+ describeCompareEnumAgainstString(description);
return description.toString();
}
+ boolean shouldNotEvaluate(DualValue dualValue) {
+ // if we have some compared types, we can't discard any values since they could have fields we need to compare.
+ if (!comparedTypes.isEmpty()) return false;
+ return shouldIgnore(dualValue);
+ }
+
boolean shouldIgnore(DualValue dualValue) {
- return !shouldBeCompared(dualValue) || shouldIgnoreFieldBasedOnFieldLocation(dualValue.fieldLocation) || shouldIgnoreFieldBasedOnFieldValue(dualValue);
+ return shouldIgnoreFieldBasedOnFieldLocation(dualValue.fieldLocation)
+ || shouldIgnoreFieldBasedOnFieldValue(dualValue);
}
private boolean shouldBeCompared(DualValue dualValue) {
- // empty comparedFields and comparedTypes <=> no restriction on compared fields <=> must be compared
+ // no comparedFields and comparedTypes <=> no restriction on compared fields => everything must be compared
if (comparedFields.isEmpty() && comparedTypes.isEmpty()) return true;
- return shouldBeComparedBasedOnFieldLocation(dualValue.fieldLocation) || shouldBeComparedBasedOnFieldValue(dualValue);
- }
-
- private boolean shouldBeComparedBasedOnFieldLocation(FieldLocation fieldLocation) {
- return comparedFields.stream().anyMatch(matchesComparedField(fieldLocation));
+ // if we have compared types, we can't ignore any values since they could have fields of types to compare
+ if (!comparedTypes.isEmpty()) return true;
+ return comparedFields.stream().anyMatch(matchesComparedField(dualValue.fieldLocation));
}
private static Predicate matchesComparedField(FieldLocation field) {
// a field f must be compared if any compared fields is f itself (obviously), a parent of f or a child of f.
// - "name.first" must be compared if "name" is a compared field so will other "name" subfields like "name.last"
// - "name" must be compared if "name.first" is a compared field otherwise "name" is ignored and "name.first" too
- return comparedField -> field.matches(comparedField) // exact match
+ return comparedField -> field.isRoot() // always compare root!
+ || field.exactlyMatches(comparedField)
|| field.hasParent(comparedField) // ex: field "name.first" and "name" compared field
|| field.hasChild(comparedField); // ex: field "name" and "name.first" compared field
}
Set getActualChildrenNodeNamesToCompare(DualValue dualValue) {
Set actualChildrenNodeNames = getChildrenNodeNamesOf(dualValue.actual);
+ // if we have some compared types, we can't discard any nodes since they could have fields we need to compare.
+ // we could evaluate the whole graphs to figure that but that would be bad performance wise so add everything
+ // and exclude later on any differences that were on fields not to compare
+ if (!comparedTypes.isEmpty()) {
+ registerFieldLocationOfFieldsOfTypesToCompare(dualValue);
+ return actualChildrenNodeNames;
+ }
// we are doing the same as shouldIgnore(DualValue dualValue) but in two steps for performance reasons:
// - we filter first ignored nodes by names that don't need building DualValues
// - then we filter field DualValues with the remaining criteria that need to get the node value
@@ -775,8 +833,8 @@ private void describeIgnoreAllExpectedNullFields(StringBuilder description) {
private void describeOverriddenEqualsMethodsUsage(StringBuilder description, Representation representation) {
String header = ignoreAllOverriddenEquals
- ? "- no overridden equals methods were used in the comparison (except for java types)"
- : "- overridden equals methods were used in the comparison";
+ ? "- no equals methods were used in the comparison EXCEPT for java JDK types since introspecting JDK types is forbidden in java 17+ (use withEqualsForType to register a specific way to compare a JDK type if you need it)"
+ : "- equals methods were used in the comparison";
description.append(header);
if (isConfiguredToIgnoreSomeButNotAllOverriddenEqualsMethods()) {
description.append(format(" except for:%n"));
@@ -829,6 +887,11 @@ private void describeIntrospectionStrategy(StringBuilder description) {
description.append(format("- the introspection strategy used was: %s%n", introspectionStrategy.getDescription()));
}
+ private void describeCompareEnumAgainstString(StringBuilder description) {
+ if (compareEnumAgainstString)
+ description.append(format("- enums can be compared against strings (and vice versa), e.g. Color.RED and \"RED\" are considered equal%n"));
+ }
+
private boolean matchesAnIgnoredOverriddenEqualsRegex(FieldLocation fieldLocation) {
if (ignoredOverriddenEqualsForFieldsMatchingRegexes.isEmpty()) return false; // shortcut
String pathToUseInRules = fieldLocation.getPathToUseInRules();
@@ -840,7 +903,7 @@ private boolean matchesAnIgnoredOverriddenEqualsType(Class> clazz) {
}
private boolean matchesAnIgnoredOverriddenEqualsField(FieldLocation fieldLocation) {
- return ignoredOverriddenEqualsForFields.stream().anyMatch(fieldLocation::matches)
+ return ignoredOverriddenEqualsForFields.stream().anyMatch(fieldLocation::exactlyMatches)
|| matchesAnIgnoredOverriddenEqualsRegex(fieldLocation);
}
@@ -865,21 +928,18 @@ private boolean matchesAnIgnoredFieldType(DualValue dualValue) {
return false;
}
- private boolean shouldBeComparedBasedOnFieldValue(DualValue dualValue) {
- // first check if the value has a parent of a type we need to compare, ex: we compare Person types and the value is
- // corresponds to one of the Person fields. If this is not the case, we check actual type against the types
- // to compare, we use expected type in case actual was null assuming expected has the same type as actual
- if (fieldLocationOfFieldsOfTypesToCompare.stream().anyMatch(dualValue.fieldLocation::hasParent)
- || (dualValue.actual != null && comparedTypes.contains(dualValue.actual.getClass()))
+ private void registerFieldLocationOfFieldsOfTypesToCompare(DualValue dualValue) {
+ if (comparedTypes.isEmpty()) return;
+ // We check actual type against the types to compare or expected type in case actual was null assuming expected
+ // has the same type as actual
+ if ((dualValue.actual != null && comparedTypes.contains(dualValue.actual.getClass()))
|| (dualValue.expected != null && comparedTypes.contains(dualValue.expected.getClass()))) {
- fieldLocationOfFieldsOfTypesToCompare.add(dualValue.fieldLocation);
- return true;
+ fieldLocationsToCompareBecauseOfTypesToCompare.add(dualValue.fieldLocation);
}
- return false;
}
private boolean matchesAnIgnoredCollectionOrderInField(FieldLocation fieldLocation) {
- return ignoredCollectionOrderInFields.stream().anyMatch(fieldLocation::matches);
+ return ignoredCollectionOrderInFields.stream().anyMatch(fieldLocation::exactlyMatches);
}
private boolean matchesAnIgnoredCollectionOrderInFieldRegex(FieldLocation fieldLocation) {
@@ -1012,6 +1072,67 @@ public static Builder builder() {
return new Builder();
}
+ void checkComparedFieldsExist(Object actual) {
+ Map unknownComparedFields = new TreeMap<>();
+ for (FieldLocation comparedField : comparedFields) {
+ checkComparedFieldExists(actual,
+ comparedField).ifPresent(entry -> unknownComparedFields.put(entry.getKey(), entry.getValue()));
+ }
+ if (!unknownComparedFields.isEmpty()) {
+ StringBuilder errorMessageBuilder = new StringBuilder("The following fields don't exist: ");
+ unknownComparedFields.forEach((fieldLocation,
+ nodeName) -> errorMessageBuilder.append(formatUnknownComparedField(fieldLocation,
+ nodeName)));
+ throw new IllegalArgumentException(errorMessageBuilder.toString());
+ }
+ }
+
+ private Optional> checkComparedFieldExists(Object actual, FieldLocation comparedFieldLocation) {
+ Object node = actual;
+ for (int nestingLevel = 0; nestingLevel < comparedFieldLocation.getDecomposedPath().size(); nestingLevel++) {
+ if (node == null) {
+ // won't be able to get children nodes, assume the field is known as we can't check it
+ return Optional.empty();
+ }
+ String comparedFieldNodeNameElement = comparedFieldLocation.getDecomposedPath().get(nestingLevel);
+ Set nodeNames = introspectionStrategy.getChildrenNodeNamesOf(node);
+ if (!nodeNames.contains(comparedFieldNodeNameElement)) {
+ return Optional.of(entry(comparedFieldLocation, comparedFieldNodeNameElement));
+ }
+ node = introspectionStrategy.getChildNodeValue(comparedFieldNodeNameElement, node);
+ }
+ return Optional.empty();
+ }
+
+ private static String formatUnknownComparedField(FieldLocation fieldLocation, String unknownNodeNameElement) {
+ return fieldLocation.isTopLevelField()
+ ? format("{%s}", unknownNodeNameElement)
+ : format("{%s in %s}", unknownNodeNameElement, fieldLocation);
+ }
+
+ boolean hierarchyMatchesAnyComparedTypes(DualValue dualValue) {
+ if (isFieldOfTypeToCompare(dualValue)) return true;
+ // dualValue is not a type to compare but could be a child of one
+ return fieldLocationsToCompareBecauseOfTypesToCompare.stream().anyMatch(dualValue.fieldLocation::hasParent);
+ }
+
+ boolean matchesOrIsChildOfFieldMatchingAnyComparedTypes(DualValue dualValue) {
+ return fieldLocationsToCompareBecauseOfTypesToCompare.stream().anyMatch(dualValue.fieldLocation::exactlyMatches);
+ }
+
+ boolean hasComparedTypes() {
+ return !comparedTypes.isEmpty();
+ }
+
+ private boolean isFieldOfTypeToCompare(DualValue dualValue) {
+ Object valueToCheck = dualValue.actual != null ? dualValue.actual : dualValue.expected;
+ return valueToCheck != null && comparedTypes.contains(valueToCheck.getClass());
+ }
+
+ boolean exactlyMatchesAnyComparedFields(DualValue dualValue) {
+ return comparedFields.stream().anyMatch(comparedField -> comparedField.exactlyMatches(dualValue.fieldLocation));
+ }
+
/**
* Builder to build {@link RecursiveComparisonConfiguration}.
*/
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonDifferenceCalculator.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonDifferenceCalculator.java
index 736788502f..17ae55dc04 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonDifferenceCalculator.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/RecursiveComparisonDifferenceCalculator.java
@@ -14,12 +14,14 @@
import static java.lang.String.format;
import static java.util.Objects.deepEquals;
+import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.StreamSupport.stream;
import static org.assertj.core.api.recursive.comparison.ComparisonDifference.rootComparisonDifference;
import static org.assertj.core.api.recursive.comparison.DualValue.DEFAULT_ORDERED_COLLECTION_TYPES;
import static org.assertj.core.api.recursive.comparison.FieldLocation.rootFieldLocation;
import static org.assertj.core.util.IterableUtil.sizeOf;
-import static org.assertj.core.util.IterableUtil.toCollection;
import static org.assertj.core.util.Lists.list;
import static org.assertj.core.util.Sets.newHashSet;
@@ -30,9 +32,9 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -49,7 +51,7 @@
/**
* Based on {@link DeepDifference} but takes a {@link RecursiveComparisonConfiguration}, {@link DeepDifference}
- * being itself based on the deep equals implementation of https://github.com/jdereg/java-util
+ * being itself based on the deep equals implementation of https://github.com/jdereg/java-util
*
* @author John DeRegnaucourt (john@cedarsoftware.com)
* @author Pascal Schumacher
@@ -57,6 +59,7 @@
public class RecursiveComparisonDifferenceCalculator {
private static final String DIFFERENT_ACTUAL_AND_EXPECTED_FIELD_TYPES = "expected field is %s but actual field is not (%s)";
+ private static final String ACTUAL_IS_AN_ENUM_WHILE_EXPECTED_IS_NOT = "expected field is a %s but actual field is an enum";
private static final String ACTUAL_NOT_ORDERED_COLLECTION = "expected field is an ordered collection but actual field is not (%s), ordered collections are: "
+ describeOrderedCollectionTypes();
@@ -69,23 +72,44 @@ public class RecursiveComparisonDifferenceCalculator {
private static class ComparisonState {
// Not using a Set as we want to precisely track visited values, a set would remove duplicates
- List visitedDualValues;
+ VisitedDualValues visitedDualValues;
List differences = new ArrayList<>();
DualValueDeque dualValuesToCompare;
RecursiveComparisonConfiguration recursiveComparisonConfiguration;
- public ComparisonState(List visited, RecursiveComparisonConfiguration recursiveComparisonConfiguration) {
- this.visitedDualValues = visited;
+ public ComparisonState(VisitedDualValues visitedDualValues,
+ RecursiveComparisonConfiguration recursiveComparisonConfiguration) {
+ this.visitedDualValues = visitedDualValues;
this.dualValuesToCompare = new DualValueDeque(recursiveComparisonConfiguration);
this.recursiveComparisonConfiguration = recursiveComparisonConfiguration;
}
void addDifference(DualValue dualValue) {
- differences.add(new ComparisonDifference(dualValue, null, getCustomErrorMessage(dualValue)));
+ addDifference(dualValue, null);
}
void addDifference(DualValue dualValue, String description) {
- differences.add(new ComparisonDifference(dualValue, description, getCustomErrorMessage(dualValue)));
+ // to evaluate differences on fields of compared types, we have to traverse the whole graph of objects to compare
+ // and decide afterward if differences were relevant, for example if we compare only the Employee type, and we
+ // come across a Company having a list of Employee, we should evaluate the Company but ignore any of its
+ // differences unless the ones on Employees.
+ if (recursiveComparisonConfiguration.hasComparedTypes()) {
+ // the comparison includes the union of fields of compared types and compared fields, if the difference is
+ // reported on a field whose type is not in the compared types, we should ignore the difference unless it was
+ // on a field from the set of compared fields.
+ if (!recursiveComparisonConfiguration.exactlyMatchesAnyComparedFields(dualValue)
+ && !recursiveComparisonConfiguration.matchesOrIsChildOfFieldMatchingAnyComparedTypes(dualValue))
+ // was not a field we had to compared
+ return;
+ // check if the value was meant to be ignored, if it is the case simply skip the difference
+ if (recursiveComparisonConfiguration.shouldIgnore(dualValue)) return;
+ }
+
+ String customErrorMessage = getCustomErrorMessage(dualValue);
+ ComparisonDifference comparisonDifference = new ComparisonDifference(dualValue, description, customErrorMessage);
+ differences.add(comparisonDifference);
+ // track the difference for the given dual values, in case we visit the same dual values again
+ visitedDualValues.registerComparisonDifference(dualValue, comparisonDifference);
}
void addKeyDifference(DualValue parentDualValue, Object actualKey, Object expectedKey) {
@@ -102,26 +126,24 @@ public boolean hasDualValuesToCompare() {
}
public DualValue pickDualValueToCompare() {
- final DualValue dualValue = dualValuesToCompare.removeFirst();
- if (dualValue.hasPotentialCyclingValues()) {
- // visited dual values are here to avoid cycle, java types don't have cycle, there is no need to track them.
- // moreover this would make should_fix_1854_minimal_test to fail (see the test for a detailed explanation)
- visitedDualValues.add(dualValue);
- }
- return dualValue;
+ return dualValuesToCompare.removeFirst();
}
private void registerForComparison(DualValue dualValue) {
- if (!visitedDualValues.contains(dualValue)) dualValuesToCompare.addFirst(dualValue);
+ dualValuesToCompare.addFirst(dualValue);
}
private void initDualValuesToCompare(Object actual, Object expected, FieldLocation nodeLocation) {
+ // before anything are these values to be compared at all?
DualValue dualValue = new DualValue(nodeLocation, actual, expected);
+ if (recursiveComparisonConfiguration.shouldNotEvaluate(dualValue)) return;
boolean mustCompareNodesRecursively = mustCompareNodesRecursively(dualValue);
if (dualValue.hasNoNullValues() && mustCompareNodesRecursively) {
// disregard the equals method and start comparing fields
- // TODO should fail if actual and expected don't have the same fields to compare (taking into account ignored/compared
- // fields)
+ if (recursiveComparisonConfiguration.someComparedFieldsHaveBeenSpecified()) {
+ recursiveComparisonConfiguration.checkComparedFieldsExist(actual);
+ }
+ // TODO should fail if actual and expected don't have the same fields (taking into account ignored/compared fields)
Set actualChildrenNodeNamesToCompare = recursiveComparisonConfiguration.getActualChildrenNodeNamesToCompare(dualValue);
if (!actualChildrenNodeNamesToCompare.isEmpty()) {
// fields to ignore are evaluated when adding their corresponding dualValues to dualValuesToCompare which filters
@@ -134,30 +156,20 @@ private void initDualValuesToCompare(Object actual, Object expected, FieldLocati
Object expectedChildNodeValue = recursiveComparisonConfiguration.getValue(actualChildNodeName, expected);
DualValue childNodeDualValue = new DualValue(nodeLocation.field(actualChildNodeName), actualChildNodeValue,
expectedChildNodeValue);
- dualValuesToCompare.addFirst(childNodeDualValue);
+ registerForComparison(childNodeDualValue);
}
} else {
- dualValuesToCompare.addFirst(dualValue);
+ registerForComparison(dualValue);
}
} else {
- dualValuesToCompare.addFirst(dualValue);
+ registerForComparison(dualValue);
}
} else {
- dualValuesToCompare.addFirst(dualValue);
+ registerForComparison(dualValue);
}
- // We need to remove already visited nodes pair to avoid infinite recursion in case parent -> set{child} with child having
- // a reference back to its parent but only for complex types can have cycle, this is not the case for primitive or enums.
- // It occurs for unordered collection where we compare all possible combination of the collection elements recursively.
- // --
- // remove visited values one by one, DualValue.equals correctly compare respective actual and expected fields by reference
- visitedDualValues.forEach(visitedDualValue -> dualValuesToCompare.stream()
- .filter(dualValueToCompare -> dualValueToCompare.equals(visitedDualValue))
- .findFirst()
- .ifPresent(dualValuesToCompare::remove));
}
private boolean mustCompareNodesRecursively(DualValue dualValue) {
-
return !recursiveComparisonConfiguration.hasCustomComparator(dualValue)
&& !shouldHonorEquals(dualValue, recursiveComparisonConfiguration)
&& dualValue.hasNoContainerValues();
@@ -169,10 +181,18 @@ private String getCustomErrorMessage(DualValue dualValue) {
if (recursiveComparisonConfiguration.hasCustomMessageForField(fieldName)) {
return recursiveComparisonConfiguration.getMessageForField(fieldName);
}
- Class> fieldType = dualValue.actual != null ? dualValue.actual.getClass() : dualValue.expected.getClass();
- if (recursiveComparisonConfiguration.hasCustomMessageForType(fieldType)) {
+
+ Class> fieldType = null;
+ if (dualValue.actual != null) {
+ fieldType = dualValue.actual.getClass();
+ } else if (dualValue.expected != null) {
+ fieldType = dualValue.expected.getClass();
+ }
+
+ if (fieldType != null && recursiveComparisonConfiguration.hasCustomMessageForType(fieldType)) {
return recursiveComparisonConfiguration.getMessageForType(fieldType);
}
+
return null;
}
}
@@ -203,20 +223,50 @@ public List determineDifferences(Object actual, Object exp
if (recursiveComparisonConfiguration.isInStrictTypeCheckingMode() && expectedTypeIsNotSubtypeOfActualType(actual, expected)) {
return list(expectedAndActualTypeDifference(actual, expected));
}
- List visited = list();
- return determineDifferences(actual, expected, rootFieldLocation(), visited, recursiveComparisonConfiguration);
+ return determineDifferences(actual, expected, rootFieldLocation(), new VisitedDualValues(), recursiveComparisonConfiguration);
}
// TODO keep track of ignored fields in an RecursiveComparisonExecution class ?
private static List determineDifferences(Object actual, Object expected, FieldLocation fieldLocation,
- List visited,
+ VisitedDualValues visitedDualValues,
RecursiveComparisonConfiguration recursiveComparisonConfiguration) {
- ComparisonState comparisonState = new ComparisonState(visited, recursiveComparisonConfiguration);
+ ComparisonState comparisonState = new ComparisonState(visitedDualValues, recursiveComparisonConfiguration);
comparisonState.initDualValuesToCompare(actual, expected, fieldLocation);
while (comparisonState.hasDualValuesToCompare()) {
+
final DualValue dualValue = comparisonState.pickDualValueToCompare();
+ if (recursiveComparisonConfiguration.hierarchyMatchesAnyComparedTypes(dualValue)) {
+ // keep track of field locations of type to compare, needed to compare child nodes, for example if we want to
+ // only compare the Person type, we must compare the Person fields too even though they are not of type Person
+ recursiveComparisonConfiguration.registerFieldLocationToCompareBecauseOfTypesToCompare(dualValue.fieldLocation);
+ }
+
+ // if we have already visited the dual value, no need to compute the comparison differences again, this also avoid cycles
+ Optional> comparisonDifferences = comparisonState.visitedDualValues.registeredComparisonDifferencesOf(dualValue);
+ if (comparisonDifferences.isPresent()) {
+ if (!comparisonDifferences.get().isEmpty()) {
+ comparisonState.addDifference(dualValue, "already visited node but now location is: " + dualValue.fieldLocation);
+ }
+ continue;
+ }
+
+ // first time we evaluate this dual value, perform the usual recursive comparison from there
+
+ // visited dual values are tracked to avoid cycle
+ if (recursiveComparisonConfiguration.someComparedFieldsHaveBeenSpecified()) {
+ // only track dual values if their field location is a compared field or a child of one that could have cycles,
+ // before we get to a compared field, tracking dual values is wrong, ex: given a person root object with a
+ // neighbour.neighbour field that cycles back to itself, and we compare neighbour.neighbour.name, if we track
+ // visited all dual values, we would not introspect neighbour.neighbour as it was already visited as root.
+ if (recursiveComparisonConfiguration.isOrIsChildOfAnyComparedFields(dualValue.fieldLocation)
+ && dualValue.hasPotentialCyclingValues()) {
+ comparisonState.visitedDualValues.registerVisitedDualValue(dualValue);
+ }
+ } else if (dualValue.hasPotentialCyclingValues()) {
+ comparisonState.visitedDualValues.registerVisitedDualValue(dualValue);
+ }
final Object actualFieldValue = dualValue.actual;
final Object expectedFieldValue = dualValue.expected;
@@ -240,6 +290,10 @@ private static List determineDifferences(Object actual, Ob
compareAsEnums(dualValue, comparisonState, recursiveComparisonConfiguration);
continue;
}
+ if (dualValue.isActualAnEnum()) {
+ compareAsEnums(dualValue, comparisonState, recursiveComparisonConfiguration);
+ continue;
+ }
// TODO move hasFieldTypesDifference check into each compareXXX
if (dualValue.isExpectedFieldAnArray()) {
@@ -323,6 +377,8 @@ private static List determineDifferences(Object actual, Ob
Set actualChildrenNodeNamesToCompare = recursiveComparisonConfiguration.getActualChildrenNodeNamesToCompare(dualValue);
Set expectedChildrenNodesNames = recursiveComparisonConfiguration.getChildrenNodeNamesOf(expectedFieldValue);
+ // Check if expected has more children nodes than actual, in that case the additional nodes are reported as difference
+
// Check if expected has more children nodes than actual, in that case the additional nodes are reported as difference
if (!expectedChildrenNodesNames.containsAll(actualChildrenNodeNamesToCompare)) {
// report missing nodes in actual
@@ -352,25 +408,60 @@ private static List determineDifferences(Object actual, Ob
return comparisonState.getDifferences();
}
+ private static boolean isChildOfSpecifiedComparatorField(DualValue dualValue, FieldLocation field) {
+
+ return field.getPathToUseInRules()
+ // Check if the comparator field is relevant by checking if the path validated so far matches
+ // &&
+ // Check if it has a child field still waiting to be validated.
+ .startsWith(dualValue.fieldLocation.getPathToUseInRules())
+ && field.getDecomposedPath().size() > dualValue.fieldLocation.getDecomposedPath().size();
+ }
+
+ private static String getChildFieldForValidation(FieldLocation field, FieldLocation fieldValue) {
+ return field.getDecomposedPath().get(fieldValue.getDecomposedPath().size());
+ }
+
// avoid comparing enum recursively since they contain static fields which are ignored in recursive comparison
// this would make different field enum value to be considered the same!
- private static void compareAsEnums(final DualValue dualValue,
- ComparisonState comparisonState,
+ private static void compareAsEnums(final DualValue dualValue, ComparisonState comparisonState,
RecursiveComparisonConfiguration recursiveComparisonConfiguration) {
if (recursiveComparisonConfiguration.isInStrictTypeCheckingMode()) {
- // we can use == for comparison which checks both actual and expected values and types are the same
+ // use == to check that both actual and expected values and types are the same
if (dualValue.actual != dualValue.expected) comparisonState.addDifference(dualValue);
return;
}
- if (!dualValue.isActualAnEnum()) {
- comparisonState.addDifference(dualValue, differentTypeErrorMessage(dualValue, "an enum"));
+ if (dualValue.isActualAnEnum() && dualValue.isExpectedAnEnum()) {
+ Enum> expectedEnum = (Enum>) dualValue.expected;
+ Enum> actualEnum = (Enum>) dualValue.actual;
+ // we must only compare actual and expected enum by value but not by type
+ if (!actualEnum.name().equals(expectedEnum.name())) comparisonState.addDifference(dualValue);
+ return;
+ }
+ if (!recursiveComparisonConfiguration.isComparingEnumAgainstStringAllowed()) {
+ // either actual or expected is not an enum, not ok as we haven't allowed comparing enums to strings fields
+ enumComparedToDifferentTypeError(dualValue, comparisonState);
+ return;
+ }
+ if (dualValue.isExpectedAnEnum() && dualValue.actual instanceof String) {
+ Enum> expectedEnum = (Enum>) dualValue.expected;
+ if (!expectedEnum.name().equals(dualValue.actual.toString())) comparisonState.addDifference(dualValue);
+ return;
+ }
+ if (dualValue.isActualAnEnum() && dualValue.expected instanceof String) {
+ Enum> actualEnum = (Enum>) dualValue.actual;
+ if (!actualEnum.name().equals(dualValue.expected.toString())) comparisonState.addDifference(dualValue);
return;
}
- // both actual and expected are enums
- Enum> actualEnum = (Enum>) dualValue.actual;
- Enum> expectedEnum = (Enum>) dualValue.expected;
- // we must only compare actual and expected enum by value but not by type
- if (!actualEnum.name().equals(expectedEnum.name())) comparisonState.addDifference(dualValue);
+ // either actual or expected is not an enum and the other type is not a string so invalid type
+ enumComparedToDifferentTypeError(dualValue, comparisonState);
+ }
+
+ private static void enumComparedToDifferentTypeError(DualValue dualValue, ComparisonState comparisonState) {
+ String typeErrorMessage = dualValue.isExpectedAnEnum()
+ ? differentTypeErrorMessage(dualValue, "an enum")
+ : format(ACTUAL_IS_AN_ENUM_WHILE_EXPECTED_IS_NOT, dualValue.expected.getClass().getCanonicalName());
+ comparisonState.addDifference(dualValue, typeErrorMessage);
}
private static boolean shouldHonorEquals(DualValue dualValue,
@@ -432,12 +523,12 @@ private static void compareOrderedCollections(DualValue dualValue, ComparisonSta
// no need to inspect elements, arrays are not equal as they don't have the same size
return;
}
- // register pair of elements with same index for later comparison as we compare elements in order
+ // register a pair of elements with same index for later comparison as we compare elements in order
Iterator> expectedIterator = expectedCollection.iterator();
int i = 0;
for (Object element : actualCollection) {
- FieldLocation elementFielLocation = dualValue.fieldLocation.field(format("[%d]", i));
- DualValue elementDualValue = new DualValue(elementFielLocation, element, expectedIterator.next());
+ FieldLocation elementFieldLocation = dualValue.fieldLocation.field(format("[%d]", i));
+ DualValue elementDualValue = new DualValue(elementFieldLocation, element, expectedIterator.next());
comparisonState.registerForComparison(elementDualValue);
i++;
}
@@ -463,29 +554,31 @@ private static void compareUnorderedIterables(DualValue dualValue, ComparisonSta
// no need to inspect elements, iterables are not equal as they don't have the same size
return;
}
- // copy actual as we will remove elements found in expected
- Collection> actualCopy = new LinkedList<>(toCollection(actual));
+ Map> actualByHashCode = stream(actual.spliterator(), false).collect(groupingBy(Objects::hashCode,
+ toList()));
List expectedElementsNotFound = list();
for (Object expectedElement : expected) {
boolean expectedElementMatched = false;
- // compare recursively expectedElement to all remaining actual elements
- Iterator> actualIterator = actualCopy.iterator();
- while (actualIterator.hasNext()) {
- Object actualElement = actualIterator.next();
- // we need to get the currently visited dual values otherwise a cycle would cause an infinite recursion.
- List differences = determineDifferences(actualElement, expectedElement, dualValue.fieldLocation,
- comparisonState.visitedDualValues,
- comparisonState.recursiveComparisonConfiguration);
- if (differences.isEmpty()) {
- // found an element in actual matching expectedElement, remove it as it can't be used to match other expected elements
- actualIterator.remove();
- expectedElementMatched = true;
- // jump to next actual element check
- break;
- }
+ // speed up comparison by selecting actual elements matching expected hash code, note that the hash code might not be
+ // relevant if fields used to compute it are ignored in the recursive comparison, it's a good heuristic though to check
+ // the first actual elements that could match the expected one, worst case we compare all actual elements.
+ Integer expectedHash = Objects.hashCode(expectedElement);
+ List> actualHashBucket = actualByHashCode.get(expectedHash);
+ if (actualHashBucket != null) {
+ Iterator> actualIterator = actualHashBucket.iterator();
+ expectedElementMatched = searchIterableForElement(actualIterator, expectedElement, dualValue, comparisonState);
}
+ // It may be that expectedElement matches an actual element in a different hash bucket, to account for this, we check the
+ // other actual elements for matches. This may result in O(n^2) complexity in the worst case.
if (!expectedElementMatched) {
- expectedElementsNotFound.add(expectedElement);
+ for (Map.Entry> entry : actualByHashCode.entrySet()) {
+ // avoid checking the same bucket twice
+ if (entry.getKey().equals(expectedHash)) continue;
+ Iterator> actualIterator = entry.getValue().iterator();
+ expectedElementMatched = searchIterableForElement(actualIterator, expectedElement, dualValue, comparisonState);
+ if (expectedElementMatched) break;
+ }
+ if (!expectedElementMatched) expectedElementsNotFound.add(expectedElement);
}
}
@@ -498,6 +591,24 @@ private static void compareUnorderedIterables(DualValue dualValue, ComparisonSta
}
}
+ private static boolean searchIterableForElement(Iterator> actualIterator, Object expectedElement,
+ DualValue dualValue, ComparisonState comparisonState) {
+ while (actualIterator.hasNext()) {
+ Object actualElement = actualIterator.next();
+ // we need to get the currently visited dual values otherwise a cycle would cause an infinite recursion.
+ List differences = determineDifferences(actualElement, expectedElement,
+ dualValue.fieldLocation,
+ comparisonState.visitedDualValues,
+ comparisonState.recursiveComparisonConfiguration);
+ if (differences.isEmpty()) {
+ // found an element in actual matching expectedElement, remove it as it can't be used to match other expected elements
+ actualIterator.remove();
+ return true;
+ }
+ }
+ return false;
+ }
+
// TODO replace by ordered map
private static void compareSortedMap(DualValue dualValue, ComparisonState comparisonState) {
if (!dualValue.isActualFieldASortedMap()) {
@@ -767,11 +878,11 @@ private static boolean areEqualUsingComparator(final Object actual, final Object
// this occurs when comparing field of different types, Person.id is an int and PersonDto.id is a long
// TODO maybe we should let the exception bubble up?
// assertion will fail with the current behavior and report other diff so it might be better to keep things this way
- System.out.println(format("WARNING: Comparator was not suited to compare '%s' field values:%n" +
- "- actual field value : %s%n" +
- "- expected field value: %s%n" +
- "- comparator used : %s",
- fieldName, actual, expected, comparator));
+ System.out.printf("WARNING: Comparator was not suited to compare '%s' field values:%n" +
+ "- actual field value : %s%n" +
+ "- expected field value: %s%n" +
+ "- comparator used : %s%n",
+ fieldName, actual, expected, comparator);
return false;
}
}
diff --git a/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/VisitedDualValues.java b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/VisitedDualValues.java
new file mode 100644
index 0000000000..85d1670ec1
--- /dev/null
+++ b/assertj-core/src/main/java/org/assertj/core/api/recursive/comparison/VisitedDualValues.java
@@ -0,0 +1,63 @@
+/*
+ * 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-2023 the original author or authors.
+ */
+package org.assertj.core.api.recursive.comparison;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static java.lang.String.format;
+
+class VisitedDualValues {
+
+ private List dualValues;
+
+ VisitedDualValues() {
+ this.dualValues = new ArrayList<>();
+ }
+
+ void registerVisitedDualValue(DualValue dualValue) {
+ this.dualValues.add(new VisitedDualValue(dualValue));
+ }
+
+ void registerComparisonDifference(DualValue dualValue, ComparisonDifference comparisonDifference) {
+ this.dualValues.stream()
+ // register difference on dual values agnostic of location, to take care of values visited several times
+ .filter(visitedDualValue -> visitedDualValue.dualValue.sameValues(dualValue))
+ .findFirst()
+ .ifPresent(visitedDualValue -> visitedDualValue.comparisonDifferences.add(comparisonDifference));
+ }
+
+ Optional> registeredComparisonDifferencesOf(DualValue dualValue) {
+ return this.dualValues.stream()
+ // use sameValues to get already visited dual values with different location
+ .filter(visitedDualValue -> visitedDualValue.dualValue.sameValues(dualValue))
+ .findFirst()
+ .map(visitedDualValue -> visitedDualValue.comparisonDifferences);
+ }
+
+ private static class VisitedDualValue {
+ DualValue dualValue;
+ List comparisonDifferences;
+
+ VisitedDualValue(DualValue dualValue) {
+ this.dualValue = dualValue;
+ this.comparisonDifferences = new ArrayList<>();
+ }
+
+ @Override
+ public String toString() {
+ return format("VisitedDualValue[dualValue=%s, comparisonDifferences=%s]", this.dualValue, this.comparisonDifferences);
+ }
+ }
+}
diff --git a/assertj-core/src/main/java/org/assertj/core/condition/Join.java b/assertj-core/src/main/java/org/assertj/core/condition/Join.java
index 8c89720aef..6650a5ac1f 100644
--- a/assertj-core/src/main/java/org/assertj/core/condition/Join.java
+++ b/assertj-core/src/main/java/org/assertj/core/condition/Join.java
@@ -79,15 +79,14 @@ private static T checkNotNullConditions(T conditions) {
public abstract String descriptionPrefix();
/**
- * method used to calculate the the subclass join description
+ * method used to calculate the subclass join description
*/
private void calculateDescription() {
List conditionsDescriptions = conditions.stream()
.map(Condition::description)
.collect(toList());
String prefix = descriptionPrefix() + PREFIX_DELIMITER;
- String suffix = SUFFIX_DELIMITER;
- describedAs(new JoinDescription(prefix, suffix, conditionsDescriptions));
+ describedAs(new JoinDescription(prefix, SUFFIX_DELIMITER, conditionsDescriptions));
}
@Override
@@ -102,8 +101,7 @@ public Description conditionDescriptionWithStatus(T actual) {
.map(condition -> condition.conditionDescriptionWithStatus(actual))
.collect(toList());
String prefix = status(actual).label + " " + descriptionPrefix() + PREFIX_DELIMITER;
- String suffix = SUFFIX_DELIMITER;
- return new JoinDescription(prefix, suffix, descriptionsWithStatus);
+ return new JoinDescription(prefix, SUFFIX_DELIMITER, descriptionsWithStatus);
}
private static T notNull(T condition) {
diff --git a/assertj-core/src/main/java/org/assertj/core/condition/NestableCondition.java b/assertj-core/src/main/java/org/assertj/core/condition/NestableCondition.java
index 0ed395462c..846d983af3 100644
--- a/assertj-core/src/main/java/org/assertj/core/condition/NestableCondition.java
+++ b/assertj-core/src/main/java/org/assertj/core/condition/NestableCondition.java
@@ -125,8 +125,9 @@ public class NestableCondition extends Join {
* @param the type of object nested into {@literal K}
*/
@SafeVarargs
- public static Condition nestable(String descriptionPrefix, Function extractor,
- Condition... conditions) {
+ public static Condition nestable(String descriptionPrefix,
+ Function super ACTUAL, ? extends NESTED> extractor,
+ Condition super NESTED>... conditions) {
return new NestableCondition<>(descriptionPrefix, stream(conditions), extractor);
}
@@ -138,16 +139,17 @@ public static Condition nestable(String descriptionPref
* @param the type of object the resulting condition accepts
*/
@SafeVarargs
- public static Condition nestable(String descriptionPrefix, Condition... conditions) {
- return new NestableCondition<>(descriptionPrefix, stream(conditions));
+ public static Condition nestable(String descriptionPrefix, Condition super ACTUAL>... conditions) {
+ return new NestableCondition(descriptionPrefix, stream(conditions));
}
- private NestableCondition(String descriptionPrefix, Stream> conditions, Function extractor) {
+ private NestableCondition(String descriptionPrefix, Stream extends Condition super NESTED>> conditions,
+ Function super ACTUAL, ? extends NESTED> extractor) {
super(compose(conditions, extractor));
this.descriptionPrefix = descriptionPrefix;
}
- private NestableCondition(String descriptionPrefix, Stream> conditions) {
+ private NestableCondition(String descriptionPrefix, Stream extends Condition super ACTUAL>> conditions) {
super(conditions.collect(toList()));
this.descriptionPrefix = descriptionPrefix;
}
@@ -162,13 +164,14 @@ public String descriptionPrefix() {
return descriptionPrefix;
}
- private static List> compose(Stream> conditions,
- Function extractor) {
+ private static List