From b0ef8cb5358bd44ef7bf1cb4ee6c808016dfe377 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 6 Sep 2020 11:24:54 +1200 Subject: [PATCH 001/191] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 45b7a31ec9e..c78685763f9 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 assertj-core - 3.17.2 + 3.17.3-SNAPSHOT jar AssertJ fluent assertions Rich and fluent assertions for testing for Java @@ -24,7 +24,7 @@ scm:git:git@github.com:joel-costigliola/assertj-core.git scm:git:git@github.com:joel-costigliola/assertj-core.git git@github.com:joel-costigliola/assertj-core - assertj-core-3.17.2 + HEAD github From 24bf22ac75779e1083bb68947420dc05b256fe66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 13:10:20 +0200 Subject: [PATCH 002/191] Bump commons-io from 2.7 to 2.8.0 (#1988) Bumps commons-io from 2.7 to 2.8.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c78685763f9..cfa8c27854f 100644 --- a/pom.xml +++ b/pom.xml @@ -195,7 +195,7 @@ commons-io commons-io - 2.7 + 2.8.0 test From c4497bca8284f071b29ff99273d8c89d5a200968 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 13 Sep 2020 16:46:18 +1200 Subject: [PATCH 003/191] Improve isTrue/isFalse assertions error messages --- .../core/api/AbstractBooleanAssert.java | 28 +++++--- .../org/assertj/core/error/ShouldBeFalse.java | 24 +++++++ .../org/assertj/core/error/ShouldBeTrue.java | 24 +++++++ .../AutoCloseableBDDSoftAssertionsTest.java | 7 +- .../api/AutoCloseableSoftAssertionsTest.java | 27 ++++---- .../core/api/BDDSoftAssertionsTest.java | 4 +- .../assertj/core/api/SoftAssertionsTest.java | 4 +- .../boolean_/BooleanAssert_isFalse_Test.java | 66 ++++++++++++++----- .../boolean_/BooleanAssert_isTrue_Test.java | 66 ++++++++++++++----- .../core/error/ShouldBeFalse_create_Test.java | 36 ++++++++++ .../core/error/ShouldBeTrue_create_Test.java | 36 ++++++++++ ...Maps_assertAnySatisfyingConsumer_Test.java | 16 +---- 12 files changed, 266 insertions(+), 72 deletions(-) create mode 100644 src/main/java/org/assertj/core/error/ShouldBeFalse.java create mode 100644 src/main/java/org/assertj/core/error/ShouldBeTrue.java create mode 100644 src/test/java/org/assertj/core/error/ShouldBeFalse_create_Test.java create mode 100644 src/test/java/org/assertj/core/error/ShouldBeTrue_create_Test.java diff --git a/src/main/java/org/assertj/core/api/AbstractBooleanAssert.java b/src/main/java/org/assertj/core/api/AbstractBooleanAssert.java index 79a623e8fde..673e1d961bc 100644 --- a/src/main/java/org/assertj/core/api/AbstractBooleanAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractBooleanAssert.java @@ -12,18 +12,22 @@ */ package org.assertj.core.api; +import static org.assertj.core.error.ShouldBeFalse.shouldBeFalse; +import static org.assertj.core.error.ShouldBeTrue.shouldBeTrue; + import java.util.Comparator; import org.assertj.core.internal.Booleans; +import org.assertj.core.internal.Failures; import org.assertj.core.util.VisibleForTesting; /** * Base class for all implementations of assertions for {@link Boolean}s. - * + * * @param the "self" type of this assertion class. Please read "Emulating 'self types' using Java Generics to simplify fluent API implementation" * for more details. - * + * * @author Alex Ruiz * @author Yvonne Wang * @author David DIDIER @@ -50,13 +54,15 @@ public AbstractBooleanAssert(Boolean actual, Class selfType) { * // assertions will fail * assertThat(false).isTrue(); * assertThat(Boolean.FALSE).isTrue(); - * + * * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if the actual value is not {@code true}. */ public SELF isTrue() { - return isEqualTo(true); + objects.assertNotNull(info, actual); + if (actual) return myself; + throw Failures.instance().failure(info, shouldBeTrue(actual), actual, true); } /** @@ -70,13 +76,15 @@ public SELF isTrue() { * // assertions will fail * assertThat(true).isFalse(); * assertThat(Boolean.TRUE).isFalse(); - * + * * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. * @throws AssertionError if the actual value is not {@code false}. */ public SELF isFalse() { - return isEqualTo(false); + objects.assertNotNull(info, actual); + if (actual == false) return myself; + throw Failures.instance().failure(info, shouldBeFalse(actual), actual, false); } /** @@ -86,11 +94,11 @@ public SELF isFalse() { *
 // assertions will pass
    * assertThat(true).isEqualTo(true);
    * assertThat(Boolean.FALSE).isEqualTo(false);
-   * 
+   *
    * // assertions will fail
    * assertThat(true).isEqualTo(false);
    * assertThat(Boolean.TRUE).isEqualTo(false);
- * + * * @param expected the given value to compare the actual value to. * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. @@ -112,7 +120,7 @@ public SELF isEqualTo(boolean expected) { * // assertions will fail * assertThat(true).isNotEqualTo(true); * assertThat(Boolean.FALSE).isNotEqualTo(false); - * + * * @param other the given value to compare the actual value to. * @return {@code this} assertion object. * @throws AssertionError if the actual value is {@code null}. @@ -137,7 +145,7 @@ public final SELF usingComparator(Comparator customComparator) /** * Do not use this method. - * + * * @deprecated Custom Comparator is not supported for Boolean comparison. * @throws UnsupportedOperationException if this method is called. */ diff --git a/src/main/java/org/assertj/core/error/ShouldBeFalse.java b/src/main/java/org/assertj/core/error/ShouldBeFalse.java new file mode 100644 index 00000000000..b062ae331bb --- /dev/null +++ b/src/main/java/org/assertj/core/error/ShouldBeFalse.java @@ -0,0 +1,24 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.error; + +public class ShouldBeFalse extends BasicErrorMessageFactory { + + public static ErrorMessageFactory shouldBeFalse(boolean actual) { + return new ShouldBeFalse(actual); + } + + private ShouldBeFalse(Object actual) { + super("%nExpecting value to be false but was %s", actual); + } +} diff --git a/src/main/java/org/assertj/core/error/ShouldBeTrue.java b/src/main/java/org/assertj/core/error/ShouldBeTrue.java new file mode 100644 index 00000000000..18385ba6015 --- /dev/null +++ b/src/main/java/org/assertj/core/error/ShouldBeTrue.java @@ -0,0 +1,24 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.error; + +public class ShouldBeTrue extends BasicErrorMessageFactory { + + public static ErrorMessageFactory shouldBeTrue(boolean actual) { + return new ShouldBeTrue(actual); + } + + private ShouldBeTrue(Object actual) { + super("%nExpecting value to be true but was %s", actual); + } +} diff --git a/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java b/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java index fbbcb36897a..510ab0be6d1 100644 --- a/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java +++ b/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java @@ -149,8 +149,8 @@ public String toString() { assertThat(errors.get(0)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); - assertThat(errors.get(1)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); - assertThat(errors.get(2)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); + assertThat(errors.get(1)).contains(format("%nExpecting value to be true but was false")); + assertThat(errors.get(2)).contains(format("%nExpecting value to be true but was false")); assertThat(errors.get(3)).contains(format("%nExpecting:%n <[false]>%nto be equal to:%n <[true]>%nbut was not.")); assertThat(errors.get(4)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); @@ -220,7 +220,8 @@ public String toString() { assertThat(errors.get(43)).contains(format("%nExpecting:%n <12:00Z>%nto be equal to:%n <13:00Z>%nbut was not.")); assertThat(errors.get(44)).contains(format("%nExpecting:%n <-999999999-01-01T00:00+18:00 (java.time.OffsetDateTime)>%nto be equal to:%n <+999999999-12-31T23:59:59.999999999-18:00 (java.time.OffsetDateTime)>%n" + - "when comparing values using '%s'%nbut was not.", OffsetDateTimeByInstantComparator.getInstance())); + "when comparing values using '%s'%nbut was not.", + OffsetDateTimeByInstantComparator.getInstance())); return; } fail("Should not reach here"); diff --git a/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java b/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java index 701fe6b2431..c223a224f4d 100644 --- a/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java +++ b/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java @@ -164,8 +164,8 @@ public String toString() { assertThat(errors.get(0)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); - assertThat(errors.get(1)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); - assertThat(errors.get(2)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); + assertThat(errors.get(1)).contains(format("%nExpecting value to be true but was false")); + assertThat(errors.get(2)).contains(format("%nExpecting value to be true but was false")); assertThat(errors.get(3)).contains(format("%nExpecting:%n <[false]>%nto be equal to:%n <[true]>%nbut was not.")); assertThat(errors.get(4)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); @@ -193,11 +193,11 @@ public String toString() { assertThat(errors.get(19)).contains(format("%nExpecting:%n <[16.0f]>%nto be equal to:%n <[17.0f]>%nbut was not.")); assertThat(errors.get(20)).contains(String.format("%nInputStreams do not have same content:%n%n" - + "Changed content at line 1:%n" - + "expecting:%n" - + " [\"B\"]%n" - + "but was:%n" - + " [\"A\"]%n")); + + "Changed content at line 1:%n" + + "expecting:%n" + + " [\"B\"]%n" + + "but was:%n" + + " [\"A\"]%n")); assertThat(errors.get(21)).contains(format("%nExpecting:%n <20>%nto be equal to:%n <21>%nbut was not.")); assertThat(errors.get(22)).contains(format("%nExpecting:%n <22>%nto be equal to:%n <23>%nbut was not.")); @@ -223,20 +223,22 @@ public String toString() { assertThat(errors.get(36)).contains(format("%nExpecting:%n <[52]>%nto be equal to:%n <[53]>%nbut was not.")); assertThat(errors.get(37)).contains(format("%nExpecting message to be:%n" + " <\"NullPointerException message\">%n" - + "but was:%n" + + "but was:%n" + " <\"IllegalArgumentException message\">")); assertThat(errors.get(38)).contains(format("%nExpecting message to be:%n" + " <\"something was good\">%n" - + "but was:%n" + + "but was:%n" + " <\"something was wrong\">")); assertThat(errors.get(39)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); assertThat(errors.get(40)).contains(format("%nExpecting:%n <2015-01-01 (java.time.LocalDate)>%nto be equal to:%n <2015-01-02 (java.time.LocalDate)>%nbut was not.")); assertThat(errors.get(41)).contains(format("%nExpecting:%n <2015-01-01T23:59:59 (java.time.LocalDateTime)>%nto be equal to:%n <2015-01-01T23:59 (java.time.LocalDateTime)>%n" + - "when comparing values using '%s'%nbut was not.", ChronoLocalDateTimeComparator.getInstance())); + "when comparing values using '%s'%nbut was not.", + ChronoLocalDateTimeComparator.getInstance())); assertThat(errors.get(42)).contains(format("%nExpecting:%n <2015-01-01T23:59:59Z (java.time.ZonedDateTime)>%nto be equal to:%n <2015-01-01T23:59Z (java.time.ZonedDateTime)>%n" + - "when comparing values using '%s'%nbut was not.", ChronoZonedDateTimeByInstantComparator.getInstance())); + "when comparing values using '%s'%nbut was not.", + ChronoZonedDateTimeByInstantComparator.getInstance())); assertThat(errors.get(43)).contains(format("%nExpecting:%n %nto be equal to:%n <1>%nbut was not.")); assertThat(errors.get(44)).contains(format("%nExpecting:%n %nto be equal to:%n <1.0>%nbut was not.")); @@ -247,7 +249,8 @@ public String toString() { assertThat(errors.get(48)).contains(format("%nExpecting:%n <-999999999-01-01T00:00+18:00 (java.time.OffsetDateTime)>%nto be equal to:%n <+999999999-12-31T23:59:59.999999999-18:00 (java.time.OffsetDateTime)>%n" + - "when comparing values using '%s'%nbut was not.", OffsetDateTimeByInstantComparator.getInstance())); + "when comparing values using '%s'%nbut was not.", + OffsetDateTimeByInstantComparator.getInstance())); return; } diff --git a/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java b/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java index 9537f6fdd28..1d5ca047f65 100644 --- a/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java +++ b/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java @@ -281,8 +281,8 @@ public String toString() { List errors = error.getFailures().stream().map(Object::toString).collect(toList()); assertThat(errors).hasSize(62); assertThat(errors.get(0)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); - assertThat(errors.get(1)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); - assertThat(errors.get(2)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); + assertThat(errors.get(1)).contains(format("%nExpecting value to be true but was false")); + assertThat(errors.get(2)).contains(format("%nExpecting value to be true but was false")); assertThat(errors.get(3)).contains(format("%nExpecting:%n <[false]>%nto be equal to:%n <[true]>%nbut was not.")); assertThat(errors.get(4)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); assertThat(errors.get(5)).contains(format("%nExpecting:%n <0x02>%nto be equal to:%n <0x03>%nbut was not.")); diff --git a/src/test/java/org/assertj/core/api/SoftAssertionsTest.java b/src/test/java/org/assertj/core/api/SoftAssertionsTest.java index 50d6dae6eb2..273698de7d0 100644 --- a/src/test/java/org/assertj/core/api/SoftAssertionsTest.java +++ b/src/test/java/org/assertj/core/api/SoftAssertionsTest.java @@ -298,8 +298,8 @@ public String toString() { List errors = e.getFailures().stream().map(Object::toString).collect(toList()); assertThat(errors).hasSize(55); assertThat(errors.get(0)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); - assertThat(errors.get(1)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); - assertThat(errors.get(2)).contains(format("%nExpecting:%n %nto be equal to:%n %nbut was not.")); + assertThat(errors.get(1)).contains(format("%nExpecting value to be true but was false")); + assertThat(errors.get(2)).contains(format("%nExpecting value to be true but was false")); assertThat(errors.get(3)).contains(format("%nExpecting:%n <[false]>%nto be equal to:%n <[true]>%nbut was not.")); assertThat(errors.get(4)).contains(format("%nExpecting:%n <0>%nto be equal to:%n <1>%nbut was not.")); diff --git a/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isFalse_Test.java b/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isFalse_Test.java index 6383689cdf1..96120d9869b 100644 --- a/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isFalse_Test.java +++ b/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isFalse_Test.java @@ -12,26 +12,62 @@ */ package org.assertj.core.api.boolean_; -import org.assertj.core.api.BooleanAssert; -import org.assertj.core.api.BooleanAssertBaseTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.description.EmptyTextDescription.emptyDescription; +import static org.assertj.core.error.ShouldBeFalse.shouldBeFalse; +import static org.assertj.core.error.ShouldNotBeNull.shouldNotBeNull; +import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; -import static org.mockito.Mockito.verify; +import org.junit.jupiter.api.Test; +class BooleanAssert_isFalse_Test { -/** - * Tests for {@link BooleanAssert#isFalse()}. - * - * @author Alex Ruiz - */ -class BooleanAssert_isFalse_Test extends BooleanAssertBaseTest { + @Test + void should_fail_if_actual_is_null() { + // GIVEN + Boolean actual = null; + // WHEN + AssertionError assertionError = expectAssertionError(() -> then(actual).isFalse()); + // THEN + then(assertionError).hasMessage(shouldNotBeNull().create()); + } + + @Test + void should_pass_if_primitive_boolean_is_false() { + // GIVEN + boolean actual = false; + // WHEN/THEN + then(actual).isFalse(); + } - @Override - protected BooleanAssert invoke_api_method() { - return assertions.isFalse(); + @Test + void should_pass_if_Boolean_is_false() { + // GIVEN + Boolean actual = false; + // WHEN/THEN + then(actual).isFalse(); } - @Override - protected void verify_internal_effects() { - verify(booleans).assertEqual(getInfo(assertions), getActual(assertions), false); + @Test + void should_fail_if_primitive_boolean_is_true() { + // GIVEN + boolean actual = true; + // WHEN + AssertionError assertionError = expectAssertionError(() -> assertThat(actual).isFalse()); + // THEN + then(assertionError).hasMessage(shouldBeFalse(actual).create(emptyDescription(), STANDARD_REPRESENTATION)); } + + @Test + void should_fail_if_Boolean_is_true() { + // GIVEN + Boolean actual = true; + // WHEN + AssertionError assertionError = expectAssertionError(() -> assertThat(actual).isFalse()); + // THEN + then(assertionError).hasMessage(shouldBeFalse(actual).create(emptyDescription(), STANDARD_REPRESENTATION)); + } + } diff --git a/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isTrue_Test.java b/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isTrue_Test.java index 102bbdc5411..6e1bb0a18c1 100644 --- a/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isTrue_Test.java +++ b/src/test/java/org/assertj/core/api/boolean_/BooleanAssert_isTrue_Test.java @@ -12,26 +12,62 @@ */ package org.assertj.core.api.boolean_; -import org.assertj.core.api.BooleanAssert; -import org.assertj.core.api.BooleanAssertBaseTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.description.EmptyTextDescription.emptyDescription; +import static org.assertj.core.error.ShouldBeTrue.shouldBeTrue; +import static org.assertj.core.error.ShouldNotBeNull.shouldNotBeNull; +import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; -import static org.mockito.Mockito.verify; +import org.junit.jupiter.api.Test; +class BooleanAssert_isTrue_Test { -/** - * Tests for {@link BooleanAssert#isTrue()}. - * - * @author Alex Ruiz - */ -class BooleanAssert_isTrue_Test extends BooleanAssertBaseTest { + @Test + void should_fail_if_actual_is_null() { + // GIVEN + Boolean actual = null; + // WHEN + AssertionError assertionError = expectAssertionError(() -> then(actual).isTrue()); + // THEN + then(assertionError).hasMessage(shouldNotBeNull().create()); + } + + @Test + void should_pass_if_primitive_boolean_is_true() { + // GIVEN + boolean actual = true; + // WHEN/THEN + then(actual).isTrue(); + } - @Override - protected BooleanAssert invoke_api_method() { - return assertions.isTrue(); + @Test + void should_pass_if_Boolean_is_true() { + // GIVEN + Boolean actual = true; + // WHEN/THEN + then(actual).isTrue(); } - @Override - protected void verify_internal_effects() { - verify(booleans).assertEqual(getInfo(assertions), getActual(assertions), true); + @Test + void should_fail_if_primitive_boolean_is_false() { + // GIVEN + boolean actual = false; + // WHEN + AssertionError assertionError = expectAssertionError(() -> assertThat(actual).isTrue()); + // THEN + then(assertionError).hasMessage(shouldBeTrue(actual).create(emptyDescription(), STANDARD_REPRESENTATION)); } + + @Test + void should_fail_if_Boolean_is_false() { + // GIVEN + Boolean actual = false; + // WHEN + AssertionError assertionError = expectAssertionError(() -> assertThat(actual).isTrue()); + // THEN + then(assertionError).hasMessage(shouldBeTrue(actual).create(emptyDescription(), STANDARD_REPRESENTATION)); + } + } diff --git a/src/test/java/org/assertj/core/error/ShouldBeFalse_create_Test.java b/src/test/java/org/assertj/core/error/ShouldBeFalse_create_Test.java new file mode 100644 index 00000000000..a9a734e498c --- /dev/null +++ b/src/test/java/org/assertj/core/error/ShouldBeFalse_create_Test.java @@ -0,0 +1,36 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.error; + +import static java.lang.String.format; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.error.ShouldBeFalse.shouldBeFalse; +import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION; + +import org.assertj.core.internal.TestDescription; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("ShouldBeFalse create") +class ShouldBeFalse_create_Test { + + @Test + void should_create_error_message() { + // GIVEN + ErrorMessageFactory errorMessageFactory = shouldBeFalse(false); + // WHEN + String message = errorMessageFactory.create(new TestDescription("Test"), STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[Test] %nExpecting value to be false but was false")); + } +} diff --git a/src/test/java/org/assertj/core/error/ShouldBeTrue_create_Test.java b/src/test/java/org/assertj/core/error/ShouldBeTrue_create_Test.java new file mode 100644 index 00000000000..85729f072ef --- /dev/null +++ b/src/test/java/org/assertj/core/error/ShouldBeTrue_create_Test.java @@ -0,0 +1,36 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.error; + +import static java.lang.String.format; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.error.ShouldBeTrue.shouldBeTrue; +import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION; + +import org.assertj.core.internal.TestDescription; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("ShouldBeTrue create") +class ShouldBeTrue_create_Test { + + @Test + void should_create_error_message() { + // GIVEN + ErrorMessageFactory errorMessageFactory = shouldBeTrue(false); + // WHEN + String message = errorMessageFactory.create(new TestDescription("Test"), STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[Test] %nExpecting value to be true but was false")); + } +} diff --git a/src/test/java/org/assertj/core/internal/maps/Maps_assertAnySatisfyingConsumer_Test.java b/src/test/java/org/assertj/core/internal/maps/Maps_assertAnySatisfyingConsumer_Test.java index 31f384460ab..f18e8d283d7 100644 --- a/src/test/java/org/assertj/core/internal/maps/Maps_assertAnySatisfyingConsumer_Test.java +++ b/src/test/java/org/assertj/core/internal/maps/Maps_assertAnySatisfyingConsumer_Test.java @@ -24,8 +24,8 @@ import static org.assertj.core.util.FailureMessages.actualIsNull; import static org.assertj.core.util.Lists.emptyList; import static org.assertj.core.util.Lists.list; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -100,19 +100,9 @@ void should_fail_if_no_entry_satisfies_the_given_requirements() { // THEN Iterator> actualEntries = actual.entrySet().iterator(); List errors = list(unsatisfiedRequirement(actualEntries.next(), - format("%n" + - "Expecting:%n" + - " %n" + - "to be equal to:%n" + - " %n" + - "but was not.")), + format("%nExpecting value to be false but was true")), unsatisfiedRequirement(actualEntries.next(), - format("%n" + - "Expecting:%n" + - " %n" + - "to be equal to:%n" + - " %n" + - "but was not."))); + format("%nExpecting value to be false but was true"))); assertThat(error).hasMessage(elementsShouldSatisfyAny(actual, errors, someInfo()).create()); } From 92a54ddd31f28c7dbd5e3aca764224a37f2a8617 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 13 Sep 2020 18:01:40 +1200 Subject: [PATCH 004/191] Bump version in verify.bndrun --- verify.bndrun | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/verify.bndrun b/verify.bndrun index 473c4a8a68e..753b7ec172e 100644 --- a/verify.bndrun +++ b/verify.bndrun @@ -30,8 +30,8 @@ # The version ranges will change as the version of # AssertJ and/or its dependencies change. -runbundles: \ - assertj-core;version='[3.17.2,3.17.3)',\ - assertj-core-tests;version='[3.17.2,3.17.3)',\ + assertj-core;version='[3.17.3,3.17.4)',\ + assertj-core-tests;version='[3.17.3,3.17.4)',\ junit-jupiter-api;version='[5.6.2,5.6.3)',\ junit-jupiter-engine;version='[5.6.2,5.6.3)',\ junit-platform-commons;version='[1.6.2,1.6.3)',\ From 378648a6ed95859274e36f21bf2472b5556e91e0 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Mon, 14 Sep 2020 22:51:53 +1200 Subject: [PATCH 005/191] Improve code coverage in WithAssertions. --- .../api/WithAssertions_delegation_Test.java | 163 +++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java b/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java index 6fba0a0d1df..41486d3c256 100644 --- a/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java +++ b/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java @@ -13,6 +13,7 @@ package org.assertj.core.api; import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import static org.assertj.core.util.Sets.newLinkedHashSet; import static org.mockito.Mockito.mock; @@ -39,6 +40,19 @@ import java.util.OptionalLong; import java.util.Set; import java.util.Spliterator; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.atomic.AtomicMarkableReference; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.concurrent.atomic.AtomicStampedReference; +import java.util.concurrent.atomic.LongAdder; import java.util.function.DoublePredicate; import java.util.function.Function; import java.util.function.IntPredicate; @@ -50,6 +64,8 @@ import org.assertj.core.util.Lists; import org.junit.jupiter.api.Test; +import com.google.common.util.concurrent.Futures; + /** * Tests for {@link WithAssertions}, to verify that delegate calls happen. * @@ -270,6 +286,16 @@ void withAssertions_assertThat_string_Test() { .isLessThanOrEqualTo("Hi World"); } + @Test + void withAssertions_assertThat_StringBuilder_Test() { + assertThat(new StringBuilder("foo")).startsWith("fo"); + } + + @Test + void withAssertions_assertThat_StringBuffer_Test() { + assertThat(new StringBuffer("foo")).startsWith("fo"); + } + /** * Test that the delegate method is called. */ @@ -477,6 +503,132 @@ void withAssertions_assertThat_float_array_Test() { assertThat(new float[5]).isNotEmpty(); } + @Test + void withAssertions_assertThat_float_2D_array_Test() { + assertThat(new float[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_double_2D_array_Test() { + assertThat(new double[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_char_2D_array_Test() { + assertThat(new char[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_byte_2D_array_Test() { + assertThat(new byte[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_long_2D_array_Test() { + assertThat(new long[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_int_2D_array_Test() { + assertThat(new int[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_short_2D_array_Test() { + assertThat(new short[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_boolean_2D_array_Test() { + assertThat(new boolean[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_object_2D_array_Test() { + assertThat(new String[0][0]).isEmpty(); + } + + @Test + void withAssertions_assertThat_AtomicBoolean_Test() { + assertThat(new AtomicBoolean(true)).isTrue(); + } + + @Test + void withAssertions_assertThat_AtomicInteger_Test() { + assertThat(new AtomicInteger(0)).hasValue(0); + } + + @Test + void withAssertions_assertThat_AtomicLong_Test() { + assertThat(new AtomicLong(0)).hasValue(0); + } + + @Test + void withAssertions_assertThat_AtomicLongArray_Test() { + assertThat(new AtomicLongArray(0)).isEmpty(); + } + + @Test + void withAssertions_assertThat_AtomicIntegerArray_Test() { + assertThat(new AtomicIntegerArray(0)).isEmpty(); + } + + @Test + void withAssertions_assertThat_AtomicIntegerFieldUpdater_Test() { + assertThat(AtomicIntegerFieldUpdater.newUpdater(Person.class, "age")).isNotNull(); + } + + @Test + void withAssertions_assertThat_AtomicLongFieldUpdater_Test() { + assertThat(AtomicLongFieldUpdater.newUpdater(Person.class, "number")).isNotNull(); + } + + @Test + void withAssertions_assertThat_AtomicReferenceFieldUpdater_Test() { + assertThat(AtomicReferenceFieldUpdater.newUpdater(Person.class, String.class, "name")).isNotNull(); + } + + static class Person { + volatile int age; + volatile long number; + volatile String name; + } + + @Test + void withAssertions_assertThat_AtomicReference_Test() { + assertThat(new AtomicReference<>("foo")).hasValue("foo"); + } + + @Test + void withAssertions_assertThat_AtomicReferenceArray_Test() { + assertThat(new AtomicReferenceArray<>(0)).isEmpty(); + } + + @Test + void withAssertions_assertThat_AtomicMarkableReference_Test() { + assertThat(new AtomicMarkableReference<>("foo", true)).hasReference("foo"); + } + + @Test + void withAssertions_assertThat_AtomicStampedReference_Test() { + assertThat(new AtomicStampedReference<>("foo", 0)).hasReference("foo"); + } + + @Test + void withAssertions_assertThat_LongAdder_Test() { + // GIVEN + LongAdder actual = new LongAdder(); + // WHEN + actual.add(5); + // THEN + assertThat(actual).hasValue(5); + } + + @Test + void withAssertions_assertThat_Future_Test() { + assertThat(Futures.immediateFuture("foo")).isDone(); + } + /** * Test that the delegate method is called. */ @@ -812,7 +964,6 @@ void withAssertions_from_function_Test() { } @Test - @SuppressWarnings("unchecked") void withAssertions_as_instanceOfAssertFactory_Test() { // GIVEN InstanceOfAssertFactory> assertFactory = mock(InstanceOfAssertFactory.class); @@ -827,4 +978,14 @@ void withAssertions_assertThat_spliterator_Test() { assertThat(Stream.of(1, 2).spliterator()).hasCharacteristics(Spliterator.SIZED); } + @Test + void withAssertions_fail_with_message_format_Test() { + // GIVEN + String failureMessage = "bat%s"; + // WHEN + AssertionError error = expectAssertionError(() -> fail(failureMessage, "man")); + // THEN + then(error).hasMessage("batman"); + } + } From 75d2458c3c1a71abdfcd90848900b0ffa5f0d251 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 16 Sep 2020 14:26:54 +1200 Subject: [PATCH 006/191] Honor toString if overridden in Iterable that are not collections. Fixes #1990 --- pom.xml | 37 +++++++++---------- .../presentation/StandardRepresentation.java | 24 ++++++++++-- ...rdRepresentation_iterable_format_Test.java | 25 +++++++++++++ 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index cfa8c27854f..0bf99896fe7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 assertj-core 3.17.3-SNAPSHOT @@ -75,10 +76,7 @@ provided true - + org.hamcrest hamcrest-core @@ -96,17 +94,10 @@ junit-jupiter test
- + org.junit.jupiter junit-jupiter-engine @@ -170,6 +161,12 @@ mockito-junit-jupiter test + + com.fasterxml.jackson.core + jackson-databind + 2.11.2 + test + + 3.0.0 @@ -594,9 +591,9 @@ - net.alchim31.maven - yuicompressor-maven-plugin - 1.5.1 + net.alchim31.maven + yuicompressor-maven-plugin + 1.5.1 diff --git a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java index 7b2a6431ae0..283a1ddb404 100644 --- a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java +++ b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java @@ -38,6 +38,7 @@ import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.util.Calendar; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashMap; @@ -215,7 +216,7 @@ public String toStringOf(Object object) { if (object instanceof PredicateDescription) return toStringOf((PredicateDescription) object); if (object instanceof Future) return toStringOf((Future) object); if (isArray(object)) return formatArray(object); - if (object instanceof Iterable) return smartFormat((Iterable) object); + if (object instanceof Collection) return smartFormat((Collection) object); if (object instanceof Map) return toStringOf((Map) object); if (object instanceof Tuple) return toStringOf((Tuple) object); if (object instanceof MapEntry) return toStringOf((MapEntry) object); @@ -223,9 +224,26 @@ public String toStringOf(Object object) { if (object instanceof InsertDelta) return toStringOf((InsertDelta) object); if (object instanceof ChangeDelta) return toStringOf((ChangeDelta) object); if (object instanceof DeleteDelta) return toStringOf((DeleteDelta) object); + // Only format Iterables that are not collections and have not overridden toString + // ex: JsonNode is an Iterable that is best formatted with its own String + // Path is another example but we can deal with it specifically as it is part of the JDK. + if (object instanceof Iterable && !hasOverriddenToString((Iterable) object)) + return smartFormat((Collection) object); return fallbackToStringOf(object); } + private static boolean hasOverriddenToString(Iterable iterable) { + try { + Method method = iterable.getClass().getMethod("toString"); + Class declaringClass = method.getDeclaringClass(); + return !Object.class.equals(declaringClass); + } catch (NoSuchMethodException | SecurityException e) { + // NoSuchMethodException should not occur as toString is always defined. + // if SecurityException occurs, returning false will lead to format iterable + return false; + } + } + @Override public String unambiguousToStringOf(Object obj) { // some types have already an unambiguous toString, no need to double down @@ -512,8 +530,6 @@ protected String safeStringOf(Object element, String start, String end, String e if (isArrayTypePrimitive(element)) return formatPrimitiveArray(element); // object array/iterable elements can cycle back to root, we pass the latter to check for it if (isArray(element)) return format((Object[]) element, start, end, elementSeparator, indentation, root); - if (element instanceof Iterable && !(element instanceof Path)) - return format((Iterable) element, start, end, elementSeparator, indentation, root); // Since potentially self referencing containers have been handled, it is reasonably safe to use toStringOf. // What we don't track are cycles like A -> B -> A but that should be rare enough thus this solution is good enough // To fully avoid all cycles we would need to track all visited elements but the issue is that: @@ -521,7 +537,7 @@ protected String safeStringOf(Object element, String start, String end, String e // List outerList = list(innerList, innerList); // outerList would be represented as [[1, 2, 3], (already visited)] instead of [[1, 2, 3], [1, 2, 3]] // Final word, the approach used here is the same as the toString implementation in AbstractCollection - return element == null ? NULL : toStringOf(element); + return element == null ? NULL : toStringOf(element); // TODO add indentation? } // private methods diff --git a/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java b/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java index 94c029c1b9b..ec661ad9285 100644 --- a/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java +++ b/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java @@ -27,6 +27,10 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + class StandardRepresentation_iterable_format_Test extends AbstractBaseRepresentationTest { @Test @@ -210,6 +214,27 @@ void should_only_consider_root_object_for_cycles() { then(formatted).isEqualTo("[[1, 2, 3], [1, 2, 3]]"); } + // see https://github.com/joel-costigliola/assertj-core/issues/1990 + @Test + public void should_use_overridden_toString_over_iterable_representation() { + // GIVEN + JsonNode a = JsonNodeFactory.instance.objectNode().put("a", 1); + // WHEN + String formatted = STANDARD_REPRESENTATION.toStringOf(a); + // THEN + then(formatted).isEqualTo("{\"a\":1}"); + } + + @Test + public void should_use_overridden_toString_over_iterable_representation_in_collection_elements() { + // GIVEN + List a = list(JsonNodeFactory.instance.objectNode().put("a", 1)); + // WHEN + String formatted = STANDARD_REPRESENTATION.toStringOf(a); + // THEN + then(formatted).isEqualTo("[{\"a\":1}]"); + } + private static String stringOfLength(int length) { return Stream.generate(() -> "a").limit(length).collect(joining()); } From 423392290d9809a81fed7c15cf17f0c3707f314b Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 16 Sep 2020 15:01:26 +1200 Subject: [PATCH 007/191] Remove duplicate check on array --- .../core/presentation/StandardRepresentation.java | 1 - .../StandardRepresentation_array_format_Test.java | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java index 283a1ddb404..5705bf090e2 100644 --- a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java +++ b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java @@ -492,7 +492,6 @@ protected String smartFormat(Object[] array) { } protected String formatPrimitiveArray(Object o) { - if (!isArray(o)) return null; if (!isArrayTypePrimitive(o)) throw notAnArrayOfPrimitives(o); Object[] array = toObjectArray(o); return format(array, DEFAULT_START, DEFAULT_END, ELEMENT_SEPARATOR, INDENTATION_FOR_SINGLE_LINE, array); diff --git a/src/test/java/org/assertj/core/presentation/StandardRepresentation_array_format_Test.java b/src/test/java/org/assertj/core/presentation/StandardRepresentation_array_format_Test.java index e8567de08f6..8afc4966d9a 100644 --- a/src/test/java/org/assertj/core/presentation/StandardRepresentation_array_format_Test.java +++ b/src/test/java/org/assertj/core/presentation/StandardRepresentation_array_format_Test.java @@ -13,6 +13,7 @@ package org.assertj.core.presentation; import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.util.Arrays.array; import static org.assertj.core.util.Strings.quote; @@ -206,10 +207,21 @@ void should_format_array_with_one_element_per_line() { " \"1234567890\"]>")); } + @ParameterizedTest(name = "{0}") + @MethodSource + public void formatPrimitiveArray_should_throw_exception_if_not_given_a_primitive_array(Object object) { + assertThatIllegalArgumentException().isThrownBy(() -> STANDARD_REPRESENTATION.formatPrimitiveArray(object)) + .withMessage("<%s> is not an array of primitives", object); + } + + private static Stream formatPrimitiveArray_should_throw_exception_if_not_given_a_primitive_array() { + return Stream.of(Arguments.of(12, array("a", "b", "c"), "foo")); + } + @ParameterizedTest(name = "with printing {0} max, {1} should be formatted as {2}") @MethodSource("should_format_array_source") void should_format_array_honoring_display_configuration(int maxElementsForPrinting, Object[] array, - String expectedDescription) { + String expectedDescription) { // GIVEN StandardRepresentation.setMaxElementsForPrinting(maxElementsForPrinting); StandardRepresentation.setMaxLengthForSingleLineDescription(15); From 672891a4c958c5fd4e62e96edac3000375836d0c Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 16 Sep 2020 15:16:53 +1200 Subject: [PATCH 008/191] Simplify code as cycle are only detected if the root object appears DIRECTLY in its element --- .../assertj/core/presentation/StandardRepresentation.java | 8 ++------ .../StandardRepresentation_iterable_format_Test.java | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java index 5705bf090e2..0074674caa8 100644 --- a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java +++ b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java @@ -524,11 +524,7 @@ protected String format(Iterable iterable, String start, String end, String e protected String safeStringOf(Object element, String start, String end, String elementSeparator, String indentation, Object root) { - if (element == root) return isArray(root) ? "(this array)" : "(this iterable)"; - // primitive array elements can't cycle back to already represented containers - if (isArrayTypePrimitive(element)) return formatPrimitiveArray(element); - // object array/iterable elements can cycle back to root, we pass the latter to check for it - if (isArray(element)) return format((Object[]) element, start, end, elementSeparator, indentation, root); + if (element == root) return isArray(root) ? "(this array)" : "(this instance)"; // Since potentially self referencing containers have been handled, it is reasonably safe to use toStringOf. // What we don't track are cycles like A -> B -> A but that should be rare enough thus this solution is good enough // To fully avoid all cycles we would need to track all visited elements but the issue is that: @@ -536,7 +532,7 @@ protected String safeStringOf(Object element, String start, String end, String e // List outerList = list(innerList, innerList); // outerList would be represented as [[1, 2, 3], (already visited)] instead of [[1, 2, 3], [1, 2, 3]] // Final word, the approach used here is the same as the toString implementation in AbstractCollection - return element == null ? NULL : toStringOf(element); // TODO add indentation? + return element == null ? NULL : toStringOf(element); } // private methods diff --git a/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java b/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java index ec661ad9285..1c7469a7d5b 100644 --- a/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java +++ b/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java @@ -190,7 +190,7 @@ void should_format_recursive_iterable() { // WHEN String formatted = STANDARD_REPRESENTATION.toStringOf(selfReferencingList); // THEN - then(formatted).isEqualTo(format("[(this iterable), (this iterable)]")); + then(formatted).isEqualTo(format("[(this instance), (this instance)]")); } @Test @@ -201,7 +201,7 @@ void should_format_iterable_having_itself_as_element() { // WHEN String formatted = STANDARD_REPRESENTATION.toStringOf(selfReferencingList); // THEN - then(formatted).isEqualTo("[\"Hello\", (this iterable)]"); + then(formatted).isEqualTo("[\"Hello\", (this instance)]"); } @Test From 2cbd1151c00eaba5f99c1b2de24ccd4b33024604 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 16 Sep 2020 18:27:04 +1200 Subject: [PATCH 009/191] Fix cast --- .../org/assertj/core/presentation/StandardRepresentation.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java index 0074674caa8..a15cddcade8 100644 --- a/src/main/java/org/assertj/core/presentation/StandardRepresentation.java +++ b/src/main/java/org/assertj/core/presentation/StandardRepresentation.java @@ -227,8 +227,7 @@ public String toStringOf(Object object) { // Only format Iterables that are not collections and have not overridden toString // ex: JsonNode is an Iterable that is best formatted with its own String // Path is another example but we can deal with it specifically as it is part of the JDK. - if (object instanceof Iterable && !hasOverriddenToString((Iterable) object)) - return smartFormat((Collection) object); + if (object instanceof Iterable && !hasOverriddenToString((Iterable) object)) return smartFormat((Iterable) object); return fallbackToStringOf(object); } From b2f2e069e30b0fc2030c3ec4cb31629d39bc898f Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Wed, 16 Sep 2020 23:10:35 +0200 Subject: [PATCH 010/191] Use Java 15 GA --- .github/workflows/cross-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cross-version.yml b/.github/workflows/cross-version.yml index f83315bab99..167fdf706af 100644 --- a/.github/workflows/cross-version.yml +++ b/.github/workflows/cross-version.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - java: [14, 15-ea, 16-ea] + java: [14, 15, 16-ea] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From e8ad15a926ab629e97bbec73e9faa5643dedbf90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2020 07:19:20 +0200 Subject: [PATCH 011/191] Bump org.eclipse.osgi from 3.15.300 to 3.16.0 (#1992) Bumps org.eclipse.osgi from 3.15.300 to 3.16.0. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bf99896fe7..9bd409e9161 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ org.eclipse.platform org.eclipse.osgi - 3.15.300 + 3.16.0 test From 92cfe28fb4a2d966b04f3ec11d47d4ab0a272d67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2020 14:44:00 +0200 Subject: [PATCH 012/191] Bump equalsverifier from 3.4.2 to 3.4.3 (#1993) * Bump equalsverifier from 3.4.2 to 3.4.3 Bumps [equalsverifier](https://github.com/jqno/equalsverifier) from 3.4.2 to 3.4.3. - [Release notes](https://github.com/jqno/equalsverifier/releases) - [Changelog](https://github.com/jqno/equalsverifier/blob/main/CHANGELOG.md) - [Commits](https://github.com/jqno/equalsverifier/compare/equalsverifier-3.4.2...equalsverifier-3.4.3) Signed-off-by: dependabot[bot] * Fix property Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stefano Cordio --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9bd409e9161..48904687f02 100644 --- a/pom.xml +++ b/pom.xml @@ -198,7 +198,7 @@ nl.jqno.equalsverifier equalsverifier - 3.4.2 + 3.4.3 test @@ -677,7 +677,7 @@ [16,) - -Dnl.jqno.equalsverifier.internal.lib.bytebuddy.experimental=true + -Dnet.bytebuddy.experimental=true true From 75c70fa47aeb41589c61a0b527f0f716d712bde6 Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Thu, 17 Sep 2020 17:49:18 +0200 Subject: [PATCH 013/191] Bump jacoco-maven-plugin from 0.8.5 to 0.8.6 (#1994) --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48904687f02..42409a18e80 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ 5.6.2 3.5.10 - 0.8.5 + 0.8.6 @@ -678,7 +678,6 @@ -Dnet.bytebuddy.experimental=true - true From 544e5b8837f6b437ccd6034dadba9fc0f67c761c Mon Sep 17 00:00:00 2001 From: Fr Jeremy Krieg Date: Mon, 14 Sep 2020 13:58:29 +0930 Subject: [PATCH 014/191] Added field injection capability to SoftAssertionsExtension. Created API for soft assertions extension. Consolidate error collection for soft assertions into one place Fixes #1970 --- .../core/api/AbstractSoftAssertions.java | 94 +---- .../core/api/AssertionErrorCollector.java | 39 +- .../api/DefaultAssertionErrorCollector.java | 129 ++++++ .../org/assertj/core/api/ErrorCollector.java | 76 +--- .../core/api/SoftAssertionsProvider.java | 15 +- .../org/assertj/core/api/SoftProxies.java | 22 +- .../junit/jupiter/InjectSoftAssertions.java | 33 ++ .../jupiter/SoftAssertionsExtension.java | 389 ++++++++++++++++-- .../api/junit/jupiter/SoftlyExtension.java | 8 +- .../junit/jupiter/CustomSoftAssertions.java | 30 ++ ...oftAssertionsExtensionIntegrationTest.java | 15 - .../api/junit/jupiter/ExtensionInjector.java | 34 ++ .../InheritingSoftlyExtensionFieldTest.java | 1 + ...AssertionsExtensionAPIIntegrationTest.java | 136 ++++++ ...xtension_InjectionSanityChecking_Test.java | 115 ++++++ ...oftAssertionsExtension_Injection_Test.java | 84 ++++ ...sExtension_PER_CLASS_Concurrency_Test.java | 126 ++++++ ...onsExtension_PER_CLASS_Injection_Test.java | 90 ++++ ...tlyAssertionsExtensionIntegrationTest.java | 1 + .../junit/jupiter/SoftlyExtensionTest.java | 1 + .../core/api/junit/jupiter/TestKitUtils.java | 76 ++++ .../junit/jupiter/WithSoftlyExtension.java | 1 + 22 files changed, 1285 insertions(+), 230 deletions(-) create mode 100644 src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java create mode 100644 src/main/java/org/assertj/core/api/junit/jupiter/InjectSoftAssertions.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertions.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/ExtensionInjector.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_Injection_Test.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Injection_Test.java create mode 100644 src/test/java/org/assertj/core/api/junit/jupiter/TestKitUtils.java diff --git a/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java b/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java index 688a95ed2d2..f18fc973913 100644 --- a/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java +++ b/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java @@ -21,64 +21,26 @@ import org.assertj.core.error.AssertionErrorCreator; import org.assertj.core.internal.Failures; -public abstract class AbstractSoftAssertions implements SoftAssertionsProvider, InstanceOfAssertFactories { +public abstract class AbstractSoftAssertions extends DefaultAssertionErrorCollector + implements SoftAssertionsProvider, InstanceOfAssertFactories { protected final SoftProxies proxies; public AbstractSoftAssertions() { - // pass itself as a AfterAssertionErrorCollected instance + // pass itself as an AssertionErrorCollector instance proxies = new SoftProxies(this); } - /** - * Register a callback allowing to react after an {@link AssertionError} is collected by the current soft assertion. - *

- * The callback is an instance of {@link AfterAssertionErrorCollected} which can be expressed as lambda. - *

- * Example: - *

 SoftAssertions softly = new SoftAssertions();
-   * StringBuilder reportBuilder = new StringBuilder(format("Assertions report:%n"));
-  
-   * // register our callback
-   * softly.setAfterAssertionErrorCollected(error -> reportBuilder.append(String.format("------------------%n%s%n", error.getMessage())));
-   *
-   * // the AssertionError corresponding to the failing assertions are registered in the report
-   * softly.assertThat("The Beatles").isEqualTo("The Rolling Stones");
-   * softly.assertThat(123).isEqualTo(123)
-   *                       .isEqualTo(456);
- *

- * resulting {@code reportBuilder}: - *

 Assertions report:
-   * ------------------
-   * Expecting:
-   *  <"The Beatles">
-   * to be equal to:
-   *  <"The Rolling Stones">
-   * but was not.
-   * ------------------
-   * Expecting:
-   *  <123>
-   * to be equal to:
-   *  <456>
-   * but was not.
- *

- * Alternatively, if you have defined your own SoftAssertions subclass and inherited from {@link AbstractSoftAssertions}, - * the only thing you have to do is to override {@link AfterAssertionErrorCollected#onAssertionErrorCollected(AssertionError)}. - * - * @param afterAssertionErrorCollected the callback. - * - * @since 3.17.0 - */ - public void setAfterAssertionErrorCollected(AfterAssertionErrorCollected afterAssertionErrorCollected) { - proxies.setAfterAssertionErrorCollected(afterAssertionErrorCollected); - } + private static final AssertionErrorCreator ASSERTION_ERROR_CREATOR = new AssertionErrorCreator(); - private final AssertionErrorCreator assertionErrorCreator = new AssertionErrorCreator(); + public static void assertAll(AssertionErrorCollector collector) { + List errors = collector.assertionErrorsCollected(); + if (!errors.isEmpty()) throw ASSERTION_ERROR_CREATOR.multipleSoftAssertionsError(errors); + } @Override public void assertAll() { - List errors = assertionErrorsCollected(); - if (!errors.isEmpty()) throw assertionErrorCreator.multipleSoftAssertionsError(errors); + assertAll(this); } @Override @@ -87,11 +49,6 @@ public void assertAll() { return proxies.createSoftAssertionProxy(assertClass, actualClass, actual); } - @Override - public void collectAssertionError(AssertionError error) { - proxies.collectError(error); - } - /** * Fails with the given message. * @@ -100,7 +57,7 @@ public void collectAssertionError(AssertionError error) { */ public void fail(String failureMessage) { AssertionError error = Failures.instance().failure(failureMessage); - proxies.collectError(error); + collectAssertionError(error); } /** @@ -112,7 +69,7 @@ public void fail(String failureMessage) { */ public void fail(String failureMessage, Object... args) { AssertionError error = Failures.instance().failure(format(failureMessage, args)); - proxies.collectError(error); + collectAssertionError(error); } /** @@ -125,7 +82,7 @@ public void fail(String failureMessage, Object... args) { public void fail(String failureMessage, Throwable realCause) { AssertionError error = Failures.instance().failure(failureMessage); error.initCause(realCause); - proxies.collectError(error); + collectAssertionError(error); } /** @@ -154,16 +111,12 @@ public void failBecauseExceptionWasNotThrown(Class throwabl */ public void shouldHaveThrown(Class throwableClass) { AssertionError error = Failures.instance().expectedThrowableNotThrown(throwableClass); - proxies.collectError(error); + collectAssertionError(error); } - /** - * Returns a copy of list of soft assertions collected errors. - * @return a copy of list of soft assertions collected errors. - */ @Override public List assertionErrorsCollected() { - return decorateErrorsCollected(proxies.errorsCollected()); + return decorateErrorsCollected(super.assertionErrorsCollected()); } /** @@ -171,7 +124,7 @@ public List assertionErrorsCollected() { * @return a copy of list of soft assertions collected errors. */ public List errorsCollected() { - return decorateErrorsCollected(proxies.errorsCollected()); + return decorateErrorsCollected(super.assertionErrorsCollected()); } /** @@ -184,23 +137,6 @@ protected List decorateErrorsCollected(List - * Example: - *

 Person person = ...
-   * SoftAssertions soft = new SoftAssertions();
-   * if (soft.assertThat(person.getAddress()).isNotNull().wasSuccess()) {
-   *     soft.assertThat(person.getAddress().getStreet()).isNotNull();
-   * }
- * - * @return true if the last assertion was a success. - */ - @Override - public boolean wasSuccess() { - return proxies.wasSuccess(); - } - private List addLineNumberToErrorMessages(List errors) { return errors.stream() .map(this::addLineNumberToErrorMessage) diff --git a/src/main/java/org/assertj/core/api/AssertionErrorCollector.java b/src/main/java/org/assertj/core/api/AssertionErrorCollector.java index 13db53bdd17..608b85c530a 100644 --- a/src/main/java/org/assertj/core/api/AssertionErrorCollector.java +++ b/src/main/java/org/assertj/core/api/AssertionErrorCollector.java @@ -13,16 +13,28 @@ package org.assertj.core.api; import java.util.List; +import java.util.Optional; public interface AssertionErrorCollector extends AfterAssertionErrorCollected { /** - * This method can be used to collect soft assertion errors. + * Optionally sets a "delegate" collector into which the collected assertions will be deposited. *

- * Warning: this is not the method used internally by AssertJ to collect all of them, overriding it to react to each - * collected assertion error will not work. + * Note that if you set a delegate, this instance will no longer collect or report assertion errors itself but will + * forward them all to the delegate for collection. + * + * @param delegate the {@link AssertionErrorCollector} to which the assertions will be forwarded. + */ + default void setDelegate(AssertionErrorCollector delegate) {} + + default Optional getDelegate() { + return Optional.empty(); + } + + /** + * This method can be used to collect soft assertion errors. *

- * To be able to react after an assertion error is collected, use @{@link #onAssertionErrorCollected(AssertionError)} instead. + * To be able to react after an assertion error is collected, use {@link #onAssertionErrorCollected(AssertionError)}. * * @param error the {@link AssertionError} to collect. */ @@ -34,4 +46,23 @@ public interface AssertionErrorCollector extends AfterAssertionErrorCollected { default void onAssertionErrorCollected(AssertionError assertionError) { // nothing by default } + + /** + * Indicates/sets that the last assertion was a success. + */ + void succeeded(); + + /** + * Returns the result of last soft assertion which can be used to decide what the next one should be. + *

+ * Example: + *

 Person person = ...
+   * SoftAssertions soft = new SoftAssertions();
+   * if (soft.assertThat(person.getAddress()).isNotNull().wasSuccess()) {
+   *     soft.assertThat(person.getAddress().getStreet()).isNotNull();
+   * }
+ * + * @return true if the last assertion was a success. + */ + boolean wasSuccess(); } diff --git a/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java b/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java new file mode 100644 index 00000000000..9e08f692a9a --- /dev/null +++ b/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java @@ -0,0 +1,129 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api; + +import static java.util.Collections.synchronizedList; +import static java.util.Collections.unmodifiableList; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class DefaultAssertionErrorCollector implements AssertionErrorCollector { + + // Marking this field as volatile doesn't ensure complete thread safety + // (mutual exclusion, race-free behaviour), but guarantees eventual visibility + private volatile boolean wasSuccess = true; + private List collectedAssertionErrors = synchronizedList(new ArrayList<>()); + + private AfterAssertionErrorCollected callback = this; + + private AssertionErrorCollector delegate = null; + + public DefaultAssertionErrorCollector() { + super(); + } + + // I think ideally, this would be set in the constructor and made final; + // however that would require a new constructor that would not make it + // backward compatible with existing SoftAssertionProvider implementations. + @Override + public void setDelegate(AssertionErrorCollector delegate) { + this.delegate = delegate; + } + + @Override + public Optional getDelegate() { + return Optional.ofNullable(delegate); + } + + @Override + public void collectAssertionError(AssertionError error) { + if (delegate == null) { + collectedAssertionErrors.add(error); + wasSuccess = false; + } else { + delegate.collectAssertionError(error); + } + callback.onAssertionErrorCollected(error); + } + + /** + * Returns a list of soft assertions collected errors. If a delegate + * has been set (see {@link #setDelegate(AssertionErrorCollector) setDelegate()}, + * then this method will return the result of the delegate's {@code assertErrorsCollected()}. + * + * @return A list of soft assertions collected errors. + */ + @Override + public List assertionErrorsCollected() { + return delegate != null ? delegate.assertionErrorsCollected() : unmodifiableList(collectedAssertionErrors); + } + + /** + * Register a callback allowing to react after an {@link AssertionError} is collected by the current soft assertion. + *

+ * The callback is an instance of {@link AfterAssertionErrorCollected} which can be expressed as lambda. + *

+ * Example: + *

 SoftAssertions softly = new SoftAssertions();
+   * StringBuilder reportBuilder = new StringBuilder(format("Assertions report:%n"));
+  
+   * // register our callback
+   * softly.setAfterAssertionErrorCollected(error -> reportBuilder.append(String.format("------------------%n%s%n", error.getMessage())));
+   *
+   * // the AssertionError corresponding to the failing assertions are registered in the report
+   * softly.assertThat("The Beatles").isEqualTo("The Rolling Stones");
+   * softly.assertThat(123).isEqualTo(123)
+   *                       .isEqualTo(456);
+ *

+ * resulting {@code reportBuilder}: + *

 Assertions report:
+   * ------------------
+   * Expecting:
+   *  <"The Beatles">
+   * to be equal to:
+   *  <"The Rolling Stones">
+   * but was not.
+   * ------------------
+   * Expecting:
+   *  <123>
+   * to be equal to:
+   *  <456>
+   * but was not.
+ *

+ * Alternatively, if you have defined your own SoftAssertions subclass and inherited from {@link AbstractSoftAssertions}, + * the only thing you have to do is to override {@link AfterAssertionErrorCollected#onAssertionErrorCollected(AssertionError)}. + * + * @param afterAssertionErrorCollected the callback. + * + * @since 3.17.0 + */ + public void setAfterAssertionErrorCollected(AfterAssertionErrorCollected afterAssertionErrorCollected) { + callback = afterAssertionErrorCollected; + } + + @Override + public void succeeded() { + if (delegate == null) { + wasSuccess = true; + } else { + delegate.succeeded(); + } + } + + @Override + public boolean wasSuccess() { + return delegate == null ? wasSuccess : delegate.wasSuccess(); + } +} \ No newline at end of file diff --git a/src/main/java/org/assertj/core/api/ErrorCollector.java b/src/main/java/org/assertj/core/api/ErrorCollector.java index 248f420cc34..23a10aaf73b 100644 --- a/src/main/java/org/assertj/core/api/ErrorCollector.java +++ b/src/main/java/org/assertj/core/api/ErrorCollector.java @@ -13,10 +13,7 @@ package org.assertj.core.api; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.concurrent.Callable; import net.bytebuddy.implementation.bind.annotation.FieldValue; @@ -34,16 +31,10 @@ public class ErrorCollector { private static final String INTERCEPT_METHOD_NAME = "intercept"; private static final String CLASS_NAME = ErrorCollector.class.getName(); - // The list is synchronized in case errors arrive from more than a single thread. - // scope : the current softassertion object - private final List errors = Collections.synchronizedList(new ArrayList<>()); - // scope : the last assertion call (might be nested) - private final LastResult lastResult = new LastResult(); + private AssertionErrorCollector assertionErrorCollector; - private AfterAssertionErrorCollected afterAssertionErrorCollected; - - void setAfterAssertionErrorCollected(AfterAssertionErrorCollected afterAssertionErrorCollected) { - this.afterAssertionErrorCollected = afterAssertionErrorCollected; + ErrorCollector(AssertionErrorCollector collector) { + this.assertionErrorCollector = collector; } /** @@ -63,10 +54,10 @@ public static Object intercept(@FieldValue(FIELD_NAME) ErrorCollector errorColle @StubValue Object stub) throws Exception { try { Object result = proxy.call(); - errorCollector.lastResult.setSuccess(true); + errorCollector.succeeded(); return result; } catch (AssertionError assertionError) { - if (errorCollector.isNestedErrorCollectorProxyCall()) { + if (isNestedErrorCollectorProxyCall()) { // let the most outer call handle the assertion error throw assertionError; } @@ -80,23 +71,15 @@ public static Object intercept(@FieldValue(FIELD_NAME) ErrorCollector errorColle return assertion; } - void addError(AssertionError error) { - errors.add(error); - lastResult.setSuccess(false); - if (afterAssertionErrorCollected != null) { - afterAssertionErrorCollected.onAssertionErrorCollected(error); - } + private void addError(AssertionError error) { + assertionErrorCollector.collectAssertionError(error); } - public List errors() { - return Collections.unmodifiableList(errors); + private void succeeded() { + assertionErrorCollector.succeeded(); } - public boolean wasSuccess() { - return lastResult.wasSuccess(); - } - - private boolean isNestedErrorCollectorProxyCall() { + private static boolean isNestedErrorCollectorProxyCall() { return countErrorCollectorProxyCalls() > 1; } @@ -106,43 +89,4 @@ private static long countErrorCollectorProxyCalls() { && stackTraceElement.getMethodName().startsWith(INTERCEPT_METHOD_NAME)) .count(); } - - private static class LastResult { - // Marking these fields as volatile doesn't ensure complete thread safety - // (mutual exclusion, race-free behaviour), but guarantees eventual visibility - private volatile boolean wasSuccess = true; - private volatile boolean errorFound = false; - - private boolean wasSuccess() { - return wasSuccess; - } - - private void setSuccess(boolean success) { - - // errorFound must be true if any nested call ends up in error - // Nested call Example : softly.assertThat(true).isFalse() - // call chain : - // -- softly.assertThat(true).isFalse() - // ----- proxied isFalse() -> calls isEqualTo(false) which is proxied - // ------- proxied isEqualTo(false) : catch AssertionError => last result success = false, back to outer call - // ---- proxied isFalse() : no AssertionError caught => last result success = true - errorFound |= !success; - wasSuccess = success; - - if (resolvingOutermostErrorCollectorProxyNestedCall()) { - // need to reset errorFound for the next soft assertion - errorFound = false; - } - } - - private boolean resolvingOutermostErrorCollectorProxyNestedCall() { - return countErrorCollectorProxyCalls() == 1; - } - - @Override - public String toString() { - return String.format("LastResult [wasSuccess=%s, errorFound=%s]", wasSuccess, errorFound); - } - - } } diff --git a/src/main/java/org/assertj/core/api/SoftAssertionsProvider.java b/src/main/java/org/assertj/core/api/SoftAssertionsProvider.java index f18b7e868c8..a5a9e6a090b 100644 --- a/src/main/java/org/assertj/core/api/SoftAssertionsProvider.java +++ b/src/main/java/org/assertj/core/api/SoftAssertionsProvider.java @@ -60,20 +60,6 @@ default void assertAlso(AssertionErrorCollector collector) { collector.assertionErrorsCollected().forEach(this::collectAssertionError); } - /** - * Returns the result of last soft assertion which can be used to decide what the next one should be. - *

- * Example : - *

 Person person = ...
-   * SoftAssertions soft = new SoftAssertions();
-   * if (soft.assertThat(person.getAddress()).isNotNull().wasSuccess()) {
-   *     soft.assertThat(person.getAddress().getStreet()).isNotNull();
-   * }
- * - * @return true if the last assertion was a success. - */ - boolean wasSuccess(); - /** * Catch and collect assertion errors coming from standard and custom assertions. *

@@ -88,6 +74,7 @@ default void assertAlso(AssertionErrorCollector collector) { default void check(ThrowingRunnable assertion) { try { assertion.run(); + succeeded(); } catch (AssertionError error) { collectAssertionError(error); } catch (RuntimeException runtimeException) { diff --git a/src/main/java/org/assertj/core/api/SoftProxies.java b/src/main/java/org/assertj/core/api/SoftProxies.java index cec4f62acb6..73f31be2c52 100644 --- a/src/main/java/org/assertj/core/api/SoftProxies.java +++ b/src/main/java/org/assertj/core/api/SoftProxies.java @@ -20,7 +20,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.List; import org.assertj.core.api.ClassLoadingStrategyFactory.ClassLoadingStrategyPair; import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; @@ -98,25 +97,8 @@ class SoftProxies { private ErrorCollector collector; - public SoftProxies(AfterAssertionErrorCollected afterAssertionErrorCollected) { - collector = new ErrorCollector(); - setAfterAssertionErrorCollected(afterAssertionErrorCollected); - } - - void setAfterAssertionErrorCollected(AfterAssertionErrorCollected afterAssertionErrorCollected) { - collector.setAfterAssertionErrorCollected(afterAssertionErrorCollected); - } - - public boolean wasSuccess() { - return collector.wasSuccess(); - } - - void collectError(AssertionError error) { - collector.addError(error); - } - - List errorsCollected() { - return collector.errors(); + public SoftProxies(AssertionErrorCollector assertionErrorCollector) { + collector = new ErrorCollector(assertionErrorCollector); } , ACTUAL> SELF createSoftAssertionProxy(Class assertClass, diff --git a/src/main/java/org/assertj/core/api/junit/jupiter/InjectSoftAssertions.java b/src/main/java/org/assertj/core/api/junit/jupiter/InjectSoftAssertions.java new file mode 100644 index 00000000000..fd6b62c348d --- /dev/null +++ b/src/main/java/org/assertj/core/api/junit/jupiter/InjectSoftAssertions.java @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.assertj.core.api.BDDSoftAssertions; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.SoftAssertionsProvider; + +/** + * Annotation used with {@link SoftAssertionsExtension} for specify wich test instance fields should be initialised with + * a {@link SoftAssertionsProvider} concrete implementation, for example {@link SoftAssertions}, {@link BDDSoftAssertions} or any + * custom soft assertions class. + */ +@Retention(RUNTIME) +@Target(FIELD) +public @interface InjectSoftAssertions { +} diff --git a/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java b/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java index fe462f565e6..b2f5e7389ea 100644 --- a/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java +++ b/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java @@ -14,58 +14,90 @@ import static java.lang.String.format; import static java.lang.reflect.Modifier.isAbstract; +import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated; +import static org.junit.platform.commons.support.ReflectionSupport.findFields; import java.lang.reflect.Executable; +import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; +import java.util.Collection; +import java.util.List; import java.util.Optional; +import java.util.concurrent.ConcurrentLinkedQueue; +import org.assertj.core.annotations.Beta; +import org.assertj.core.api.AbstractSoftAssertions; +import org.assertj.core.api.AssertionErrorCollector; import org.assertj.core.api.BDDSoftAssertions; +import org.assertj.core.api.DefaultAssertionErrorCollector; import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.SoftAssertionsProvider; +import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.platform.commons.annotation.Testable; +import org.junit.platform.commons.support.HierarchyTraversalMode; import org.junit.platform.commons.support.ReflectionSupport; /** - * Extension for JUnit Jupiter that provides support for injecting a concrete - * implementation of {@link SoftAssertionsProvider} into test methods. Two examples that - * come packaged with AssertJ are {@link SoftAssertions} and - * {@link BDDSoftAssertions}, but custom implementations are also supported as - * long as they have a default constructor. + * Extension for JUnit Jupiter that provides support for injecting a concrete implementation of {@link SoftAssertionsProvider} + * into test methods and (since 3.18.0) into test fields annotated with {@code @InjectSoftAssertions}. + *

+ * Two examples of {@code SoftAssertionsProvider}s that come packaged with AssertJ are {@link SoftAssertions} and + * {@link BDDSoftAssertions}, but custom implementations are also supported as long as they are non-abstract and have a default + * constructor. * *

Applicability

* *

- * In this context, the term "test method" refers to any method annotated with - * {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest}, - * {@code @TestFactory}, or {@code @TestTemplate}.
- * This extension does not inject {@code SoftAssertionsProvider} arguments into test - * constructors or lifecycle methods. + * In this context, the term "test method" refers to any method annotated with {@code @Test}, {@code @RepeatedTest}, + * {@code @ParameterizedTest}, {@code @TestFactory}, or {@code @TestTemplate}.
+ * This extension does not inject {@code SoftAssertionsProvider} arguments into test constructors or lifecycle methods. * *

Scope

* - *

- * The scope of the {@code SoftAssertionsProvider} instance managed by this extension - * begins when a parameter of type {@code SoftAssertionsProvider} is resolved for a test - * method.
- * The scope of the instance ends after the test method has been executed, this - * is when {@code assertAll()} will be invoked on the instance to verify that no - * soft assertions failed. + * Annotated {@code SoftAssertionsProvider} fields become valid from the `@BeforeEach` lifecycle phase. + * For parameters, they become are valid when the parameter is resolved.
+ * In the {@code afterTestExecution} phase (immediately after the test has returned, but before the {@code AfterEach} phase, all + * collected errors (if any) will wrapped in a single multiple-failures error.
+ * All {@code SoftAssertionsProvider} instances (fields & parameters) created within the scope of the same test method + * (including its {@code BeforeEach} phase) will share the same state object to collect the failed assertions, so that all + * assertion failures from all {@link SoftAssertionsProvider}s will be reported in the order that they failed. + * + *

Integration with third-party extensions

* - *

Example with {@code SoftAssertions}

+ * Sometimes a third-party extension may wish to softly assert something as part of the main test. Or sometimes a third-party + * extension may be a wrapper around another assertion library (eg, Mockito) and it would be nice for that library's soft + * assertions to mix well with AssertJ's. This can be achieved through the use of the {@code SoftAssertionExtension}'s API. + * Calling {@link #getAssertionErrorCollector(ExtensionContext)} will return a handle to the error collector used for the current + * context into which a third-party extension can directly store its assertion failures. Alternatively, calling + * {@link #getSoftAssertionsProvider(ExtensionContext, Class) getSoftAssertionsProvider()} will instantiate a + * {@link SoftAssertionsProvider} for the given context that can then be used to make assertions. + * + *

Examples

+ * + *

Example parameter injection

* *
  *  {@literal @}ExtendWith(SoftAssertionsExtension.class)
  * class ExampleTestCase {
  *
+ *    {@literal @}InjectSoftAssertions
+ *    BDDSoftAssertions bdd;
+ *
  *    {@literal @}Test
  *    void multipleFailures(SoftAssertions softly) {
  *       softly.assertThat(2 * 3).isEqualTo(0);
@@ -75,28 +107,181 @@
  * }
  * 
* - *

Example with {@code BDDSoftAssertions}

+ *

Example field injection

+ *
 {@literal @}ExtendWith(SoftlyExtension.class)
+ * public class SoftlyExtensionExample {
+ *
+ *   // initialized by the SoftlyExtension extension
+ *   {@literal @}InjectSoftAssertions
+ *   private SoftAssertions soft;
+ *
+ *   {@literal @}Test
+ *   public void chained_soft_assertions_example() {
+ *     String name = "Michael Jordan - Bulls";
+ *     soft.assertThat(name)
+ *         .startsWith("Mi")
+ *         .contains("Bulls");
+ *     // no need to call softly.assertAll(), this is done by the extension
+ *   }
+ *
+ *   // nested classes test work too
+ *   {@literal @}Nested
+ *   class NestedExample {
+ *
+ *     {@literal @}Test
+ *     public void football_assertions_example() {
+ *       String kylian = "Kylian Mbappé";
+ *       soft.assertThat(kylian)
+ *           .startsWith("Ky")
+ *           .contains("bap");
+ *       // no need to call softly.assertAll(), this is done by the extension
+ *     }
+ *   }
+ * } 
+ * + *

Example using a mix of field and parameter injection

* *
  *  {@literal @}ExtendWith(SoftAssertionsExtension.class)
  * class ExampleTestCase {
  *
+ *    {@literal @}InjectSoftAssertions
+ *    SoftAssertions softly
+ *
  *    {@literal @}Test
- *    void multipleFailures(BDDSoftAssertions softly) {
- *       softly.then(2 * 3).isEqualTo(0);
- *       softly.then(Arrays.asList(1, 2)).containsOnly(1);
- *       softly.then(1 + 1).isEqualTo(2);
+ *    void multipleFailures(BDDSoftAssertions bdd) {
+ *       bdd.then(2 * 3).isEqualTo(0);
+ *       softly.assertThat(Arrays.asList(1, 2)).containsOnly(1);
+ *       bdd.then(1 + 1).isEqualTo(2);
+ *       // When SoftAssertionsExtension calls assertAll(), the three
+ *       // above failures above will be reported in-order.
+ *    }
+ * }
+ * 
+ * + *

Example third-party extension using {@code SoftAssertionsExtension}

+ * + *
+ * 
+ * class ExampleTestCase implements BeforeEachCallback {
+ *
+ *    {@literal @}Override
+ *    public void beforeEach(ExtensionContext context) {
+ *      SoftAssertions softly = SoftAssertionsExtension
+ *        .getSoftAssertionsProvider(context, SoftAssertions.class);
+ *      softly.assertThat(false).isTrue();
+ *      // When SoftAssertionsExtension calls assertAll(), the
+ *      // above failure will be included in the list of reported failures.
  *    }
  * }
  * 
* * @author Sam Brannen + * @author Arthur Mita (author of {@link SoftlyExtension}) + * @author Fr Jeremy Krieg * @since 3.13 */ -public class SoftAssertionsExtension implements ParameterResolver, AfterTestExecutionCallback { +public class SoftAssertionsExtension + implements TestInstancePostProcessor, BeforeEachCallback, ParameterResolver, AfterTestExecutionCallback { private static final Namespace SOFT_ASSERTIONS_EXTENSION_NAMESPACE = Namespace.create(SoftAssertionsExtension.class); + static class ThreadLocalErrorCollector implements AssertionErrorCollector { + + InheritableThreadLocal threadLocal = new InheritableThreadLocal<>(); + + @Override + public Optional getDelegate() { + return Optional.of(threadLocal.get()); + } + + @Override + public void setDelegate(AssertionErrorCollector assertionErrorCollector) { + threadLocal.set(assertionErrorCollector); + } + + public void reset() { + threadLocal.remove(); + } + + @Override + public void collectAssertionError(AssertionError assertionError) { + threadLocal.get().collectAssertionError(assertionError); + } + + @Override + public List assertionErrorsCollected() { + return threadLocal.get().assertionErrorsCollected(); + } + + @Override + public void succeeded() { + threadLocal.get().succeeded(); + } + + @Override + public boolean wasSuccess() { + return threadLocal.get().wasSuccess(); + } + } + + static boolean isPerClass(ExtensionContext context) { + return context.getTestInstanceLifecycle().map(x -> x == Lifecycle.PER_CLASS).orElse(false); + } + + static boolean isAnnotatedConcurrent(ExtensionContext context) { + return findAnnotation(context.getRequiredTestClass(), Execution.class).map(Execution::value) + .map(x -> x == ExecutionMode.CONCURRENT) + .orElse(false); + } + + static boolean isPerClassConcurrent(ExtensionContext context) { + return isPerClass(context) && isAnnotatedConcurrent(context); + } + + @Override + public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception { + // find SoftAssertions fields in the test class hierarchy + Collection softAssertionsFields = findFields(testInstance.getClass(), + field -> isAnnotated(field, InjectSoftAssertions.class), + HierarchyTraversalMode.BOTTOM_UP); + for (Field softAssertionsField : softAssertionsFields) { + checkIsNotStaticOrFinal(softAssertionsField); + Class softAssertionsProviderClass = asSoftAssertionsProviderClass(softAssertionsField, + softAssertionsField.getType()); + checkIsNotAbstract(softAssertionsField, softAssertionsProviderClass); + checkHasDefaultConstructor(softAssertionsField, softAssertionsProviderClass); + SoftAssertionsProvider softAssertions = getSoftAssertionsProvider(context, softAssertionsProviderClass); + setTestInstanceSoftAssertionsField(testInstance, softAssertionsField, softAssertions); + } + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + AssertionErrorCollector collector = getAssertionErrorCollector(context); + + if (isPerClassConcurrent(context)) { + // If the current context is "per class+concurrent", then getSoftAssertionsProvider() will have already set the delegate + // for all the soft assertions provider to the thread-local error collector, so all we need to do is set the tlec's value + // for the current thread. + ThreadLocalErrorCollector tlec = getThreadLocalCollector(context); + tlec.setDelegate(collector); + } else { + // Make sure that all of the soft assertion provider instances have their delegate initialised to the assertion error + // collector for the current context. Also check parents (in the case of nested tests). + while (initialiseDelegate(context, collector)) { + context = context.getParent().get(); + } + } + } + + private static boolean initialiseDelegate(ExtensionContext context, AssertionErrorCollector collector) { + Collection providers = getSoftAssertionsProviders(context); + if (providers == null) return false; + providers.forEach(x -> x.setDelegate(collector)); + return context.getParent().isPresent(); + } + @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { // Abort if parameter type is unsupported. @@ -129,15 +314,23 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte @SuppressWarnings("unchecked") Class concreteSoftAssertionsProviderType = (Class) parameterContext.getParameter() .getType(); - return getStore(extensionContext).getOrComputeIfAbsent(SoftAssertionsProvider.class, - unused -> ReflectionSupport.newInstance(concreteSoftAssertionsProviderType), - SoftAssertionsProvider.class); + SoftAssertionsProvider provider = ReflectionSupport.newInstance(concreteSoftAssertionsProviderType); + provider.setDelegate(getAssertionErrorCollector(extensionContext)); + return provider; } @Override public void afterTestExecution(ExtensionContext extensionContext) { - Optional.ofNullable(getStore(extensionContext).remove(SoftAssertionsProvider.class, SoftAssertionsProvider.class)) - .ifPresent(SoftAssertionsProvider::assertAll); + AssertionErrorCollector collector; + if (isPerClassConcurrent(extensionContext)) { + ThreadLocalErrorCollector tlec = getThreadLocalCollector(extensionContext); + collector = tlec.getDelegate().get(); + // Clear the tlec just in case this thread gets re-used. + tlec.reset(); + } else { + collector = getAssertionErrorCollector(extensionContext); + } + AbstractSoftAssertions.assertAll(collector); } private static boolean isUnsupportedParameterType(Parameter parameter) { @@ -149,4 +342,142 @@ private static Store getStore(ExtensionContext extensionContext) { return extensionContext.getStore(SOFT_ASSERTIONS_EXTENSION_NAMESPACE); } + private static ThreadLocalErrorCollector getThreadLocalCollector(ExtensionContext context) { + return getStore(context).getOrComputeIfAbsent(ThreadLocalErrorCollector.class, unused -> new ThreadLocalErrorCollector(), + ThreadLocalErrorCollector.class); + } + + /** + * Returns the {@link AssertionErrorCollector} for the given extension context, if none exists for the current context then + * one is created. + *

+ * This method is thread safe - all extensions attempting to access the {@code AssertionErrorCollector} for a given context + * through this method will get a reference to the same {@code AssertionErrorCollector} instance, regardless of the order + * in which they are called. + *

+ * Third-party extensions that wish to provide soft-asserting behavior can use this method to obtain the current + * {@code AssertionErrorCollector} instance and record their assertion failures into it by calling + * {@link AssertionErrorCollector#collectAssertionError(AssertionError) collectAssertionError(AssertionError)}.
+ * In this way their soft assertions will integrate with the existing AssertJ soft assertions and the assertion failures (both + * AssertJ's and the third-party extension's) will be reported in the order that they occurred. + * + * @param context the {@code ExtensionContext} whose error collector we are attempting to retrieve. + * @return The {@code AssertionErrorCollector} for the given context. + */ + @Beta + public static AssertionErrorCollector getAssertionErrorCollector(ExtensionContext context) { + return getStore(context).getOrComputeIfAbsent(AssertionErrorCollector.class, unused -> new DefaultAssertionErrorCollector(), + AssertionErrorCollector.class); + } + + @SuppressWarnings("unchecked") + private static Collection getSoftAssertionsProviders(ExtensionContext context) { + return getStore(context).getOrComputeIfAbsent(Collection.class, unused -> new ConcurrentLinkedQueue<>(), Collection.class); + } + + private static T instantiateProvider(ExtensionContext context, Class providerType) { + T softAssertions = ReflectionSupport.newInstance(providerType); + // If we are running single-threaded, we won't have any concurrency issues. Likewise, + // if we are running "per-method", then every test gets its own instance and again there + // won't be any concurrency issues. But we need to special-case the situation where + // we are running *both* per class and concurrent - use a thread-local so that each thread + // gets its own copy. The beforeEach() callback above will take care of setting the + // ThreadLocal collector's value for the thread in which it is executing. + if (isPerClassConcurrent(context)) { + softAssertions.setDelegate(getThreadLocalCollector(context)); + } else if (context.getTestMethod().isPresent()) { + // If we're already in a method, then set our delegate as the beforeEach() which sets it may have already run. + softAssertions.setDelegate(getAssertionErrorCollector(context)); + } + getSoftAssertionsProviders(context).add(softAssertions); + return softAssertions; + } + + /** + * Returns a {@link SoftAssertionsProvider} instance of the given type for the given extension context. + * If no instance of the given type exists for the supplied context, then one is created.
+ * Note that the given type must be a concrete type with an accessible no-arg constructor for this method to work. + *

+ * This method is thread safe - all extensions attempting to access the {@code SoftAssertionsProvider} for a + * given context through this method will receive end up getting a reference to the same + * {@code SoftAssertionsProvider} instance of that same type, regardless of the order in which they are called. + *

+ * Third party extensions that wish to use soft assertions in their own implementation can use this + * to get a {@code SoftAssertionsProvider} instance that interoperates with other soft-asserting + * extensions (including {@code SoftAssertionsExtension}). + *

+ * The {@code SoftAssertionExtension} will take care of initialising this provider instance's delegate + * at the appropriate time, so that collected soft assertions are routed to the {@link AssertionErrorCollector} + * instance for the current context. + * + *

 public class CustomExtension implements BeforeEachCallback {
+   *
+   *   {@literal @}Override
+   *   public void beforeEach(ExtensionContext context) {
+   *     CustomSoftAssertions softly = SoftAssertionsExtension.getSoftAssertionsProvider(context, CustomSoftAssertions.class);
+   *     softly.assertThat(1).isOne();
+   *   }
+   * }
+   * 
+ * + * @param the type of {@link SoftAssertionsProvider} to instantiate. + * @param context the {@code ExtensionContext} whose error collector we are attempting to retrieve. + * @param concreteSoftAssertionsProviderType the class instance for the type of soft assertions + * @return The {@code AssertionErrorCollector} for the given context. + */ + @Beta + public static T getSoftAssertionsProvider(ExtensionContext context, + Class concreteSoftAssertionsProviderType) { + return getStore(context).getOrComputeIfAbsent(concreteSoftAssertionsProviderType, + unused -> instantiateProvider(context, concreteSoftAssertionsProviderType), + concreteSoftAssertionsProviderType); + } + + private static void setTestInstanceSoftAssertionsField(Object testInstance, Field softAssertionsField, + SoftAssertionsProvider softAssertions) { + softAssertionsField.setAccessible(true); + try { + softAssertionsField.set(testInstance, softAssertions); + } catch (IllegalAccessException e) { + throw new ExtensionConfigurationException(format("[%s] Could not gain access to field", softAssertionsField.getName()), e); + } + } + + private static void checkHasDefaultConstructor(Field softAssertionsField, + Class softAssertionsProviderClass) { + try { + softAssertionsProviderClass.getDeclaredConstructor(); + } catch (@SuppressWarnings("unused") Exception e) { + throw new ExtensionConfigurationException(format("[%s] SoftAssertionsProvider [%s] does not have a default constructor", + softAssertionsField.getName(), softAssertionsProviderClass.getName())); + } + } + + private static void checkIsNotAbstract(Field softAssertionsField, + Class softAssertionsProviderClass) { + if (Modifier.isAbstract(softAssertionsProviderClass.getModifiers())) { + throw new ExtensionConfigurationException(format("[%s] SoftAssertionsProvider [%s] is abstract and cannot be instantiated.", + softAssertionsField.getName(), softAssertionsProviderClass)); + } + } + + @SuppressWarnings("unchecked") + private static Class asSoftAssertionsProviderClass(Field softAssertionsField, + Class providerClass) { + if (!SoftAssertionsProvider.class.isAssignableFrom(providerClass)) { + throw new ExtensionConfigurationException(format("[%s] field is not a SoftAssertionsProvider (%s).", + softAssertionsField.getName(), providerClass.getTypeName())); + } + // Guaranteed because of the sanity check + return (Class) providerClass; + } + + private static void checkIsNotStaticOrFinal(Field softAssertionsField) { + int fieldModifiers = softAssertionsField.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isFinal(fieldModifiers)) { + throw new ExtensionConfigurationException(format("[%s] SoftAssertionsProvider field must not be static or final.", + softAssertionsField.getName())); + } + } + } diff --git a/src/main/java/org/assertj/core/api/junit/jupiter/SoftlyExtension.java b/src/main/java/org/assertj/core/api/junit/jupiter/SoftlyExtension.java index a46ea469809..5c9759b7672 100644 --- a/src/main/java/org/assertj/core/api/junit/jupiter/SoftlyExtension.java +++ b/src/main/java/org/assertj/core/api/junit/jupiter/SoftlyExtension.java @@ -19,7 +19,6 @@ import java.util.Collection; import java.util.Optional; -import org.assertj.core.annotations.Beta; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.AfterTestExecutionCallback; @@ -52,7 +51,8 @@ *
  • May exhibit unpredictable behaviour in concurrent test execution
  • * *

    - * If you hit such limitations, consider using {@link SoftAssertionsExtension} instead. + * If you hit such limitations, consider using {@link SoftAssertionsExtension} instead, since 3.18.0, + * {@code SoftAssertionsExtension} supports field injection with neither of these two limitations. *

    * Example: *

     {@literal @}ExtendWith(SoftlyExtension.class)
    @@ -85,8 +85,10 @@
      *   }
      * } 
    * @author Arthur Mita + * @deprecated This functionality (and more) has been rolled into {@link SoftAssertionsExtension} + * as of AssertJ 3.18.0. **/ -@Beta +@Deprecated public class SoftlyExtension implements AfterTestExecutionCallback, TestInstancePostProcessor { private static final Namespace SOFTLY_EXTENSION_NAMESPACE = Namespace.create(SoftlyExtension.class); diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertions.java b/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertions.java new file mode 100644 index 00000000000..d2cf4d6f67a --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertions.java @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import java.util.List; + +import org.assertj.core.api.AbstractSoftAssertions; +import org.assertj.core.api.IntegerAssert; +import org.assertj.core.api.ProxyableListAssert; + +class CustomSoftAssertions extends AbstractSoftAssertions { + public IntegerAssert expectThat(int value) { + return proxy(IntegerAssert.class, Integer.class, value); + } + + @SuppressWarnings("unchecked") + public ProxyableListAssert expectThat(List actual) { + return proxy(ProxyableListAssert.class, List.class, actual); + } +} \ No newline at end of file diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertionsExtensionIntegrationTest.java b/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertionsExtensionIntegrationTest.java index d3fbb717c61..3d84b82b260 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertionsExtensionIntegrationTest.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/CustomSoftAssertionsExtensionIntegrationTest.java @@ -16,11 +16,7 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD; import java.util.Arrays; -import java.util.List; -import org.assertj.core.api.AbstractSoftAssertions; -import org.assertj.core.api.IntegerAssert; -import org.assertj.core.api.ProxyableListAssert; import org.assertj.core.api.SoftAssertionsProvider; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; @@ -66,17 +62,6 @@ protected Class getTestInstancePerClassNestedTestCase() { return TestInstancePerClassNestedExample.class; } - private static class CustomSoftAssertions extends AbstractSoftAssertions { - public IntegerAssert expectThat(int value) { - return proxy(IntegerAssert.class, Integer.class, value); - } - - @SuppressWarnings("unchecked") - public ProxyableListAssert expectThat(List actual) { - return proxy(ProxyableListAssert.class, List.class, actual); - } - } - // ------------------------------------------------------------------------- @ExtendWith(SoftAssertionsExtension.class) diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/ExtensionInjector.java b/src/test/java/org/assertj/core/api/junit/jupiter/ExtensionInjector.java new file mode 100644 index 00000000000..d14262ecc14 --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/ExtensionInjector.java @@ -0,0 +1,34 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +class ExtensionInjector implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) throws ParameterResolutionException { + return parameterContext.getParameter().getType() == ExtensionContext.class; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) throws ParameterResolutionException { + return extensionContext; + } + +} \ No newline at end of file diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/InheritingSoftlyExtensionFieldTest.java b/src/test/java/org/assertj/core/api/junit/jupiter/InheritingSoftlyExtensionFieldTest.java index 3d115ce5632..4d95ec7c020 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/InheritingSoftlyExtensionFieldTest.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/InheritingSoftlyExtensionFieldTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +@SuppressWarnings("deprecation") @DisplayName("SoftlyExtension inheriting SoftAssertions field") class InheritingSoftlyExtensionFieldTest extends WithSoftlyExtension { diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java new file mode 100644 index 00000000000..f2be091a3cd --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java @@ -0,0 +1,136 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.testkit.engine.EventConditions.event; +import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; +import static org.junit.platform.testkit.engine.EventConditions.test; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf; +import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.AssertionErrorCollector; +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.assertj.core.api.BDDSoftAssertions; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.error.AssertJMultipleFailuresError; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.testkit.engine.EngineTestKit; + +/** + * Integration tests for the public API functions of {@link SoftAssertionsExtension}. + * + * @author Fr Jeremy Krieg + * @since 3.18 + */ +class SoftAssertionsExtensionAPIIntegrationTest { + + @Disabled("Executed via the JUnit Platform Test Kit") + @ExtendWith(ExtensionInjector.class) + @ExtendWith(SoftAssertionsExtension.class) + static class APITest { + + static Map map = new HashMap<>(); + + @BeforeAll + static void beforeAll() { + map.clear(); + } + + @BeforeEach + void beforeEach(ExtensionContext context) { + SoftAssertions provider = SoftAssertionsExtension.getSoftAssertionsProvider(context, SoftAssertions.class); + assertThat(provider.assertionErrorsCollected()).isEmpty(); + provider.assertThat("something").isEqualTo("nothing"); + assertThat(provider.assertionErrorsCollected()).as("beforeEach:after assert").hasSize(1); + AssertionErrorCollector collector = SoftAssertionsExtension.getAssertionErrorCollector(context); + assertThat(provider.getDelegate()).contains(collector); + map.put(context.getTestMethod().get().getName(), collector); + } + + @Test + void multipleFailuresCustom(ExtensionContext context, CustomSoftAssertions softly) { + AssertionErrorCollector collector = SoftAssertionsExtension.getAssertionErrorCollector(context); + assertThat(collector.assertionErrorsCollected()).as("init").hasSize(1); + softly.expectThat(1).isEqualTo(0); + assertThat(collector.assertionErrorsCollected()).as("after first").hasSize(2); + SoftAssertions provider = SoftAssertionsExtension.getSoftAssertionsProvider(context, SoftAssertions.class); + provider.assertThat(2).isEqualTo(2); + assertThat(collector.assertionErrorsCollected()).as("after second").hasSize(2); + provider.assertThat(2).isEqualTo(1); + assertThat(collector.assertionErrorsCollected()).as("after third").hasSize(3); + softly.expectThat(3).isEqualTo(4); + assertThat(collector.assertionErrorsCollected()).as("after fourth").hasSize(4); + } + + @Test + void multipleFailuresBDD(ExtensionContext context, BDDSoftAssertions softly) { + AssertionErrorCollector collector = SoftAssertionsExtension.getAssertionErrorCollector(context); + assertThat(collector.assertionErrorsCollected()).as("init").hasSize(1); + softly.then(1).isEqualTo(0); + assertThat(collector.assertionErrorsCollected()).as("after first").hasSize(2); + CustomSoftAssertions provider = SoftAssertionsExtension.getSoftAssertionsProvider(context, CustomSoftAssertions.class); + provider.expectThat(2).isEqualTo(2); + assertThat(collector.assertionErrorsCollected()).as("after second").hasSize(2); + softly.then(3).isEqualTo(4); + assertThat(collector.assertionErrorsCollected()).as("after third").hasSize(3); + } + + } + + @Test + void apiTest() { + EngineTestKit.engine("junit-jupiter") + .selectors(selectClass(APITest.class)) + .configurationParameter("junit.jupiter.conditions.deactivate", "*") + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(2).succeeded(0).failed(2)) + .failed() + // @format:off + .assertThatEvents().haveExactly(1, + event(test("multipleFailuresCustom"), + finishedWithFailure(instanceOf(AssertJMultipleFailuresError.class), + message(msg -> msg.contains("Multiple Failures (4 failures)"))))) + .haveExactly(1, + event(test("multipleFailuresBDD"), + finishedWithFailure(instanceOf(AssertJMultipleFailuresError.class), + message(msg -> msg.contains("Multiple Failures (3 failures)"))))); + // @format:on + try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { + List collected = APITest.map.get("multipleFailuresCustom").assertionErrorsCollected(); + softly.assertThat(collected).as("size").hasSize(4); + softly.assertThat(collected.get(0)).as("zero").hasMessageContaining("something").hasMessageContaining("nothing"); + softly.assertThat(collected.get(1)).as("one").hasMessageContaining("1").hasMessageContaining("0"); + softly.assertThat(collected.get(2)).as("two").hasMessageContaining("2").hasMessageContaining("1"); + softly.assertThat(collected.get(3)).as("three").hasMessageContaining("3").hasMessageContaining("4"); + + collected = APITest.map.get("multipleFailuresBDD").assertionErrorsCollected(); + softly.assertThat(collected).as("size2").hasSize(3); + softly.assertThat(collected.get(0)).as("zero2").hasMessageContaining("something").hasMessageContaining("nothing"); + softly.assertThat(collected.get(1)).as("one2").hasMessageContaining("1").hasMessageContaining("0"); + softly.assertThat(collected.get(2)).as("two2").hasMessageContaining("3").hasMessageContaining("4"); + } + } + +} diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java new file mode 100644 index 00000000000..2605a0be6d2 --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java @@ -0,0 +1,115 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static org.assertj.core.api.junit.jupiter.TestKitUtils.assertThatTest; + +import org.assertj.core.api.AbstractSoftAssertions; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.SoftAssertionsProvider; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; + +@DisplayName("SoftAssertionsExtension injection sanity checking test") +class SoftAssertionsExtension_InjectionSanityChecking_Test { + + @ExtendWith(SoftAssertionsExtension.class) + static private class TestBase { + @Test + void myTest() {} + } + + class NoDefaultSoftAssertionsProvider extends AbstractSoftAssertions { + public NoDefaultSoftAssertionsProvider(@SuppressWarnings("unused") String param) {} + } + + @Disabled("Run by the testkit") + static class NoDefaultConstructorTest extends TestBase { + @InjectSoftAssertions + NoDefaultSoftAssertionsProvider usp; + } + + @Test + void field_with_no_default_constructor_throws_exception() { + assertThatTest(NoDefaultConstructorTest.class).isInstanceOf(ExtensionConfigurationException.class) + .hasMessage("[usp] SoftAssertionsProvider [%s] does not have a default constructor", + NoDefaultSoftAssertionsProvider.class.getName()); + } + + static abstract class AbstractProvider implements SoftAssertionsProvider { + } + + @Disabled("Run by the testkit") + static class AbstractProviderTest extends TestBase { + @InjectSoftAssertions + AbstractProvider usp; + } + + @Test + void field_with_abstract_provider_throws_exception() { + assertThatTest(AbstractProviderTest.class).isInstanceOf(ExtensionConfigurationException.class) + .hasMessage("[usp] SoftAssertionsProvider [%s] is abstract and cannot be instantiated.", + AbstractProvider.class); + } + + @Disabled("Run by the testkit") + static class FinalField extends TestBase { + @InjectSoftAssertions + final SoftAssertions usp = null; + + @Override + @Test + void myTest() {} + } + + @Test + void final_field__throws_exception() { + assertThatTest(FinalField.class).isInstanceOf(ExtensionConfigurationException.class) + .hasMessage("[usp] SoftAssertionsProvider field must not be static or final."); + } + + @Disabled("Run by the testkit") + static class StaticField extends TestBase { + @InjectSoftAssertions + static SoftAssertions usp = null; + + @Override + @Test + void myTest() {} + } + + @Test + void static_field_throws_exception() { + assertThatTest(StaticField.class).isInstanceOf(ExtensionConfigurationException.class) + .hasMessage("[usp] SoftAssertionsProvider field must not be static or final."); + } + + @Disabled("Run by the testkit") + static class WrongType extends TestBase { + @InjectSoftAssertions + String usp; + + @Override + @Test + void myTest() {} + } + + @Test + void wrong_type_throws_exception() { + assertThatTest(WrongType.class).isInstanceOf(ExtensionConfigurationException.class) + .hasMessage("[usp] field is not a SoftAssertionsProvider (java.lang.String)."); + } +} diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_Injection_Test.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_Injection_Test.java new file mode 100644 index 00000000000..af215a38058 --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_Injection_Test.java @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.api.BDDSoftAssertions; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SoftAssertionsExtension.class) +@DisplayName("SoftAssertionsExtension injection test") +class SoftAssertionsExtension_Injection_Test { + + // use a mix of private and package-private here to test behaviour in the variety of different circumstances. + @InjectSoftAssertions + SoftAssertions softly; + + @InjectSoftAssertions + private BDDSoftAssertions deftly; + + SoftAssertions unannotated; + + @Test + void should_pass_if_not_null() { + assertThat(softly).isNotNull(); + } + + @Test + void bdd_should_pass_if_not_null() { + assertThat(deftly).isNotNull(); + assertThat(deftly.getDelegate().get()).isSameAs(softly.getDelegate().get()); + } + + @Test + void should_have_same_collector_as_parameter(CustomSoftAssertions custom) { + assertThat(custom.getDelegate().get()).isSameAs(softly.getDelegate().get()); + } + + @Test + void should_not_inject_into_unannotated_field() { + assertThat(unannotated).isNull(); + } + + @Nested + @ExtendWith(SoftAssertionsExtension.class) + @DisplayName("nested test class without SoftAssertions field") + class NestedMethodLifecycle { + + @Test + void should_use_parent_SoftAssertions_initialized_field() { + assertThat(softly).isNotNull(); + } + } + + @Nested + @ExtendWith(SoftAssertionsExtension.class) + @DisplayName("nested test class with SoftAssertions field") + class SoftlyNestedMethodLifecycle { + + @InjectSoftAssertions + SoftAssertions nestedSoftly; + + @Test + void should_use_own_SoftAssertions_initialized_field() { + assertThat(nestedSoftly).isNotNull(); + assertThat(nestedSoftly.getDelegate().get()).isSameAs(softly.getDelegate().get()); + } + + } +} diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java new file mode 100644 index 00000000000..76e6f517ef2 --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java @@ -0,0 +1,126 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import org.assertj.core.api.AssertionErrorCollector; +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.platform.testkit.engine.EngineTestKit; + +@DisplayName("SoftAssertionsExtension PER_CLASS concurrent injection test") +public class SoftAssertionsExtension_PER_CLASS_Concurrency_Test { + // Use CountDownLatches to synchronize between the two parallel running tests to make sure that they overlap in time. + @Disabled("Run by the testkit") + @ExtendWith(SoftAssertionsExtension.class) + @ExtendWith(ExtensionInjector.class) + @Execution(ExecutionMode.CONCURRENT) + @TestInstance(Lifecycle.PER_CLASS) + static class ConcurrencyTest { + + @InjectSoftAssertions + SoftAssertions softly; + + static CountDownLatch[] flags = new CountDownLatch[6]; + static Map map = new HashMap<>(); + + @BeforeAll + static void beforeAll() { + map.clear(); + for (int i = 0; i < flags.length; i++) { + flags[i] = new CountDownLatch(1); + } + } + + @BeforeEach + void beforeEach(ExtensionContext context) { + map.put(context.getTestMethod().get().getName(), SoftAssertionsExtension.getAssertionErrorCollector(context)); + } + + static void waitForFlag(int flagNum) { + try { + if (!flags[flagNum].await(5000, MILLISECONDS)) { + throw new IllegalStateException("Timed out while waiting for flag " + flagNum); + } + } catch (InterruptedException e) { + throw new IllegalStateException("Interrupted while waiting for flag " + flagNum, e); + } + } + + @Test + void test1(ExtensionContext context) { + softly.assertThat(1).isEqualTo(0); + flags[0].countDown(); + waitForFlag(1); + softly.assertThat(3).isEqualTo(4); + flags[2].countDown(); + waitForFlag(3); + softly.assertThat(5).isEqualTo(6); + map.put(context.getTestMethod().get().getName(), SoftAssertionsExtension.getAssertionErrorCollector(context)); + } + + @Test + void test2(@SuppressWarnings("unused") ExtensionContext context) { + waitForFlag(0); + softly.assertThat(2).isEqualTo(1); + flags[1].countDown(); + waitForFlag(2); + softly.assertThat(4).isEqualTo(5); + flags[3].countDown(); + } + } + + @Test + void concurrent_tests_with_explicit_per_class_annotation_do_not_interfere() { + EngineTestKit.engine("junit-jupiter") + .selectors(selectClass(ConcurrencyTest.class)) + .configurationParameter("junit.jupiter.conditions.deactivate", "*") + .configurationParameter("junit.jupiter.execution.parallel.enabled", "true") + .execute() + .testEvents() + .debug(System.err) + .assertStatistics(stats -> stats.started(2).succeeded(0).failed(2)) + .failed(); + + try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { + List collected = ConcurrencyTest.map.get("test1").assertionErrorsCollected(); + softly.assertThat(collected).as("size").hasSize(3); + softly.assertThat(collected.get(0)).as("zero").hasMessageContainingAll("1", "0"); + softly.assertThat(collected.get(1)).as("one").hasMessageContainingAll("3", "4"); + softly.assertThat(collected.get(2)).as("two").hasMessageContainingAll("5", "6"); + + collected = ConcurrencyTest.map.get("test2").assertionErrorsCollected(); + softly.assertThat(collected).as("size2").hasSize(2); + softly.assertThat(collected.get(0)).as("zero2").hasMessageContainingAll("2", "1"); + softly.assertThat(collected.get(1)).as("one2").hasMessageContainingAll("4", "5"); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Injection_Test.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Injection_Test.java new file mode 100644 index 00000000000..c1a8273f005 --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Injection_Test.java @@ -0,0 +1,90 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.api.BDDSoftAssertions; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; + +@ExtendWith(SoftAssertionsExtension.class) +@DisplayName("SoftAssertionsExtension PER_CLASS injection test") +@TestInstance(Lifecycle.PER_CLASS) +@Execution(ExecutionMode.SAME_THREAD) // Just in case we move to parallel threads in the future +class SoftAssertionsExtension_PER_CLASS_Injection_Test { + + // use mix of private and package-private here to test behaviour in the variety of different circumstances. + @InjectSoftAssertions + SoftAssertions softly; + + @InjectSoftAssertions + private BDDSoftAssertions deftly; + + SoftAssertions unannotated; + + @Test + void should_pass_if_not_null() { + assertThat(softly).isNotNull(); + } + + @Test + void bdd_should_pass_if_not_null() { + assertThat(deftly).isNotNull(); + assertThat(deftly.getDelegate().get()).isSameAs(softly.getDelegate().get()); + } + + @Test + void should_have_same_collector_as_parameter(CustomSoftAssertions custom) { + assertThat(custom.getDelegate().get()).isSameAs(softly.getDelegate().get()); + } + + @Test + void should_not_inject_into_unannotated_field() { + assertThat(unannotated).isNull(); + } + + @Nested + @ExtendWith(SoftAssertionsExtension.class) + @DisplayName("nested test class without SoftAssertions field") + class NestedMethodLifecycle { + + @Test + void should_use_parent_SoftAssertions_initialized_field() { + assertThat(softly).isNotNull(); + } + } + + @Nested + @ExtendWith(SoftAssertionsExtension.class) + @DisplayName("nested test class with SoftAssertions field") + class SoftlyNestedMethodLifecycle { + + @InjectSoftAssertions + SoftAssertions nestedSoftly; + + @Test + void should_use_own_SoftAssertions_initialized_field() { + assertThat(nestedSoftly).isNotNull(); + assertThat(nestedSoftly.getDelegate().get()).isSameAs(softly.getDelegate().get()); + } + + } +} diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyAssertionsExtensionIntegrationTest.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyAssertionsExtensionIntegrationTest.java index c610da53d38..76c1f5ff342 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyAssertionsExtensionIntegrationTest.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyAssertionsExtensionIntegrationTest.java @@ -39,6 +39,7 @@ /** * Integration tests for {@link SoftlyExtension}. */ +@SuppressWarnings("deprecation") @DisplayName("JUnit Jupiter SoftlyExtension integration tests") class SoftlyAssertionsExtensionIntegrationTest extends AbstractSoftAssertionsExtensionIntegrationTests { diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyExtensionTest.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyExtensionTest.java index f637c9b1e6f..09b2a8ed42c 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyExtensionTest.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftlyExtensionTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +@SuppressWarnings("deprecation") @ExtendWith(SoftlyExtension.class) @DisplayName("SoftlyExtension") class SoftlyExtensionTest { diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/TestKitUtils.java b/src/test/java/org/assertj/core/api/junit/jupiter/TestKitUtils.java new file mode 100644 index 00000000000..500fcc1ded1 --- /dev/null +++ b/src/test/java/org/assertj/core/api/junit/jupiter/TestKitUtils.java @@ -0,0 +1,76 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.junit.jupiter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.testkit.engine.EventType.FINISHED; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.assertj.core.api.AbstractThrowableAssert; +import org.junit.jupiter.engine.JupiterTestEngine; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.testkit.engine.EngineTestKit; +import org.junit.platform.testkit.engine.Event; + +public class TestKitUtils { + + private TestKitUtils() {} + + public static AbstractThrowableAssert assertThatTest(Class testClass, String... config) { + checkClass(testClass); + + Logger logger = Logger.getLogger("org.junit.jupiter"); + Level oldLevel = logger.getLevel(); + try { + // Suppress log output while the testkit is running + logger.setLevel(Level.OFF); + EngineTestKit.Builder builder = EngineTestKit.engine(new JupiterTestEngine()) + .selectors(selectClass(testClass)) + .configurationParameter("junit.jupiter.conditions.deactivate", "*"); + + if (config != null) { + if (config.length % 2 != 0) { + throw new IllegalStateException("Odd number of config parameters provided: " + Arrays.toString(config)); + } + for (int i = 0; i < config.length; i++) { + builder.configurationParameter(config[i++], config[i]); + } + } + + Event testEvent = builder.execute() + .allEvents() + .filter(event -> event.getType().equals(FINISHED)) + .findAny() + .orElseThrow(() -> new IllegalStateException("Test failed to run at all")); + + TestExecutionResult result = testEvent.getPayload(TestExecutionResult.class) + .orElseThrow(() -> new IllegalStateException("Test result payload missing")); + + return assertThat(result.getThrowable().orElse(null)); + } finally { + // Restore the filter to what it was so that we do not interfere with the parent test + logger.setLevel(oldLevel); + } + } + + private static void checkClass(Class testClass) { + // This is to protect against developer slip-ups that can be costly... + if (!Modifier.isStatic(testClass.getModifiers())) throw new IllegalStateException("Test class is not static: " + testClass); + } + +} diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/WithSoftlyExtension.java b/src/test/java/org/assertj/core/api/junit/jupiter/WithSoftlyExtension.java index 97fa9d3d969..e651ecf3a70 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/WithSoftlyExtension.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/WithSoftlyExtension.java @@ -15,6 +15,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.extension.ExtendWith; +@SuppressWarnings("deprecation") @ExtendWith(SoftlyExtension.class) class WithSoftlyExtension { From 4aca6e7d9a6dbc356357679d447c2e564a4770d6 Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Sun, 20 Sep 2020 12:10:24 +0200 Subject: [PATCH 015/191] Add assumptions it tests (#1989) --- pom.xml | 26 +++++++- src/it/junit4-with-opentest4j/pom.xml | 31 ++++++++++ .../JUnit4_with_opentest4j_Test.java | 52 ++++++++++++++++ src/it/settings.xml | 35 +++++++++++ src/it/setup/invoker.properties | 1 + src/it/setup/pom.xml | 59 +++++++++++++++++++ src/it/testng-with-junit4/pom.xml | 31 ++++++++++ .../assumptions/TestNG_with_JUnit4_Test.java | 52 ++++++++++++++++ 8 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/it/junit4-with-opentest4j/pom.xml create mode 100644 src/it/junit4-with-opentest4j/src/test/java/maven/invoker/it/assumptions/JUnit4_with_opentest4j_Test.java create mode 100644 src/it/settings.xml create mode 100644 src/it/setup/invoker.properties create mode 100644 src/it/setup/pom.xml create mode 100644 src/it/testng-with-junit4/pom.xml create mode 100644 src/it/testng-with-junit4/src/test/java/maven/invoker/it/assumptions/TestNG_with_JUnit4_Test.java diff --git a/pom.xml b/pom.xml index 42409a18e80..d717915f979 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ -html5 --allow-script-in-comments --no-module-directories 1.10.14 + 1.2.0 5.1.2 4.13 @@ -64,7 +65,7 @@ org.opentest4j opentest4j - 1.2.0 + ${opentest4j.version}
    @@ -587,6 +588,29 @@ + + org.apache.maven.plugins + maven-invoker-plugin + 3.2.1 + + ${project.build.directory}/it + src/it/settings.xml + ${project.build.directory}/local-repo + + clean + test + + + + + integration-test + + install + run + + + + diff --git a/src/it/junit4-with-opentest4j/pom.xml b/src/it/junit4-with-opentest4j/pom.xml new file mode 100644 index 00000000000..778367709b3 --- /dev/null +++ b/src/it/junit4-with-opentest4j/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + it + setup + 0 + + + junit4-with-opentest4j + + + + @project.groupId@ + @project.artifactId@ + test + + + junit + junit + test + + + org.opentest4j + opentest4j + test + + + + diff --git a/src/it/junit4-with-opentest4j/src/test/java/maven/invoker/it/assumptions/JUnit4_with_opentest4j_Test.java b/src/it/junit4-with-opentest4j/src/test/java/maven/invoker/it/assumptions/JUnit4_with_opentest4j_Test.java new file mode 100644 index 00000000000..8fa319f56e9 --- /dev/null +++ b/src/it/junit4-with-opentest4j/src/test/java/maven/invoker/it/assumptions/JUnit4_with_opentest4j_Test.java @@ -0,0 +1,52 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package maven.invoker.it.assumptions; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.BDDAssertions.then; + +import org.junit.AssumptionViolatedException; +import org.junit.Test; + +public class JUnit4_with_opentest4j_Test { + + @Test + public void should_throw_JUnit4_AssumptionViolatedException_when_assumption_fails() { + // WHEN + Throwable thrown = catchThrowable(() -> assumeThat(true).isFalse()); + // THEN + then(thrown).isInstanceOf(AssumptionViolatedException.class); + } + + @Test + public void should_not_have_TestNG_in_the_classpath() { + // WHEN/THEN + assertThatExceptionOfType(ClassNotFoundException.class).isThrownBy(() -> Class.forName("org.testng.SkipException")); + } + + @Test + public void should_have_JUnit_4_in_the_classpath() { + // WHEN/THEN + assertThatNoException().isThrownBy(() -> Class.forName("org.junit.AssumptionViolatedException")); + } + + @Test + public void should_have_opentest4j_in_the_classpath() { + // WHEN/THEN + assertThatNoException().isThrownBy(() -> Class.forName("org.opentest4j.TestAbortedException")); + } + +} diff --git a/src/it/settings.xml b/src/it/settings.xml new file mode 100644 index 00000000000..9675c2ec6bc --- /dev/null +++ b/src/it/settings.xml @@ -0,0 +1,35 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/src/it/setup/invoker.properties b/src/it/setup/invoker.properties new file mode 100644 index 00000000000..e513b1137f7 --- /dev/null +++ b/src/it/setup/invoker.properties @@ -0,0 +1 @@ +invoker.goals = install diff --git a/src/it/setup/pom.xml b/src/it/setup/pom.xml new file mode 100644 index 00000000000..f07ec2d3736 --- /dev/null +++ b/src/it/setup/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + it + setup + 0 + pom + + + UTF-8 + + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + junit + junit + @junit.version@ + + + org.opentest4j + opentest4j + @opentest4j.version@ + + + org.testng + testng + 7.3.0 + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @maven-compiler-plugin.version@ + + 8 + + + + org.apache.maven.plugins + maven-surefire-plugin + @maven-surefire-plugin.version@ + + + + + + diff --git a/src/it/testng-with-junit4/pom.xml b/src/it/testng-with-junit4/pom.xml new file mode 100644 index 00000000000..3bad016f1ac --- /dev/null +++ b/src/it/testng-with-junit4/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + it + setup + 0 + + + testng-with-junit4 + + + + @project.groupId@ + @project.artifactId@ + test + + + junit + junit + test + + + org.testng + testng + test + + + + diff --git a/src/it/testng-with-junit4/src/test/java/maven/invoker/it/assumptions/TestNG_with_JUnit4_Test.java b/src/it/testng-with-junit4/src/test/java/maven/invoker/it/assumptions/TestNG_with_JUnit4_Test.java new file mode 100644 index 00000000000..624cd9baf25 --- /dev/null +++ b/src/it/testng-with-junit4/src/test/java/maven/invoker/it/assumptions/TestNG_with_JUnit4_Test.java @@ -0,0 +1,52 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package maven.invoker.it.assumptions; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.BDDAssertions.then; + +import org.testng.SkipException; +import org.testng.annotations.Test; + +public class TestNG_with_JUnit4_Test { + + @Test + public void should_throw_TestNG_SkipException_when_assumption_fails() { + // WHEN + Throwable thrown = catchThrowable(() -> assumeThat(true).isFalse()); + // THEN + then(thrown).isInstanceOf(SkipException.class); + } + + @Test + public void should_have_TestNG_in_the_classpath() { + // WHEN/THEN + assertThatNoException().isThrownBy(() -> Class.forName("org.testng.SkipException")); + } + + @Test + public void should_have_JUnit_4_in_the_classpath() { + // WHEN/THEN + assertThatNoException().isThrownBy(() -> Class.forName("org.junit.AssumptionViolatedException")); + } + + @Test + public void should_not_have_opentest4j_in_the_classpath() { + // WHEN/THEN + assertThatExceptionOfType(ClassNotFoundException.class).isThrownBy(() -> Class.forName("org.opentest4j.TestAbortedException")); + } + +} From 67bd32b8dac0974360c215cebebd869ae774ff37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2020 08:18:12 +0200 Subject: [PATCH 016/191] Bump byte-buddy.version from 1.10.14 to 1.10.15 (#1998) Bumps `byte-buddy.version` from 1.10.14 to 1.10.15. Updates `byte-buddy` from 1.10.14 to 1.10.15 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.14...byte-buddy-1.10.15) Updates `byte-buddy-agent` from 1.10.14 to 1.10.15 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.14...byte-buddy-1.10.15) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d717915f979..bfa15be8c49 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ -html5 --allow-script-in-comments --no-module-directories - 1.10.14 + 1.10.15 1.2.0 5.1.2 From 87d5af3511a97bb7cc733703a5a5efe1958af554 Mon Sep 17 00:00:00 2001 From: Fr Jeremy Krieg Date: Mon, 21 Sep 2020 15:59:54 +0930 Subject: [PATCH 017/191] Attempted fix for flaky test (#1997) Attempted fix for #1996 --- ...sExtension_PER_CLASS_Concurrency_Test.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java index 76e6f517ef2..2e8d409b2dd 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_PER_CLASS_Concurrency_Test.java @@ -15,6 +15,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,7 +51,7 @@ static class ConcurrencyTest { SoftAssertions softly; static CountDownLatch[] flags = new CountDownLatch[6]; - static Map map = new HashMap<>(); + static Map map = Collections.synchronizedMap(new HashMap<>()); @BeforeAll static void beforeAll() { @@ -111,16 +112,23 @@ void concurrent_tests_with_explicit_per_class_annotation_do_not_interfere() { .failed(); try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { - List collected = ConcurrencyTest.map.get("test1").assertionErrorsCollected(); - softly.assertThat(collected).as("size").hasSize(3); - softly.assertThat(collected.get(0)).as("zero").hasMessageContainingAll("1", "0"); - softly.assertThat(collected.get(1)).as("one").hasMessageContainingAll("3", "4"); - softly.assertThat(collected.get(2)).as("two").hasMessageContainingAll("5", "6"); - - collected = ConcurrencyTest.map.get("test2").assertionErrorsCollected(); - softly.assertThat(collected).as("size2").hasSize(2); - softly.assertThat(collected.get(0)).as("zero2").hasMessageContainingAll("2", "1"); - softly.assertThat(collected.get(1)).as("one2").hasMessageContainingAll("4", "5"); + AssertionErrorCollector collector = ConcurrencyTest.map.get("test1"); + softly.assertThat(collector).as("test1").isNotNull(); + if (softly.wasSuccess()) { + List collected = collector.assertionErrorsCollected(); + softly.assertThat(collected).as("size").hasSize(3); + softly.assertThat(collected.get(0)).as("zero").hasMessageContainingAll("1", "0"); + softly.assertThat(collected.get(1)).as("one").hasMessageContainingAll("3", "4"); + softly.assertThat(collected.get(2)).as("two").hasMessageContainingAll("5", "6"); + } + collector = ConcurrencyTest.map.get("test2"); + softly.assertThat(collector).as("test2").isNotNull(); + if (softly.wasSuccess()) { + List collected = collector.assertionErrorsCollected(); + softly.assertThat(collected).as("size2").hasSize(2); + softly.assertThat(collected.get(0)).as("zero2").hasMessageContainingAll("2", "1"); + softly.assertThat(collected.get(1)).as("one2").hasMessageContainingAll("4", "5"); + } } } } \ No newline at end of file From 3c6bb8a5d4f452c60cf3c8e336a03feba5e9b207 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Mon, 21 Sep 2020 22:59:56 +1200 Subject: [PATCH 018/191] Minor readme update --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4df225710f6..3f9df43986f 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,11 @@ AssertJ provides a rich and intuitive set of strongly-typed assertions to use fo * [AssertJ's goals](#goals) * [Quick start](#quickstart) * [Latest News](#news) -* [Features highlight](http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html) (still in the old site but will soon be available in the [new site](https://assertj.github.io/doc/#overview)) +* AssertJ web site contains the [**AssertJ Core documentation**](https://assertj.github.io/doc/#assertj-core-assertions-guide). * [Assertions for custom types](http://joel-costigliola.github.io/assertj/assertj-core-custom-assertions.html) (still in the old site but will soon be available in the [new site](https://assertj.github.io/doc/#overview)) * [Replacing JUnit assertions with AssertJ Assertions](#junit-to-assertj-assertions) * [Contributing](#contributing) -The new AssertJ web site contains the [**AssertJ Core documentation**](https://assertj.github.io/doc/#assertj-core-assertions-guide). You can ask questions in [**stackoverflow (assertj tag)**](https://stackoverflow.com/questions/tagged/assertj?mixed=1) and make suggestions by simply creating an issue. From 45bbadd58b6e548ed2263e8c76faab5c4f44f774 Mon Sep 17 00:00:00 2001 From: Fr Jeremy Krieg Date: Tue, 22 Sep 2020 19:31:38 +0930 Subject: [PATCH 019/191] SonarCloud fixes for SoftAssertionsExtension (#1999) None of these were bugs in practice, but it is easy to keep SonarCloud happy so let's do it. --- .../core/api/junit/jupiter/SoftAssertionsExtension.java | 7 +++---- ...ftAssertionsExtension_InjectionSanityChecking_Test.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java b/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java index b2f5e7389ea..aacaf7a5c37 100644 --- a/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java +++ b/src/main/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension.java @@ -268,8 +268,8 @@ public void beforeEach(ExtensionContext context) throws Exception { tlec.setDelegate(collector); } else { // Make sure that all of the soft assertion provider instances have their delegate initialised to the assertion error - // collector for the current context. Also check parents (in the case of nested tests). - while (initialiseDelegate(context, collector)) { + // collector for the current context. Also check enclosing contexts (in the case of nested tests). + while (initialiseDelegate(context, collector) && context.getParent().isPresent()) { context = context.getParent().get(); } } @@ -324,8 +324,7 @@ public void afterTestExecution(ExtensionContext extensionContext) { AssertionErrorCollector collector; if (isPerClassConcurrent(extensionContext)) { ThreadLocalErrorCollector tlec = getThreadLocalCollector(extensionContext); - collector = tlec.getDelegate().get(); - // Clear the tlec just in case this thread gets re-used. + collector = tlec.getDelegate().orElseThrow(() -> new IllegalStateException("Expecting delegate to be present for current context")); tlec.reset(); } else { collector = getAssertionErrorCollector(extensionContext); diff --git a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java index 2605a0be6d2..a8a5840ee20 100644 --- a/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java +++ b/src/test/java/org/assertj/core/api/junit/jupiter/SoftAssertionsExtension_InjectionSanityChecking_Test.java @@ -27,7 +27,7 @@ class SoftAssertionsExtension_InjectionSanityChecking_Test { @ExtendWith(SoftAssertionsExtension.class) - static private class TestBase { + static abstract class TestBase { @Test void myTest() {} } From a619a163161052d13dc18acd8698c7d71376f6b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2020 22:01:58 +1200 Subject: [PATCH 020/191] Bump assertj-parent-pom from 2.2.7 to 2.2.8 (#2002) Bumps [assertj-parent-pom](https://github.com/assertj/assertj-maven-parent-pom) from 2.2.7 to 2.2.8. - [Release notes](https://github.com/assertj/assertj-maven-parent-pom/releases) - [Commits](https://github.com/assertj/assertj-maven-parent-pom/compare/assertj-parent-pom-2.2.7...assertj-parent-pom-2.2.8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bfa15be8c49..574e789175f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.assertj assertj-parent-pom - 2.2.7 + 2.2.8 From 710e5f7b681f83b0054887537c32dce1381effb1 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Mon, 21 Sep 2020 23:06:07 +1200 Subject: [PATCH 021/191] Prepare moving assertj-core to the assertj organisation --- CONTRIBUTING.md | 4 ++-- README.md | 20 +++++++++---------- pom.xml | 18 +++++------------ .../org/assertj/core/api/AbstractAssert.java | 2 +- .../core/api/filter/FilterOperator.java | 5 ++--- .../core/presentation/Representation.java | 2 +- .../convert-junit5-assertions-to-assertj.sh | 2 +- .../api/WithAssertions_delegation_Test.java | 4 ++-- ..._chained_after_superclass_method_Test.java | 2 +- ..._chained_after_superclass_method_Test.java | 2 +- ...ns_chained_after_base_assertions_Test.java | 2 +- .../ObjectArrayAssert_extracting_Test.java | 2 +- ...Strings_assertContainsOnlyDigits_Test.java | 2 +- .../core/perf/ContainsOnlyPerfTest.java | 2 +- ...rdRepresentation_iterable_format_Test.java | 2 +- 15 files changed, 31 insertions(+), 40 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f2d9fce376..12512a2e4ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ We appreciate your effort and to make sure that your pull request is easy to rev * Put GIVEN WHEN THEN steps in each test, favoring `BDDAssertions.then` instead of `Assertions.assertThat` for assertions in the THEN step. Steps can be combined or omitted if a separate step does not provide much benefit to test readability, just ensure that the WHEN step (either single or combined) contains the test target. * Use `AssertionUtil.expectAssertionError` for tests expecting to get an `AssertionError` - see `OptionalAssert_containsInstanceOf_Test` as an example.. * Use static import when it makes the code more readable. -* If possible, add a (fun) code example in [assertj-examples](https://github.com/joel-costigliola/assertj-examples) and use it in the javadoc. +* If possible, add a (fun) code example in [assertj-examples](https://github.com/assertj/assertj-examples) and use it in the javadoc. A good unit test to use as a reference is `OptionalAssert_containsInstanceOf_Test`. Here's a sample below: @@ -53,7 +53,7 @@ class OptionalAssert_containsInstanceOf_Test extends BaseTest { It's ok not to follow some of the rules described above if you have a good reason not to (use your best judgement) -[assertj-examples](https://github.com/joel-costigliola/assertj-examples) shows how to efficiently use AssertJ through fun unit test examples, it can be seen as AssertJs living documentation. +[assertj-examples](https://github.com/assertj/assertj-examples) shows how to efficiently use AssertJ through fun unit test examples, it can be seen as AssertJs living documentation. ## Rebase your PR on main (no merge!) diff --git a/README.md b/README.md index 3f9df43986f..92baa37079f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # AssertJ - Fluent assertions for java -[![Github CI status](https://github.com/joel-costigliola/assertj-core/workflows/CI/badge.svg)](https://github.com/joel-costigliola/assertj-core/actions?query=workflow%3ACI) -[![Github Cross-Version status](https://github.com/joel-costigliola/assertj-core/workflows/Cross-Version/badge.svg)](https://github.com/joel-costigliola/assertj-core/actions?query=workflow%3ACross-Version) +[![Github CI status](https://github.com/assertj/assertj-core/workflows/CI/badge.svg)](https://github.assertj-costigliola/assertj-core/actions?query=workflow%3ACI) +[![Github Cross-Version status](https://github.com/assertj/assertj-core/workflows/Cross-Version/badge.svg)](https://github.com/assertj/assertj-core/actions?query=workflow%3ACross-Version) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.assertj/assertj-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.assertj/assertj-core) [![Javadocs](http://www.javadoc.io/badge/org.assertj/assertj-core.svg)](http://www.javadoc.io/doc/org.assertj/assertj-core) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=joel-costigliola_assertj-core&metric=alert_status)](https://sonarcloud.io/dashboard?id=joel-costigliola_assertj-core) @@ -28,13 +28,13 @@ a `Map`? Use Map-specific assertions to easily check the contents of the map. AssertJ is composed of several modules: * A core module (this one) to provide assertions for JDK types (`String`, `Iterable`, `Stream`, `Path`, `File`, `Map`...) - see [AssertJ Core documentation](https://assertj.github.io/doc/#assertj-core-assertions-guide) and [javadoc](https://www.javadoc.io/doc/org.assertj/assertj-core/latest/index.html). -* A **[Guava module](https://github.com/joel-costigliola/assertj-guava#readme)** to provide assertions for Guava types (`Multimap`, `Optional`...) - see [AssertJ Guava documentation](http://joel-costigliola.github.io/assertj/assertj-guava.html) and [javadoc](http://joel-costigliola.github.io/assertj/guava/api/index.html). -* A **[Joda Time module](https://github.com/joel-costigliola/assertj-joda-time#readme)** to provide assertions for Joda Time types (`DateTime`, `LocalDateTime`) - see [AssertJ Joda Time documentation](http://joel-costigliola.github.io/assertj/assertj-joda-time.html) and [javadoc](http://joel-costigliola.github.io/assertj/jodatime/api/index.html). -* A **[Neo4J module](https://github.com/joel-costigliola/assertj-neo4j#readme)** to provide assertions for Neo4J types (`Path`, `Node`, `Relationship`...) - see [AssertJ Neo4J documentation](http://joel-costigliola.github.io/assertj/assertj-neo4j.html) and [javadoc](http://joel-costigliola.github.io/assertj/neo4j/api/index.html). -* A **[DB module](https://github.com/joel-costigliola/assertj-db#readme)** to provide assertions for relational database types (`Table`, `Row`, `Column`...) - see [AssertJ DB documentation](http://joel-costigliola.github.io/assertj/assertj-db.html) and [javadoc](http://joel-costigliola.github.io/assertj/db/current/api/index.html). -* A **[Swing module](https://github.com/joel-costigliola/assertj-swing#readme)** provides a simple and intuitive API for functional testing of Swing user interfaces - see [AssertJ Swing documentation](http://joel-costigliola.github.io/assertj/assertj-swing.html) and [javadoc](http://joel-costigliola.github.io/assertj/swing/api/index.html). +* A **[Guava module](https://github.com/assertj/assertj-guava#readme)** to provide assertions for Guava types (`Multimap`, `Optional`...) - see [AssertJ Guava documentation](https://assertj.github.io/doc/#assertj-guava) and [javadoc](https://www.javadoc.io/doc/org.assertj/assertj-guava/latest/index.html). +* A **[Joda Time module](https://github.com/assertj/assertj-joda-time#readme)** to provide assertions for Joda Time types (`DateTime`, `LocalDateTime`) - see [AssertJ Joda Time documentation](http://joel-costigliola.github.io/assertj/assertj-joda-time.html) and [javadoc](https://www.javadoc.io/doc/org.assertj/assertj-joda-time/latest/index.html). +* A **[Neo4J module](https://github.com/assertj/assertj-neo4j#readme)** to provide assertions for Neo4J types (`Path`, `Node`, `Relationship`...) - see [AssertJ Neo4J documentation](http://joel-costigliola.github.io/assertj/assertj-neo4j.html) and [javadoc](https://www.javadoc.io/doc/org.assertj/assertj-neo4j/latest/index.html). +* A **[DB module](https://github.com/assertj/assertj-db#readme)** to provide assertions for relational database types (`Table`, `Row`, `Column`...) - see [AssertJ DB documentation](https://assertj.github.io/doc/#assertj-db) and [javadoc](https://www.javadoc.io/doc/org.assertj/assertj-db/latest/index.html). +* A **[Swing module](https://github.com/assertj/assertj-swing#readme)** provides a simple and intuitive API for functional testing of Swing user interfaces - see [AssertJ Swing documentation](http://joel-costigliola.github.io/assertj/assertj-swing.html) and [javadoc](https://www.javadoc.io/doc/org.assertj/assertj-swing/latest/index.html). -Assertion missing? Please [create an issue](https://github.com/joel-costigliola/assertj-core/issues)! +Assertion missing? Please [create an issue](https://github.com/assertj/assertj-core/issues)! AssertJ's assertions are super easy to use: just type **```assertThat```** followed by the actual value in parentheses and a dot, then any Java IDE will show you all assertions available for the type of the object. No more confusion about the @@ -64,8 +64,8 @@ Moreover, to ease your work, we provide assertions generator that can take a set ## Replacing JUnit assertions with AssertJ Assertions -To help you [**replace JUnit assertions**](http://joel-costigliola.github.io/assertj/assertj-core-converting-junit-assertions-to-assertj.html) with AssertJ ones, you can use a [**script**](http://joel-costigliola.github.io/assertj/assertj-core-converting-junit-assertions-to-assertj.html#automatic-conversion) or do regexp search and replace manually as described [**here**](http://joel-costigliola.github.io/assertj/assertj-core-converting-junit-assertions-to-assertj.html#manual-conversion). +To help you [**replace JUnit assertions**](https://assertj.github.io/doc/#assertj-migration) with AssertJ ones, you can use a [**script**](https://assertj.github.io/doc/#assertj-migration-using-scripts) or do regexp search and replace manually as described [**here**](https://assertj.github.io/doc/#assertj-migration-using-regexes). ## Want to contribute? -You are encouraged to contribute any missing, useful assertions. To do so, please read the [contributing section](https://github.com/joel-costigliola/assertj-core/blob/main/CONTRIBUTING.md). +You are encouraged to contribute any missing, useful assertions. To do so, please read the [contributing section](https://github.com/assertj/assertj-core/blob/main/CONTRIBUTING.md). diff --git a/pom.xml b/pom.xml index 574e789175f..47fafcaa2d1 100644 --- a/pom.xml +++ b/pom.xml @@ -13,23 +13,15 @@ 2.2.8 - - - AssertJ Group - http://groups.google.com/group/assertj - http://groups.google.com/group/assertj - http://groups.google.com/group/assertj - - - scm:git:git@github.com:joel-costigliola/assertj-core.git - scm:git:git@github.com:joel-costigliola/assertj-core.git - git@github.com:joel-costigliola/assertj-core + scm:git:git@github.com:assertj/assertj-core.git + scm:git:git@github.com:assertj/assertj-core.git + git@github.com:assertj/assertj-core HEAD github - https://github.com/joel-costigliola/assertj-core/issues + https://github.com/assertj/assertj-core/issues @@ -510,7 +502,7 @@ en_US - + 8 ${project.build.directory}/javadoc-stylesheet/assertj-javadoc-min.css diff --git a/src/main/java/org/assertj/core/api/AbstractAssert.java b/src/main/java/org/assertj/core/api/AbstractAssert.java index 8026a0f6719..582dc66188b 100644 --- a/src/main/java/org/assertj/core/api/AbstractAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractAssert.java @@ -65,7 +65,7 @@ */ public abstract class AbstractAssert, ACTUAL> implements Assert { - // https://github.com/joel-costigliola/assertj-core/issues/1128 + // https://github.com/assertj/assertj-core/issues/1128 public static boolean throwUnsupportedExceptionOnEquals = true; private static final String ORG_ASSERTJ = "org.assert"; diff --git a/src/main/java/org/assertj/core/api/filter/FilterOperator.java b/src/main/java/org/assertj/core/api/filter/FilterOperator.java index fc51762915a..3c06001dc7d 100644 --- a/src/main/java/org/assertj/core/api/filter/FilterOperator.java +++ b/src/main/java/org/assertj/core/api/filter/FilterOperator.java @@ -14,12 +14,11 @@ public abstract class FilterOperator { - private static final String COMBINING_OPERATOR_IS_NOT_SUPPORTED = "Combining operator is not supported, but you can use Filters as in http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#filters"; + private static final String COMBINING_OPERATOR_IS_NOT_SUPPORTED = "Combining operator is not supported, but you can use Filters, see filteredOn methods in https://www.javadoc.io/doc/org.assertj/assertj-core/latest/org/assertj/core/api/AbstractIterableAssert.html"; protected final T filterParameter; protected FilterOperator(T filterValue) { - if (filterValue instanceof FilterOperator) - throw new UnsupportedOperationException(COMBINING_OPERATOR_IS_NOT_SUPPORTED); + if (filterValue instanceof FilterOperator) throw new UnsupportedOperationException(COMBINING_OPERATOR_IS_NOT_SUPPORTED); this.filterParameter = filterValue; } diff --git a/src/main/java/org/assertj/core/presentation/Representation.java b/src/main/java/org/assertj/core/presentation/Representation.java index 35d721b6d6c..b45630ae804 100644 --- a/src/main/java/org/assertj/core/presentation/Representation.java +++ b/src/main/java/org/assertj/core/presentation/Representation.java @@ -35,7 +35,7 @@ * {@link StandardRepresentation#fallbackToStringOf(Object)}. By doing this all the defaults of AssertJ would be applied and you can apply your own customization * *

    - * The assertj-examples project provides a working example of registering a custom representation. + * The assertj-examples project provides a working example of registering a custom representation. *

    * Registering a representation has been introduced in AssertJ 2.9.0/3.9.0. * diff --git a/src/main/scripts/convert-junit5-assertions-to-assertj.sh b/src/main/scripts/convert-junit5-assertions-to-assertj.sh index db5401bfe0e..1e71209b9af 100755 --- a/src/main/scripts/convert-junit5-assertions-to-assertj.sh +++ b/src/main/scripts/convert-junit5-assertions-to-assertj.sh @@ -6,7 +6,7 @@ function usage() { echo "convert-junit5-assertions-to-assertj.sh - Converts most of JUnit 5 assertions to AssertJ assertions" echo echo "AUTHOR" - echo "this script is based on JUnit 4 script found at http://joel-costigliola.github.io/assertj/assertj-core-converting-junit-assertions-to-assertj.html + echo "this script is based on JUnit 4 script found at https://assertj.github.io/doc/#assertj-migration The changes in regexps are mostly due to the fact the order arguments changed in JUnit 5 (i.e. message moved to the last position). Kudos to whoever wrote the original script!" echo diff --git a/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java b/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java index 41486d3c256..d47325603c2 100644 --- a/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java +++ b/src/test/java/org/assertj/core/api/WithAssertions_delegation_Test.java @@ -945,12 +945,12 @@ void withAssertions_assertThat_doublePredicate_Test() { @Test void withAssertions_assertThat_url_Test() throws MalformedURLException { - assertThat(new URL("https://github.com/joel-costigliola/assertj-core")).hasHost("github.com"); + assertThat(new URL("https://github.com/assertj/assertj-core")).hasHost("github.com"); } @Test void withAssertions_assertThat_uri_Test() { - assertThat(java.net.URI.create("https://github.com/joel-costigliola/assertj-core")).hasHost("github.com"); + assertThat(java.net.URI.create("https://github.com/assertj/assertj-core")).hasHost("github.com"); } @Test diff --git a/src/test/java/org/assertj/core/api/iterable/SetAssert_raw_set_assertions_chained_after_superclass_method_Test.java b/src/test/java/org/assertj/core/api/iterable/SetAssert_raw_set_assertions_chained_after_superclass_method_Test.java index 61201dd8b49..1072618e2a4 100644 --- a/src/test/java/org/assertj/core/api/iterable/SetAssert_raw_set_assertions_chained_after_superclass_method_Test.java +++ b/src/test/java/org/assertj/core/api/iterable/SetAssert_raw_set_assertions_chained_after_superclass_method_Test.java @@ -78,7 +78,7 @@ void raw_set_assertions_mixed_with_inherited_methods() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Test void test_bug_485() { - // https://github.com/joel-costigliola/assertj-core/issues/485 + // https://github.com/assertj/assertj-core/issues/485 Set set = new java.util.HashSet<>(); set.add("Key1"); set.add("Key2"); diff --git a/src/test/java/org/assertj/core/api/list/ListAssert_raw_list_assertions_chained_after_superclass_method_Test.java b/src/test/java/org/assertj/core/api/list/ListAssert_raw_list_assertions_chained_after_superclass_method_Test.java index 20a19a6a8df..64000b15522 100644 --- a/src/test/java/org/assertj/core/api/list/ListAssert_raw_list_assertions_chained_after_superclass_method_Test.java +++ b/src/test/java/org/assertj/core/api/list/ListAssert_raw_list_assertions_chained_after_superclass_method_Test.java @@ -80,7 +80,7 @@ void raw_list_assertions_mixed_with_inherited_methods() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Test void test_bug_485() { - // https://github.com/joel-costigliola/assertj-core/issues/485 + // https://github.com/assertj/assertj-core/issues/485 List list = new java.util.ArrayList<>(); list.add("Key1"); list.add("Key2"); diff --git a/src/test/java/org/assertj/core/api/map/MapAssert_raw_map_assertions_chained_after_base_assertions_Test.java b/src/test/java/org/assertj/core/api/map/MapAssert_raw_map_assertions_chained_after_base_assertions_Test.java index 6266d679b1a..530ce204d70 100644 --- a/src/test/java/org/assertj/core/api/map/MapAssert_raw_map_assertions_chained_after_base_assertions_Test.java +++ b/src/test/java/org/assertj/core/api/map/MapAssert_raw_map_assertions_chained_after_base_assertions_Test.java @@ -74,7 +74,7 @@ void raw_map_mixing_assertions_from_AbstractAssert_and_AbstractMapAssert() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Test void test_bug_485() { - // https://github.com/joel-costigliola/assertj-core/issues/485 + // https://github.com/assertj/assertj-core/issues/485 Map map1 = new java.util.HashMap<>(); map1.put("Key1", "Value1"); map1.put("Key2", "Value2"); diff --git a/src/test/java/org/assertj/core/api/objectarray/ObjectArrayAssert_extracting_Test.java b/src/test/java/org/assertj/core/api/objectarray/ObjectArrayAssert_extracting_Test.java index 46ba563eb74..6f6d235cd0c 100644 --- a/src/test/java/org/assertj/core/api/objectarray/ObjectArrayAssert_extracting_Test.java +++ b/src/test/java/org/assertj/core/api/objectarray/ObjectArrayAssert_extracting_Test.java @@ -270,7 +270,7 @@ void should_allow_assertions_on_more_than_five_extracted_values_from_given_itera tuple("Boromir", 37, MAN, "Boromir", 37, MAN)); } - // https://github.com/joel-costigliola/assertj-core/issues/880 + // https://github.com/assertj/assertj-core/issues/880 @Test void should_be_able_to_extract_values_returned_from_default_methods_from_given_iterable_elements() { List people = asList(new Person()); diff --git a/src/test/java/org/assertj/core/internal/strings/Strings_assertContainsOnlyDigits_Test.java b/src/test/java/org/assertj/core/internal/strings/Strings_assertContainsOnlyDigits_Test.java index f1a3ce3cef9..3d3bd9a29de 100644 --- a/src/test/java/org/assertj/core/internal/strings/Strings_assertContainsOnlyDigits_Test.java +++ b/src/test/java/org/assertj/core/internal/strings/Strings_assertContainsOnlyDigits_Test.java @@ -44,7 +44,7 @@ void should_fail_if_actual_contains_any_non_digit_character() { } /** - * See discussion on failing the assertion for empty CharSequence + * See discussion on failing the assertion for empty CharSequence */ @Test void should_fail_if_actual_is_empty() { diff --git a/src/test/java/org/assertj/core/perf/ContainsOnlyPerfTest.java b/src/test/java/org/assertj/core/perf/ContainsOnlyPerfTest.java index 4e8e32999b9..3bf06bfb5bb 100644 --- a/src/test/java/org/assertj/core/perf/ContainsOnlyPerfTest.java +++ b/src/test/java/org/assertj/core/perf/ContainsOnlyPerfTest.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.Timeout; /** - * See https://github.com/joel-costigliola/assertj-core/issues/1718. + * See https://github.com/assertj/assertj-core/issues/1718. * * This test ensures assertThat(list_of_1m_elements).containsOnly(...) is an O(N) rather than O(N^2) * operation. Given that the list has 1 million elements, O(N^2) is O(1000 billion), which should diff --git a/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java b/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java index 1c7469a7d5b..6ee9cb63884 100644 --- a/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java +++ b/src/test/java/org/assertj/core/presentation/StandardRepresentation_iterable_format_Test.java @@ -214,7 +214,7 @@ void should_only_consider_root_object_for_cycles() { then(formatted).isEqualTo("[[1, 2, 3], [1, 2, 3]]"); } - // see https://github.com/joel-costigliola/assertj-core/issues/1990 + // see https://github.com/assertj/assertj-core/issues/1990 @Test public void should_use_overridden_toString_over_iterable_representation() { // GIVEN From a68f0dbe1b1195583d20c3596198e0a5187d6136 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Tue, 22 Sep 2020 22:33:47 +1200 Subject: [PATCH 022/191] Fix warnings. --- .../org/assertj/core/api/filter/Filters.java | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/assertj/core/api/filter/Filters.java b/src/main/java/org/assertj/core/api/filter/Filters.java index c65a5477c4c..a050957b78e 100644 --- a/src/main/java/org/assertj/core/api/filter/Filters.java +++ b/src/main/java/org/assertj/core/api/filter/Filters.java @@ -12,10 +12,10 @@ */ package org.assertj.core.api.filter; +import static java.util.Objects.deepEquals; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; import static org.assertj.core.util.Lists.newArrayList; -import static org.assertj.core.util.Objects.areEqual; import static org.assertj.core.util.Preconditions.checkArgument; import java.util.List; @@ -36,21 +36,21 @@ *

     assertThat(filter(players).with("pointsPerGame").greaterThan(20)
      *                           .and("assistsPerGame").greaterThan(7).get())
      *                           .containsOnly(james, rose);
    - * + * * With {@link Condition} : - *
     List<Player> players = ...; 
    - *   
    + * 
     List<Player> players = ...;
    + *
      * Condition<Player> potentialMVP = new Condition<Player>("is a possible MVP"){
      *   public boolean matches(Player player) {
      *     return player.getPointsPerGame() > 20 && player.getAssistsPerGame() > 7;
      *   };
      * };
    - * 
    + *
      * // use filter static method to build Filters
      * assertThat(filter(players).being(potentialMVP).get()).containsOnly(james, rose);
    - * + * * @param the type of element of group to filter. - * + * * @author Joel Costigliola * @author Mikhail Mazursky */ @@ -78,22 +78,22 @@ public class Filters { * Note that the given {@link Iterable} is not modified, the filters are performed on a copy. *

    * With fluent filter language on element properties/fields : - *

     List<Player> players = ...; 
    -   *   
    +   * 
     List<Player> players = ...;
    +   *
        * assertThat(filter(players).with("pointsPerGame").greaterThan(20)
        *                           .and("assistsPerGame").greaterThan(7).get())
        *                           .containsOnly(james, rose);
    - * + * * With {@link Condition} : *
    
        *   public boolean matches(Player player) {
        *     return player.getPointsPerGame() > 20 && player.getAssistsPerGame() > 7;
        *   };
        * };
    -   * 
    +   *
        * // use filter static method to build Filters
        * assertThat(filter(players).being(potentialMVP).get()).containsOnly(james, rose);
    - * + * * @param the iterable elements type. * @param iterable the {@code Iterable} to filter. * @throws NullPointerException if the given iterable is {@code null}. @@ -112,22 +112,22 @@ public static Filters filter(Iterable iterable) { * Note that the given array is not modified, the filters are performed on an {@link Iterable} copy of the array. *

    * With {@link Condition} : - *

     Player[] players = ...; 
    -   *   
    +   * 
     Player[] players = ...;
    +   *
        * assertThat(filter(players).with("pointsPerGame").greaterThan(20)
        *                           .and("assistsPerGame").greaterThan(7).get())
        *                           .containsOnly(james, rose);
    - * + * * With {@link Condition} : *
     Condition<Player> potentialMVP = new Condition<Player>("is a possible MVP"){
        *   public boolean matches(Player player) {
        *     return player.getPointsPerGame() > 20 && player.getAssistsPerGame() > 7;
        *   };
        * };
    -   * 
    +   *
        * // use filter static method to build Filters
        * assertThat(filter(players).being(potentialMVP).get()).containsOnly(james, rose);
    - * + * * @param the array elements type. * @param array the array to filter. * @throws NullPointerException if the given array is {@code null}. @@ -150,18 +150,18 @@ private Filters(E[] array) { /** * Filter the underlying group, keeping only elements satisfying the given {@link Condition}.
    * Same as {@link #having(Condition)} - pick the method you prefer to have the most readable code. - * + * *
     List<Player> players = ...;
    -   *   
    +   *
        * Condition<Player> potentialMVP = new Condition<Player>("is a possible MVP") {
        *   public boolean matches(Player player) {
        *     return player.getPointsPerGame() > 20 && player.getAssistsPerGame() > 7;
        *   };
        * };
    -   * 
    +   *
        * // use filter static method to build Filters
        * assertThat(filter(players).being(potentialMVP).get()).containsOnly(james, rose);
    - * + * * @param condition the filter {@link Condition}. * @return this {@link Filters} to chain other filter operations. * @throws IllegalArgumentException if the given condition is {@code null}. @@ -173,18 +173,18 @@ public Filters being(Condition condition) { /** * Filter the underlying group, keeping only elements satisfying the given {@link Condition}.
    * Same as {@link #being(Condition)} - pick the method you prefer to have the most readable code. - * + * *
     List<Player> players = ...;
    -   *   
    +   *
        * Condition<Player> mvpStats = new Condition<Player>("is a possible MVP") {
        *   public boolean matches(Player player) {
        *     return player.getPointsPerGame() > 20 && player.getAssistsPerGame() > 7;
        *   };
        * };
    -   * 
    +   *
        * // use filter static method to build Filters
        * assertThat(filter(players).having(mvpStats).get()).containsOnly(james, rose);
    - * + * * @param condition the filter {@link Condition}. * @return this {@link Filters} to chain other filter operations. * @throws IllegalArgumentException if the given condition is {@code null}. @@ -204,10 +204,10 @@ private Filters applyFilterCondition(Condition condition) { *

    * Let's, for example, filter Employees with name "Alex" : *

     filter(employees).with("name", "Alex").get();
    - * + * * which is shortcut of : *
     filter(employees).with("name").equalsTo("Alex").get();
    - * + * * @param propertyOrFieldName the name of the property/field whose value will compared to given value. It may be a * nested property. * @param propertyValue the expected property value. @@ -228,7 +228,7 @@ public Filters with(String propertyOrFieldName, Object propertyValue) { *

    * The typical usage is to chain this call with a comparison method, for example : *

     filter(employees).with("name").equalsTo("Alex").get();
    - * + * * @param propertyOrFieldName the name of the property/field used for filtering. It may be a nested property. * @return this {@link Filters} to chain other filter operation. * @throws IllegalArgumentException if the given propertyOrFieldName is {@code null}. @@ -247,7 +247,7 @@ private void validatePropertyOrFieldName(String propertyOrFieldName) { /** * Alias of {@link #with(String)} for synthetic sugar to write things like : *
     filter(employees).with("name").equalsTo("Alex").and("job").notEqualsTo("lawyer").get();
    - * + * * @param propertyOrFieldName the name of the property/field used for filtering. It may be a nested property. * @return this {@link Filters} to chain other filter operation. * @throws IllegalArgumentException if the given propertyOrFieldName is {@code null}. @@ -262,7 +262,7 @@ public Filters and(String propertyOrFieldName) { *

    * Typical usage : *

     filter(employees).with("name").equalsTo("Luke").get();
    - * + * * @param propertyValue the filter value. * @return this {@link Filters} to chain other filter operation. * @throws IllegalArgumentException if the property name to filter on has not been set. @@ -271,7 +271,7 @@ public Filters equalsTo(Object propertyValue) { checkPropertyNameToFilterOnIsNotNull(); this.filteredIterable = filteredIterable.stream().filter(element -> { Object propertyValueOfCurrentElement = PROPERTY_OR_FIELD_SUPPORT.getValueOf(propertyOrFieldNameToFilterOn, element); - return areEqual(propertyValueOfCurrentElement, propertyValue); + return deepEquals(propertyValueOfCurrentElement, propertyValue); }).collect(toList()); return this; } @@ -283,7 +283,7 @@ public Filters equalsTo(Object propertyValue) { *

    * Typical usage : *

     filter(employees).with("name").notEqualsTo("Vader").get();
    - * + * * @param propertyValue the filter value. * @return this {@link Filters} to chain other filter operation. * @throws IllegalArgumentException if the property name to filter on has not been set. @@ -292,7 +292,7 @@ public Filters notEqualsTo(Object propertyValue) { checkPropertyNameToFilterOnIsNotNull(); this.filteredIterable = filteredIterable.stream().filter(element -> { Object propertyValueOfCurrentElement = PROPERTY_OR_FIELD_SUPPORT.getValueOf(propertyOrFieldNameToFilterOn, element); - return !areEqual(propertyValueOfCurrentElement, propertyValue); + return !deepEquals(propertyValueOfCurrentElement, propertyValue); }).collect(toList()); return this; } @@ -308,7 +308,7 @@ private void checkPropertyNameToFilterOnIsNotNull() { *

    * Typical usage : *

    filter(players).with("team").in("Bulls", "Lakers").get();
    - * + * * @param propertyValues the filter values. * @return this {@link Filters} to chain other filter operation. * @throws IllegalArgumentException if the property name to filter on has not been set. @@ -328,7 +328,7 @@ public Filters in(Object... propertyValues) { *

    * Typical usage : *

     filter(players).with("team").notIn("Heat", "Lakers").get();
    - * + * * @param propertyValues the filter values. * @return this {@link Filters} to chain other filter operation. * @throws IllegalArgumentException if the property name to filter on has not been set. @@ -344,20 +344,20 @@ public Filters notIn(Object... propertyValues) { /** * Returns true if given item is in given array, false otherwise. - * + * * @param item the object to look for in arrayOfValues * @param arrayOfValues the array of values * @return true if given item is in given array, false otherwise. */ private static boolean isItemInArray(Object item, Object[] arrayOfValues) { for (Object value : arrayOfValues) - if (areEqual(value, item)) return true; + if (deepEquals(value, item)) return true; return false; } /** * Returns the resulting filtered Iterable<E> (even if the constructor parameter type was an array). - * + * * @return the Iterable<E> containing the filtered elements. */ public List get() { From 1f51332ca752c77daa097403c6ac78b43e61979c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2020 19:29:29 +0200 Subject: [PATCH 023/191] Bump byte-buddy.version from 1.10.15 to 1.10.16 (#2004) Bumps `byte-buddy.version` from 1.10.15 to 1.10.16. Updates `byte-buddy` from 1.10.15 to 1.10.16 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.15...byte-buddy-1.10.16) Updates `byte-buddy-agent` from 1.10.15 to 1.10.16 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.15...byte-buddy-1.10.16) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47fafcaa2d1..11f73cf7cc3 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ -html5 --allow-script-in-comments --no-module-directories - 1.10.15 + 1.10.16 1.2.0 5.1.2 From 80bf6162125ccba64de1194143f50b357f7ffa7a Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Thu, 24 Sep 2020 03:51:24 +0200 Subject: [PATCH 024/191] Reduce visibility of FieldUtils as no method is publicly accessible --- .../java/org/assertj/core/util/introspection/FieldUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/assertj/core/util/introspection/FieldUtils.java b/src/main/java/org/assertj/core/util/introspection/FieldUtils.java index 44689d650f9..92d3fbd90b1 100644 --- a/src/main/java/org/assertj/core/util/introspection/FieldUtils.java +++ b/src/main/java/org/assertj/core/util/introspection/FieldUtils.java @@ -27,7 +27,7 @@ * The ability is provided to break the scoping restrictions coded by the programmer. This can allow fields to be * changed that shouldn't be. This facility should be used with care. */ -public class FieldUtils { +class FieldUtils { /** * Gets an accessible Field by name breaking scope if requested. Superclasses/interfaces will be From 12af4db3699c5908d5b83cb5a6e1e37d986fb3b3 Mon Sep 17 00:00:00 2001 From: epeee Date: Thu, 24 Sep 2020 19:44:26 +0200 Subject: [PATCH 025/191] Adjust workflows to assertj organization --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f59e74e5b56..dcd10b03be1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: sonar: name: Sonar code analysis runs-on: ubuntu-latest - if: github.repository == 'joel-costigliola/assertj-core' && github.event_name == 'push' + if: github.repository == 'assertj/assertj-core' && github.event_name == 'push' steps: - uses: actions/checkout@v2 with: From e936c83e0fb4ec84f19671f605b4b70685e6b778 Mon Sep 17 00:00:00 2001 From: epeee Date: Thu, 24 Sep 2020 21:36:13 +0200 Subject: [PATCH 026/191] Fix github ci status link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92baa37079f..5987fa00850 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AssertJ - Fluent assertions for java -[![Github CI status](https://github.com/assertj/assertj-core/workflows/CI/badge.svg)](https://github.assertj-costigliola/assertj-core/actions?query=workflow%3ACI) +[![Github CI status](https://github.com/assertj/assertj-core/workflows/CI/badge.svg)](https://github.com/assertj/assertj-core/actions?query=workflow%3ACI) [![Github Cross-Version status](https://github.com/assertj/assertj-core/workflows/Cross-Version/badge.svg)](https://github.com/assertj/assertj-core/actions?query=workflow%3ACross-Version) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.assertj/assertj-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.assertj/assertj-core) [![Javadocs](http://www.javadoc.io/badge/org.assertj/assertj-core.svg)](http://www.javadoc.io/doc/org.assertj/assertj-core) From e3f7b32b1174f9506d02a29bda850d11872ec41d Mon Sep 17 00:00:00 2001 From: epeee Date: Thu, 24 Sep 2020 21:48:17 +0200 Subject: [PATCH 027/191] Bump mockito.version from 3.5.10 to 3.5.13 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 11f73cf7cc3..d42c58b2a2d 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 4.13 5.6.2 - 3.5.10 + 3.5.13 0.8.6 From 9b2962864c8aac73829c5894a626cd87d1665363 Mon Sep 17 00:00:00 2001 From: epeee Date: Mon, 28 Sep 2020 18:23:23 +0200 Subject: [PATCH 028/191] Unnecessary semicolon --- .../core/api/iterable/IterableAssert_anySatisfy_Test.java | 2 +- .../java/org/assertj/core/api/recursive/comparison/Color.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/assertj/core/api/iterable/IterableAssert_anySatisfy_Test.java b/src/test/java/org/assertj/core/api/iterable/IterableAssert_anySatisfy_Test.java index 91b69bba83a..54ae194ae7d 100644 --- a/src/test/java/org/assertj/core/api/iterable/IterableAssert_anySatisfy_Test.java +++ b/src/test/java/org/assertj/core/api/iterable/IterableAssert_anySatisfy_Test.java @@ -27,7 +27,7 @@ class IterableAssert_anySatisfy_Test extends IterableAssertBaseTest { @BeforeEach void beforeOnce() { - restrictions = o -> assertThat(o).isNotNull();; + restrictions = o -> assertThat(o).isNotNull(); } @Override diff --git a/src/test/java/org/assertj/core/api/recursive/comparison/Color.java b/src/test/java/org/assertj/core/api/recursive/comparison/Color.java index 6dfcf5a8269..6127686e0d8 100644 --- a/src/test/java/org/assertj/core/api/recursive/comparison/Color.java +++ b/src/test/java/org/assertj/core/api/recursive/comparison/Color.java @@ -13,5 +13,5 @@ package org.assertj.core.api.recursive.comparison; enum Color { - RED, GREEN, BLUE; -} \ No newline at end of file + RED, GREEN, BLUE +} From 8f5e54618dd8c8ccd1f6e1c6afe3d164a3e49641 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 30 Sep 2020 15:54:28 +1300 Subject: [PATCH 029/191] Add isCloseTo for Duration. Fixes #1942 --- .../core/api/AbstractDurationAssert.java | 52 ++++++++ .../java/org/assertj/core/api/Assertions.java | 13 ++ .../org/assertj/core/api/WithAssertions.java | 13 ++ .../assertj/core/error/ShouldBeCloseTo.java | 12 +- .../DurationAssert_isCloseTo_Test.java | 125 ++++++++++++++++++ .../error/ShouldBeCloseTo_create_Test.java | 18 +++ 6 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java diff --git a/src/main/java/org/assertj/core/api/AbstractDurationAssert.java b/src/main/java/org/assertj/core/api/AbstractDurationAssert.java index dea9c9b811c..0830fc903a1 100644 --- a/src/main/java/org/assertj/core/api/AbstractDurationAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractDurationAssert.java @@ -12,16 +12,19 @@ */ package org.assertj.core.api; +import static org.assertj.core.error.ShouldBeCloseTo.shouldBeCloseTo; import static org.assertj.core.error.ShouldHaveDuration.shouldHaveDays; import static org.assertj.core.error.ShouldHaveDuration.shouldHaveHours; import static org.assertj.core.error.ShouldHaveDuration.shouldHaveMillis; import static org.assertj.core.error.ShouldHaveDuration.shouldHaveMinutes; import static org.assertj.core.error.ShouldHaveDuration.shouldHaveNanos; import static org.assertj.core.error.ShouldHaveDuration.shouldHaveSeconds; +import static org.assertj.core.util.Preconditions.checkArgument; import java.time.Duration; import org.assertj.core.internal.Failures; +import org.assertj.core.internal.Objects; /** * Assertions for {@link Duration} type. @@ -248,4 +251,53 @@ public SELF hasDays(long otherDays) { } return myself; } + + /** + * Verifies that the actual {@link Duration} is close to the given one within the given allowed difference (assertion succeeds if difference = allowed difference). + *

    + * This is equivalent of: {@code abs(actual - expected) <= allowed difference}. + *

    + * For readability you can use {@link Assertions#within(java.math.BigDecimal)} to express the allowed difference. + *

    + * Examples: + *

     Duration twoMinutes = Duration.ofMinutes(2);
    +   * // assertions succeed:
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), Duration.ofMinutes(5));
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), Duration.ofMinutes(10));
    +   *
    +   * // assertion succeeds when difference is exactly equals to the allowed difference
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), Duration.ofMinutes(1));
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), Duration.ofMinutes(5));
    +   *
    +   * // assertions using within syntactic sugar
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(5)));
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(1)));
    +   *
    +   * // assertions fail
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(5), within(Duration.ofMinutes(1)));
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), within(Duration.ofMinutes(4)));
    + * + * @param expected the given {@link Duration} to compare to actual. + * @param allowedDifference a positive {@link Duration} to express the maximum allowed difference. + * @return {@code this} assertion object. + * @throws IllegalArgumentException if the expected Duration is {@code null}. + * @throws IllegalArgumentException if the allowed difference Duration is {@code null} or negative. + * @throws AssertionError if the actual value is not close enough to the given one. + * @since 3.18.0 + */ + public SELF isCloseTo(Duration expected, Duration allowedDifference) { + Objects.instance().assertNotNull(info, actual); + checkArgument(expected != null, "expected duration should not be null"); + checkArgument(allowedDifference != null, "allowed difference duration should not be null"); + checkArgument(!allowedDifference.isNegative(), "allowed difference duration should be >= 0"); + if (absDiff(actual, expected).compareTo(allowedDifference) > 0) { + throw Failures.instance().failure(info, shouldBeCloseTo(actual, expected, allowedDifference, absDiff(actual, expected))); + } + return myself; + } + + private static Duration absDiff(Duration actual, Duration expected) { + return actual.minus(expected).abs(); + } + } diff --git a/src/main/java/org/assertj/core/api/Assertions.java b/src/main/java/org/assertj/core/api/Assertions.java index 1348fd49d3b..4851b7a891c 100644 --- a/src/main/java/org/assertj/core/api/Assertions.java +++ b/src/main/java/org/assertj/core/api/Assertions.java @@ -1941,6 +1941,19 @@ public static TemporalUnitOffset within(long value, TemporalUnit unit) { return new TemporalUnitWithinOffset(value, unit); } + /** + * Syntactic sugar method to use with {@link AbstractDurationAssert#isCloseTo(Duration, Duration)} assertion. + *

    + * Example: + *

     assertThat(Duration.ofMinutes(2)).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(1)));
    + * + * @param allowedDifference the allowed difference {@link Duration}. + * @return the given value. + */ + public static Duration within(Duration allowedDifference) { + return allowedDifference; + } + /** * Assertions entry point for Double {@link org.assertj.core.data.Percentage} to use with isCloseTo assertions for * percentages. diff --git a/src/main/java/org/assertj/core/api/WithAssertions.java b/src/main/java/org/assertj/core/api/WithAssertions.java index bfbbe82e79f..3ee45e47a35 100644 --- a/src/main/java/org/assertj/core/api/WithAssertions.java +++ b/src/main/java/org/assertj/core/api/WithAssertions.java @@ -1542,6 +1542,19 @@ default TemporalUnitOffset within(long value, TemporalUnit unit) { return Assertions.within(value, unit); } + /** + * Syntactic sugar method to use with {@link AbstractDurationAssert#isCloseTo(Duration, Duration)} assertion. + *

    + * Example: + *

     assertThat(Duration.ofMinutes(2)).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(1)));
    + * + * @param allowedDifference the allowed difference {@link Duration}. + * @return the given value. + */ + default Duration within(Duration allowedDifference) { + return allowedDifference; + } + /** * Assertions entry point for Double {@link org.assertj.core.data.Percentage} to use with isCloseTo assertions for * percentages. diff --git a/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java b/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java index 394a66ca71a..fe3eed42082 100644 --- a/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java +++ b/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java @@ -13,10 +13,10 @@ package org.assertj.core.error; import static java.lang.String.format; - import static org.assertj.core.util.DateUtil.formatAsDatetimeWithMs; import java.time.temporal.Temporal; +import java.time.temporal.TemporalAmount; import java.util.Date; /** @@ -51,6 +51,11 @@ public static ErrorMessageFactory shouldBeCloseTo(Temporal actual, Temporal othe return new ShouldBeCloseTo(actual, other, differenceDescription); } + public static ErrorMessageFactory shouldBeCloseTo(TemporalAmount actual, TemporalAmount other, TemporalAmount allowedDifference, + TemporalAmount difference) { + return new ShouldBeCloseTo(actual, other, allowedDifference, difference); + } + private ShouldBeCloseTo(Date actual, Date other, long deltaInMilliseconds, long difference) { // format Date up to the given ms, because default format is the second, thus dates with a difference less than 1s // seems equal in the error message. @@ -63,4 +68,9 @@ private ShouldBeCloseTo(Date actual, Date other, long deltaInMilliseconds, long private ShouldBeCloseTo(Temporal actual, Temporal other, String differenceDescription) { super(format("%nExpecting:%n <%s>%nto be close to:%n <%s>%n%s", actual, other, differenceDescription)); } + + private ShouldBeCloseTo(TemporalAmount actual, TemporalAmount other, TemporalAmount offset, TemporalAmount difference) { + super(format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin %s but difference was %s", actual, other, offset, + difference)); + } } diff --git a/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java b/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java new file mode 100644 index 00000000000..32b326fc3ab --- /dev/null +++ b/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java @@ -0,0 +1,125 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.BDDAssertions.thenIllegalArgumentException; +import static org.assertj.core.error.ShouldBeCloseTo.shouldBeCloseTo; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; +import static org.assertj.core.util.FailureMessages.actualIsNull; + +import java.time.Duration; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@DisplayName("DurationAssert isCloseTo") +class DurationAssert_isCloseTo_Test { + + @ParameterizedTest(name = "PT2M should close to {0} within {1}") + @CsvSource({ + "PT1M, PT70S", + "PT70S, PT1M", + "PT2M, PT0S", + "PT2M, PT1S", + "PT0M, PT2M", + "PT0M, PT3M", + "PT1M, PT3M", + "PT-1M, PT3M", + "PT-1M, PT4M", + "PT-10S, PT130S", + "PT-10S, PT131S", + "PT1M, PT70S" + }) + void should_pass_if_close_enough_to_the_given_duration(Duration expected, Duration allowedDifference) { + // GIVEN + Duration actual = Duration.ofMinutes(2); + // WHEN/THEN + assertThat(actual).isCloseTo(expected, allowedDifference) + .isCloseTo(expected, within(allowedDifference)); + } + + @ParameterizedTest(name = "PT2M should not be close to {0} within {1}") + @CsvSource({ + "PT1M, PT10S, PT1M", + "PT59S, PT1M, PT1M1S", + "PT1M, PT59S, PT1M", + "PT1M31S, PT10S, PT29S", + "PT0M, PT15S, PT2M" + }) + void should_fail_if_not_close_enough_to_the_given_duration(Duration expected, Duration allowedDifference, Duration difference) { + // GIVEN + Duration actual = Duration.ofMinutes(2); + // WHEN + AssertionError assertionError = expectAssertionError(() -> assertThat(actual).isCloseTo(expected, allowedDifference)); + // THEN + then(assertionError).hasMessage(shouldBeCloseTo(actual, expected, allowedDifference, difference).create()); + } + + @Test + void should_fail_when_actual_duration_is_null() { + // GIVEN + Duration actual = null; + Duration expected = Duration.ofDays(4); + Duration allowedDifference = Duration.ofDays(5); + // WHEN + AssertionError assertionError = expectAssertionError(() -> assertThat(actual).isCloseTo(expected, allowedDifference)); + // THEN + then(assertionError).hasMessage(actualIsNull()); + } + + @Test + void should_throw_IllegalArgumentException_when_expected_duration_is_null() { + // GIVEN + Duration actual = Duration.ofMinutes(2); + Duration expected = null; + Duration allowedDifference = Duration.ofDays(5); + // WHEN + ThrowingCallable code = () -> assertThat(actual).isCloseTo(expected, allowedDifference); + // THEN + thenIllegalArgumentException().isThrownBy(code) + .withMessage("expected duration should not be null"); + } + + @Test + void should_throw_IllegalArgumentException_when_allowed_difference_duration_is_null() { + // GIVEN + Duration actual = Duration.ofMinutes(2); + Duration expected = Duration.ofDays(5); + Duration allowedDifference = null; + // WHEN + ThrowingCallable code = () -> assertThat(actual).isCloseTo(expected, allowedDifference); + // THEN + thenIllegalArgumentException().isThrownBy(code) + .withMessage("allowed difference duration should not be null"); + } + + @Test + void should_throw_IllegalArgumentException_when_allowed_difference_duration_is_negative() { + // GIVEN + Duration actual = Duration.ofMinutes(2); + Duration expected = Duration.ofDays(5); + Duration allowedDifference = Duration.ofDays(-5); + // WHEN + ThrowingCallable code = () -> assertThat(actual).isCloseTo(expected, allowedDifference); + // THEN + thenIllegalArgumentException().isThrownBy(code) + .withMessage("allowed difference duration should be >= 0"); + } + +} diff --git a/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java b/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java index 1ca62236881..264445840ff 100644 --- a/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java +++ b/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java @@ -17,6 +17,8 @@ import static org.assertj.core.error.ShouldBeCloseTo.shouldBeCloseTo; import static org.assertj.core.util.DateUtil.parseDatetimeWithMs; +import java.time.Duration; + import org.assertj.core.description.Description; import org.assertj.core.description.TextDescription; import org.assertj.core.presentation.StandardRepresentation; @@ -41,4 +43,20 @@ void should_create_error_message_with_period_boundaries_included() { then(message).isEqualTo(format("[Test] %nExpecting:%n <2011-01-01T00:00:00.000>%nto be close to:%n <2011-01-01T00:00:00.101>%nby less than 100ms but difference was 101ms")); } + @Test + void should_create_error_message_with_TemporalAmount() { + // GIVEN + ErrorMessageFactory factory = shouldBeCloseTo(Duration.ofHours(1), Duration.ofHours(2), Duration.ofMinutes(10), + Duration.ofHours(1)); + // WHEN + String message = factory.create(new TextDescription("Test"), STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[Test] %n" + + "Expecting:%n" + + " %n" + + "to be close to:%n" + + " %n" + + "within PT10M but difference was PT1H")); + } + } From b30892c4df00cdcaca23b34f34481e3e3bd2615b Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 30 Sep 2020 15:56:10 +1300 Subject: [PATCH 030/191] Use two spaces indentation in error messages --- .../assertj/core/error/ShouldBeCloseTo.java | 6 ++-- ...AbstractTemporalAssert_isCloseTo_Test.java | 22 +++++++------- .../error/ShouldBeCloseTo_create_Test.java | 29 ++++++++++++++++--- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java b/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java index fe3eed42082..a3f31225dce 100644 --- a/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java +++ b/src/main/java/org/assertj/core/error/ShouldBeCloseTo.java @@ -61,12 +61,12 @@ private ShouldBeCloseTo(Date actual, Date other, long deltaInMilliseconds, long // seems equal in the error message. // Use standard formatting to avoid calling ToString.toStringOf for long that adds a 'L' (like 100L) to // differentiate integer from long (here there is no ambiguity). - super(format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nby less than %sms but difference was %sms", - formatAsDatetimeWithMs(actual), formatAsDatetimeWithMs(other), deltaInMilliseconds, difference)); + super(format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nby less than %sms but difference was %sms", + formatAsDatetimeWithMs(actual), formatAsDatetimeWithMs(other), deltaInMilliseconds, difference)); } private ShouldBeCloseTo(Temporal actual, Temporal other, String differenceDescription) { - super(format("%nExpecting:%n <%s>%nto be close to:%n <%s>%n%s", actual, other, differenceDescription)); + super(format("%nExpecting:%n <%s>%nto be close to:%n <%s>%n%s", actual, other, differenceDescription)); } private ShouldBeCloseTo(TemporalAmount actual, TemporalAmount other, TemporalAmount offset, TemporalAmount difference) { diff --git a/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java b/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java index 452aba1b980..e48ee392c98 100644 --- a/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java +++ b/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java @@ -111,21 +111,21 @@ public class AbstractTemporalAssert_isCloseTo_Test { }; private static String[] differenceMessages = { - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 50 Hours but difference was 96 Hours", - _2017_Mar_12_07_10_Instant, _2017_Mar_08_07_10_Instant), - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 50 Hours but difference was 96 Hours", + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 50 Hours but difference was 96 Hours", + _2017_Mar_12_07_10_Instant, _2017_Mar_08_07_10_Instant), + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 50 Hours but difference was 96 Hours", _2017_Mar_12_07_10, _2017_Mar_08_07_10), - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 3 Days but difference was 15 Days", + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 3 Days but difference was 15 Days", _2017_Mar_12, _2017_Mar_27), - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 5 Minutes but difference was 13 Minutes", _07_10, + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 5 Minutes but difference was 13 Minutes", _07_10, _07_23), - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 10 Minutes but difference was 13 Minutes", + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 10 Minutes but difference was 13 Minutes", OffsetDateTime.of(_2017_Mar_12_07_10, UTC), OffsetDateTime.of(_2017_Mar_12_07_23, UTC)), - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nby less than 95 Hours but difference was 95 Hours", + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nby less than 95 Hours but difference was 95 Hours", ZonedDateTime.of(_2017_Mar_12_07_10, ZoneId.of("America/New_York")), ZonedDateTime.of(_2017_Mar_08_07_10, ZoneId.of("America/New_York"))), - format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 2 Minutes but difference was 13 Minutes", + format("%nExpecting:%n <%s>%nto be close to:%n <%s>%nwithin 2 Minutes but difference was 13 Minutes", OffsetTime.of(_07_10, UTC), OffsetTime.of(_07_23, UTC)), }; @@ -210,7 +210,8 @@ private static TemporalUnitOffset inapplicableOffset(ArgumentsAccessor arguments @ParameterizedTest @MethodSource("parameters") public void should_fail_if_actual_is_null(ArgumentsAccessor args) { - assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> nullAssert(args).isCloseTo(closeTemporal(args), offset(args))) + assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> nullAssert(args).isCloseTo(closeTemporal(args), + offset(args))) .withMessage(actualIsNull()); } @@ -242,7 +243,8 @@ public void should_fail_when_offset_is_inapplicable(ArgumentsAccessor args) { if (inapplicableOffset != null) { assertThatExceptionOfType(UnsupportedTemporalTypeException.class).isThrownBy(() -> temporalAssert(args).isCloseTo(closeTemporal(args), inapplicableOffset)) - .withMessage("Unsupported unit: " + inapplicableOffset.getUnit()); + .withMessage("Unsupported unit: " + + inapplicableOffset.getUnit()); } } diff --git a/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java b/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java index 264445840ff..ac7f7dcfdab 100644 --- a/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java +++ b/src/test/java/org/assertj/core/error/ShouldBeCloseTo_create_Test.java @@ -15,13 +15,14 @@ import static java.lang.String.format; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.error.ShouldBeCloseTo.shouldBeCloseTo; +import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION; import static org.assertj.core.util.DateUtil.parseDatetimeWithMs; import java.time.Duration; +import java.time.LocalTime; import org.assertj.core.description.Description; import org.assertj.core.description.TextDescription; -import org.assertj.core.presentation.StandardRepresentation; import org.junit.jupiter.api.Test; /** @@ -38,9 +39,14 @@ void should_create_error_message_with_period_boundaries_included() { parseDatetimeWithMs("2011-01-01T00:00:00.101"), 100, 101); // WHEN - String message = factory.create(new TextDescription("Test"), new StandardRepresentation()); + String message = factory.create(new TextDescription("Test"), STANDARD_REPRESENTATION); // THEN - then(message).isEqualTo(format("[Test] %nExpecting:%n <2011-01-01T00:00:00.000>%nto be close to:%n <2011-01-01T00:00:00.101>%nby less than 100ms but difference was 101ms")); + then(message).isEqualTo(format("[Test] %n" + + "Expecting:%n" + + " <2011-01-01T00:00:00.000>%n" + + "to be close to:%n" + + " <2011-01-01T00:00:00.101>%n" + + "by less than 100ms but difference was 101ms")); } @Test @@ -58,5 +64,20 @@ void should_create_error_message_with_TemporalAmount() { " %n" + "within PT10M but difference was PT1H")); } - + + @Test + void should_create_error_message_with_Temporal() { + // GIVEN + ErrorMessageFactory factory = shouldBeCloseTo(LocalTime.of(13, 22, 37), LocalTime.of(13, 22, 32), "but difference was 5s"); + // WHEN + String message = factory.create(new TextDescription("Test"), STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[Test] %n" + + "Expecting:%n" + + " <13:22:37>%n" + + "to be close to:%n" + + " <13:22:32>%n" + + "but difference was 5s")); + } + } From c9b57150923677d6d57c4c64295a83c544c2088c Mon Sep 17 00:00:00 2001 From: kriegfrj Date: Wed, 23 Sep 2020 16:28:17 +0930 Subject: [PATCH 031/191] Add assertion factory methods to AbstractAssert. Fixes #2001. --- .../org/assertj/core/api/AbstractAssert.java | 72 +++++++++++++--- .../org/assertj/core/api/ConcreteAssert.java | 12 +++ ...Assert_failureWithActualExpected_Test.java | 86 +++++++++++++++++++ .../AbstractAssert_failure_Test.java | 66 ++++++++++++++ 4 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failureWithActualExpected_Test.java create mode 100644 src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failure_Test.java diff --git a/src/main/java/org/assertj/core/api/AbstractAssert.java b/src/main/java/org/assertj/core/api/AbstractAssert.java index 582dc66188b..4270f7a1a6e 100644 --- a/src/main/java/org/assertj/core/api/AbstractAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractAssert.java @@ -116,13 +116,35 @@ public WritableAssertionInfo getWritableAssertionInfo() { } /** - * Utility method to ease writing custom assertions classes using {@link String#format(String, Object...)} specifiers + * Throw an assertion error based on information in this assertion. Equivalent to: + *
    throw failure(errorMessage, arguments);
    + *

    + * This method is a thin wrapper around {@link #failure(String, Object...) failure()} - see that method for a more detailed + * description. + *

    + * Note that generally speaking, using {@link #failure(String, Object...) failure()} directly is preferable to using this + * wrapper method, as the compiler and other code analysis tools will be able to tell that the statement will never return + * normally and respond appropriately. + * + * @param errorMessage the error message to format + * @param arguments the arguments referenced by the format specifiers in the errorMessage string. + * @see #failWithActualExpectedAndMessage(Object, Object, String, Object...) + * @see #failure(String, Object...) + */ + protected void failWithMessage(String errorMessage, Object... arguments) { + throw failure(errorMessage, arguments); + } + + /** + * Generate a custom assertion error using the information in this assertion. + *

    + * This is a utility method to ease writing custom assertions classes using {@link String#format(String, Object...)} specifiers * in error message. *

    * Moreover, this method honors any description set with {@link #as(String, Object...)} or overridden error message * defined by the user with {@link #overridingErrorMessage(String, Object...)}. *

    - * Example : + * Example: *

     public TolkienCharacterAssert hasName(String name) {
        *   // check that actual TolkienCharacter we want to make assertions on is not null.
        *   isNotNull();
    @@ -138,9 +160,11 @@ public WritableAssertionInfo getWritableAssertionInfo() {
        *
        * @param errorMessage the error message to format
        * @param arguments the arguments referenced by the format specifiers in the errorMessage string.
    -   * @see #failWithActualExpectedAndMessage(Object, Object, String, Object...)
    +   * @see #failureWithActualExpected(Object, Object, String, Object...)
    +   * @see #failWithMessage(String, Object...)
    +   * @return The generated assertion error.
        */
    -  protected void failWithMessage(String errorMessage, Object... arguments) {
    +  protected AssertionError failure(String errorMessage, Object... arguments) {
         AssertionError assertionError = Failures.instance().failureIfErrorMessageIsOverridden(info);
         if (assertionError == null) {
           // error message was not overridden, build it.
    @@ -149,11 +173,35 @@ protected void failWithMessage(String errorMessage, Object... arguments) {
         }
         Failures.instance().removeAssertJRelatedElementsFromStackTraceIfNeeded(assertionError);
         removeCustomAssertRelatedElementsFromStackTraceIfNeeded(assertionError);
    -    throw assertionError;
    +    return assertionError;
       }
     
       /**
    -   * Utility method to ease writing custom assertions classes using {@link String#format(String, Object...)} specifiers
    +   * Throw an assertion error based on information in this assertion. Equivalent to:
    +   * 
    throw failureWithActualExpected(actual, expected, errorMessageFormat, arguments);
    + *

    + * This method is a thin wrapper around {@link #failureWithActualExpected(Object, Object, String, Object...) failureWithActualExpected()} - + * see that method for a more detailed description. Note that generally speaking, using + * {@link #failureWithActualExpected(Object, Object, String, Object...) failureWithActualExpected()} directly is + * preferable to using this wrapper method, as the compiler and other code analysis tools will be able to tell that the + * statement will never return normally and respond appropriately. + * + * @param actual the actual object that was found during the test + * @param expected the object that was expected + * @param errorMessageFormat the error message to format + * @param arguments the arguments referenced by the format specifiers in the errorMessage string. + * @see #failWithMessage(String, Object...) + * @see #failureWithActualExpected(Object, Object, String, Object...) + */ + protected void failWithActualExpectedAndMessage(Object actual, Object expected, String errorMessageFormat, + Object... arguments) { + throw failureWithActualExpected(actual, expected, errorMessageFormat, arguments); + } + + /** + * Generate a custom assertion error using the information in this assertion, using the given actual and expected values. + *

    + * This is a utility method to ease writing custom assertions classes using {@link String#format(String, Object...)} specifiers * in error message with actual and expected values. *

    * Moreover, this method honors any description set with {@link #as(String, Object...)} or overridden error message @@ -169,7 +217,7 @@ protected void failWithMessage(String errorMessage, Object... arguments) { * * // check condition * if (!actual.getName().equals(name)) { - * failWithActualExpectedAndMessage(actual.getName(), name, "Expected character's name to be <%s> but was <%s>", name, actual.getName()); + * throw failureWithActualExpected(actual.getName(), name, "Expected character's name to be <%s> but was <%s>", name, actual.getName()); * } * * // return the current assertion for method chaining @@ -180,17 +228,19 @@ protected void failWithMessage(String errorMessage, Object... arguments) { * @param expected the object that was expected * @param errorMessageFormat the error message to format * @param arguments the arguments referenced by the format specifiers in the errorMessage string. - * @see #failWithMessage(String, Object...) + * @return The generated assertion error. + * @see #failure(String, Object...) + * @see #failWithActualExpectedAndMessage(Object, Object, String, Object...) */ - protected void failWithActualExpectedAndMessage(Object actual, Object expected, String errorMessageFormat, - Object... arguments) { + protected AssertionError failureWithActualExpected(Object actual, Object expected, String errorMessageFormat, + Object... arguments) { String errorMessage = Optional.ofNullable(info.overridingErrorMessage()) .orElse(format(errorMessageFormat, arguments)); String description = MessageFormatter.instance().format(info.description(), info.representation(), errorMessage); AssertionError assertionError = assertionErrorCreator.assertionError(description, actual, expected); Failures.instance().removeAssertJRelatedElementsFromStackTraceIfNeeded(assertionError); removeCustomAssertRelatedElementsFromStackTraceIfNeeded(assertionError); - throw assertionError; + return assertionError; } /** diff --git a/src/test/java/org/assertj/core/api/ConcreteAssert.java b/src/test/java/org/assertj/core/api/ConcreteAssert.java index 8546c173995..37b4858a405 100644 --- a/src/test/java/org/assertj/core/api/ConcreteAssert.java +++ b/src/test/java/org/assertj/core/api/ConcreteAssert.java @@ -14,6 +14,7 @@ import org.assertj.core.internal.Objects; import org.assertj.core.util.VisibleForTesting; +import org.opentest4j.AssertionFailedError; /** * @author Alex Ruiz @@ -59,4 +60,15 @@ public void failWithActualExpectedAndMessage(Object actual, Object expected, Str super.failWithActualExpectedAndMessage(actual, expected, errorMessage, arguments); } + @VisibleForTesting + @Override + public AssertionError failure(String errorMessage, Object... arguments) { + return super.failure(errorMessage, arguments); + } + + @VisibleForTesting + @Override + public AssertionFailedError failureWithActualExpected(Object actual, Object expected, String errorMessage, Object... arguments) { + return (AssertionFailedError)super.failureWithActualExpected(actual, expected, errorMessage, arguments); + } } diff --git a/src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failureWithActualExpected_Test.java b/src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failureWithActualExpected_Test.java new file mode 100644 index 00000000000..b625880f997 --- /dev/null +++ b/src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failureWithActualExpected_Test.java @@ -0,0 +1,86 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.abstract_; + +import static org.assertj.core.api.BDDAssertions.then; + +import org.assertj.core.api.ConcreteAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +/** + * Tests for AbstractAssert#failureWithActualExpected(Object, Object, String, Object...). + * + * @author Joel Costigliola + * @author Fr Jeremy Krieg + */ +@DisplayName("AbstractAssert#failureWithActualExpected") +class AbstractAssert_failureWithActualExpected_Test { + + private ConcreteAssert assertion; + + private Object actual = "Actual"; + private Object expected = "Expected"; + + @BeforeEach + void setup() { + assertion = new ConcreteAssert("foo"); + } + + @Test + void should_create_failure_with_simple_message() { + // WHEN + AssertionFailedError afe = assertion.failureWithActualExpected(actual, expected, "fail"); + + // THEN + then(afe).hasMessage("fail"); + then(afe.getActual().getEphemeralValue()).isSameAs(actual); + then(afe.getExpected().getEphemeralValue()).isSameAs(expected); + } + + @Test + void should_create_failure_with_message_having_args() { + // WHEN + AssertionFailedError afe = assertion.failureWithActualExpected(actual, expected, "fail %d %s %%s", 5, "times"); + + // THEN + then(afe).hasMessage("fail 5 times %s"); + then(afe.getActual().getEphemeralValue()).isSameAs(actual); + then(afe.getExpected().getEphemeralValue()).isSameAs(expected); + } + + @Test + void should_keep_description_set_by_user() { + // WHEN + AssertionFailedError afe = assertion.as("user description") + .failureWithActualExpected(actual, expected, "fail %d %s", 5, "times"); + // THEN + then(afe).hasMessage("[user description] fail 5 times"); + then(afe.getActual().getEphemeralValue()).isSameAs(actual); + then(afe.getExpected().getEphemeralValue()).isSameAs(expected); + } + + @Test + void should_keep_specific_error_message_and_description_set_by_user() { + // WHEN + AssertionFailedError afe = assertion.as("test context") + .overridingErrorMessage("my %d errors %s", 5, "!") + .failureWithActualExpected(actual, expected, "%d %s", 5, "time"); + // THEN + then(afe).hasMessage("[test context] my 5 errors !"); + then(afe.getActual().getEphemeralValue()).isSameAs(actual); + then(afe.getExpected().getEphemeralValue()).isSameAs(expected); + } +} diff --git a/src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failure_Test.java b/src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failure_Test.java new file mode 100644 index 00000000000..3467b423dd3 --- /dev/null +++ b/src/test/java/org/assertj/core/api/abstract_/AbstractAssert_failure_Test.java @@ -0,0 +1,66 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.abstract_; + +import static org.assertj.core.api.BDDAssertions.then; + +import org.assertj.core.api.ConcreteAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("AbstractAssert#failure") +class AbstractAssert_failure_Test { + + private ConcreteAssert assertion; + + @BeforeEach + void setup() { + assertion = new ConcreteAssert("foo"); + } + + @Test + void should_create_failure_with_simple_message() { + // WHEN + AssertionError error = assertion.failure("fail"); + // THEN + then(error).hasMessage("fail"); + } + + @Test + void should_create_failure_with_message_having_args() { + // WHEN + AssertionError error = assertion.failure("fail %d %s", 5, "times"); + // THEN + then(error).hasMessage("fail 5 times"); + } + + @Test + void should_keep_description_set_by_user() { + // WHEN + AssertionError error = assertion.as("user description").failure("fail %d%% %s", 5, "of the times"); + // THEN + then(error).hasMessage("[user description] fail 5% of the times"); + } + + @Test + void should_keep_specific_error_message_and_description_set_by_user() { + // WHEN + AssertionError error = assertion.as("test context") + .overridingErrorMessage("my %d errors %s %%s", 5, "!") + .failure("%d %s", 5, "time"); + // THEN + then(error).hasMessage("[test context] my 5 errors ! %s"); + } + +} From ca806ce21d3d250be8c7af65229aacdf615ce711 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2020 13:34:10 +0200 Subject: [PATCH 032/191] Bump jackson-databind from 2.11.2 to 2.11.3 (#2008) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.11.2 to 2.11.3. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d42c58b2a2d..02402245cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ com.fasterxml.jackson.core jackson-databind - 2.11.2 + 2.11.3 test 4.13 5.6.2 From f9a1a16dd998a85d1ca56a6f58a4e682be68b4d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2020 16:38:47 +0200 Subject: [PATCH 049/191] Bump byte-buddy.version from 1.10.16 to 1.10.17 (#2015) Bumps `byte-buddy.version` from 1.10.16 to 1.10.17. Updates `byte-buddy` from 1.10.16 to 1.10.17 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.16...byte-buddy-1.10.17) Updates `byte-buddy-agent` from 1.10.16 to 1.10.17 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.16...byte-buddy-1.10.17) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59b67036cb2..d5efe22cb82 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ -html5 --allow-script-in-comments --no-module-directories - 1.10.16 + 1.10.17 1.2.0 5.2.0 From b4660c3a1e56d9e60973d159a1f0a11c277e92da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2020 09:48:56 +0200 Subject: [PATCH 050/191] Bump guava from 29.0-jre to 30.0-jre (#2021) Bumps [guava](https://github.com/google/guava) from 29.0-jre to 30.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5efe22cb82..2471dd46587 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ com.google.guava guava - 29.0-jre + 30.0-jre test From 5e5c33827062ac57a10114ddd0c4a17472222a46 Mon Sep 17 00:00:00 2001 From: BJ Hargrave Date: Mon, 19 Oct 2020 10:15:43 -0400 Subject: [PATCH 051/191] bnd: Clean up some pom dependencies now that Bnd 5.2.0 is being used (#2022) Now that Bnd 5.2.0 is released and being used by the project, we can clean up some pom dependencies which were necessary to avoid a bad interaction between Bnd and the enforcer plugin. Signed-off-by: BJ Hargrave --- pom.xml | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/pom.xml b/pom.xml index 2471dd46587..acb1572dbb9 100644 --- a/pom.xml +++ b/pom.xml @@ -87,30 +87,6 @@ junit-jupiter test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - org.junit.platform - junit-platform-engine - test - - - org.junit.platform - junit-platform-commons - provided - org.junit.vintage @@ -538,7 +514,7 @@ false false - false + true provided compile @@ -568,7 +544,7 @@ target/${project.build.finalName}-tests.jar false - false + true false provided From a8851f8bb7e45eccbd92e2c30beff9fca19881be Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Tue, 20 Oct 2020 23:30:50 +1300 Subject: [PATCH 052/191] rename within(Duration) to withMarginOf(Duration) --- .../org/assertj/core/api/AbstractDurationAssert.java | 10 +++++----- src/main/java/org/assertj/core/api/Assertions.java | 4 ++-- src/main/java/org/assertj/core/api/WithAssertions.java | 4 ++-- .../api/duration/DurationAssert_isCloseTo_Test.java | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/assertj/core/api/AbstractDurationAssert.java b/src/main/java/org/assertj/core/api/AbstractDurationAssert.java index 0830fc903a1..d37a99899d7 100644 --- a/src/main/java/org/assertj/core/api/AbstractDurationAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractDurationAssert.java @@ -257,7 +257,7 @@ public SELF hasDays(long otherDays) { *

    * This is equivalent of: {@code abs(actual - expected) <= allowed difference}. *

    - * For readability you can use {@link Assertions#within(java.math.BigDecimal)} to express the allowed difference. + * For readability you can use {@link Assertions#withMarginOf(Duration)} to express the allowed difference. *

    * Examples: *

     Duration twoMinutes = Duration.ofMinutes(2);
    @@ -270,12 +270,12 @@ public SELF hasDays(long otherDays) {
        * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), Duration.ofMinutes(5));
        *
        * // assertions using within syntactic sugar
    -   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(5)));
    -   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(1)));
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), withMarginOf(Duration.ofMinutes(5)));
    +   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), withMarginOf(Duration.ofMinutes(1)));
        *
        * // assertions fail
    -   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(5), within(Duration.ofMinutes(1)));
    -   * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), within(Duration.ofMinutes(4)));
    + * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(5), withMarginOf(Duration.ofMinutes(1))); + * assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), withMarginOf(Duration.ofMinutes(4)));
    * * @param expected the given {@link Duration} to compare to actual. * @param allowedDifference a positive {@link Duration} to express the maximum allowed difference. diff --git a/src/main/java/org/assertj/core/api/Assertions.java b/src/main/java/org/assertj/core/api/Assertions.java index 4851b7a891c..7c4056781db 100644 --- a/src/main/java/org/assertj/core/api/Assertions.java +++ b/src/main/java/org/assertj/core/api/Assertions.java @@ -1945,12 +1945,12 @@ public static TemporalUnitOffset within(long value, TemporalUnit unit) { * Syntactic sugar method to use with {@link AbstractDurationAssert#isCloseTo(Duration, Duration)} assertion. *

    * Example: - *

     assertThat(Duration.ofMinutes(2)).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(1)));
    + *
     assertThat(Duration.ofMinutes(2)).isCloseTo(Duration.ofMinutes(3), withMarginOf(Duration.ofMinutes(1)));
    * * @param allowedDifference the allowed difference {@link Duration}. * @return the given value. */ - public static Duration within(Duration allowedDifference) { + public static Duration withMarginOf(Duration allowedDifference) { return allowedDifference; } diff --git a/src/main/java/org/assertj/core/api/WithAssertions.java b/src/main/java/org/assertj/core/api/WithAssertions.java index 3ee45e47a35..73ccc2e488e 100644 --- a/src/main/java/org/assertj/core/api/WithAssertions.java +++ b/src/main/java/org/assertj/core/api/WithAssertions.java @@ -1546,12 +1546,12 @@ default TemporalUnitOffset within(long value, TemporalUnit unit) { * Syntactic sugar method to use with {@link AbstractDurationAssert#isCloseTo(Duration, Duration)} assertion. *

    * Example: - *

     assertThat(Duration.ofMinutes(2)).isCloseTo(Duration.ofMinutes(3), within(Duration.ofMinutes(1)));
    + *
     assertThat(Duration.ofMinutes(2)).isCloseTo(Duration.ofMinutes(3), withMarginOf(Duration.ofMinutes(1)));
    * * @param allowedDifference the allowed difference {@link Duration}. * @return the given value. */ - default Duration within(Duration allowedDifference) { + default Duration withMarginOf(Duration allowedDifference) { return allowedDifference; } diff --git a/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java b/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java index 32b326fc3ab..628e5fb7664 100644 --- a/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java +++ b/src/test/java/org/assertj/core/api/duration/DurationAssert_isCloseTo_Test.java @@ -13,7 +13,7 @@ package org.assertj.core.api.duration; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; +import static org.assertj.core.api.Assertions.withMarginOf; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.api.BDDAssertions.thenIllegalArgumentException; import static org.assertj.core.error.ShouldBeCloseTo.shouldBeCloseTo; @@ -31,7 +31,7 @@ @DisplayName("DurationAssert isCloseTo") class DurationAssert_isCloseTo_Test { - @ParameterizedTest(name = "PT2M should close to {0} within {1}") + @ParameterizedTest(name = "PT2M should close to {0} withMarginOf {1}") @CsvSource({ "PT1M, PT70S", "PT70S, PT1M", @@ -51,10 +51,10 @@ void should_pass_if_close_enough_to_the_given_duration(Duration expected, Durati Duration actual = Duration.ofMinutes(2); // WHEN/THEN assertThat(actual).isCloseTo(expected, allowedDifference) - .isCloseTo(expected, within(allowedDifference)); + .isCloseTo(expected, withMarginOf(allowedDifference)); } - @ParameterizedTest(name = "PT2M should not be close to {0} within {1}") + @ParameterizedTest(name = "PT2M should not be close to {0} withMarginOf {1}") @CsvSource({ "PT1M, PT10S, PT1M", "PT59S, PT1M, PT1M1S", From f8ba44c94ac0606936454b8e504360a972c7ca89 Mon Sep 17 00:00:00 2001 From: epeee Date: Tue, 20 Oct 2020 18:38:39 +0200 Subject: [PATCH 053/191] Bump mockito.version from 3.5.13 to 3.5.15 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index acb1572dbb9..ad23278610e 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 4.13 5.6.2 - 3.5.13 + 3.5.15 0.8.6 From 9774849fc5b13a2dd69ad6bddfae5a91f4943d9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2020 16:02:53 +0200 Subject: [PATCH 054/191] Bump equalsverifier from 3.4.3 to 3.5 (#2024) Bumps [equalsverifier](https://github.com/jqno/equalsverifier) from 3.4.3 to 3.5. - [Release notes](https://github.com/jqno/equalsverifier/releases) - [Changelog](https://github.com/jqno/equalsverifier/blob/main/CHANGELOG.md) - [Commits](https://github.com/jqno/equalsverifier/compare/equalsverifier-3.4.3...equalsverifier-3.5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad23278610e..308425defe0 100644 --- a/pom.xml +++ b/pom.xml @@ -167,7 +167,7 @@ nl.jqno.equalsverifier equalsverifier - 3.4.3 + 3.5 test From 1873ff0fbea12bea8a7e1546c734fc90ccd821e0 Mon Sep 17 00:00:00 2001 From: Matteo Mirk Date: Sat, 24 Oct 2020 23:42:05 +0200 Subject: [PATCH 055/191] Add hasPackage Class assertion (#2019) --- .../assertj/core/api/AbstractClassAssert.java | 60 ++++++++++++ .../assertj/core/error/ShouldHavePackage.java | 67 +++++++++++++ .../core/error/ShouldHaveSuperclass.java | 7 +- .../org/assertj/core/internal/Classes.java | 42 +++++++++ ...ssAssert_hasPackage_with_Package_Test.java | 42 +++++++++ ...assAssert_hasPackage_with_String_Test.java | 41 ++++++++ .../error/ShouldHavePackage_create_Test.java | 94 +++++++++++++++++++ ...es_assertHasPackage_with_Package_Test.java | 70 ++++++++++++++ ...ses_assertHasPackage_with_String_Test.java | 66 +++++++++++++ 9 files changed, 485 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/assertj/core/error/ShouldHavePackage.java create mode 100644 src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_Package_Test.java create mode 100644 src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_String_Test.java create mode 100644 src/test/java/org/assertj/core/error/ShouldHavePackage_create_Test.java create mode 100644 src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java create mode 100644 src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java diff --git a/src/main/java/org/assertj/core/api/AbstractClassAssert.java b/src/main/java/org/assertj/core/api/AbstractClassAssert.java index 33da1747ab0..b51c9a7ddfe 100644 --- a/src/main/java/org/assertj/core/api/AbstractClassAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractClassAssert.java @@ -647,4 +647,64 @@ public SELF hasPublicMethods(String... methodNames) { classes.assertHasPublicMethods(info, actual, methodNames); return myself; } + + /** + * Verifies that the actual {@code Class} has the given package name (as in {@link Class#getPackage()}). + * + *

    + * Example: + *

     package one.two;
    +   *
    +   * class MyClass {}
    +   *
    +   * // this assertions succeeds:
    +   * assertThat(MyClass.class).hasPackage("one.two");
    +   *
    +   * // these assertions fail:
    +   * assertThat(MyClass.class).hasPackage("one");
    +   * assertThat(MyClass.class).hasPackage("");
    +   * assertThat(MyClass.class).hasPackage("java.lang");
    + * + * @param packageName 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); + return myself; + } + + /** + * Verifies that the actual {@code Class} has the given package (as in {@link Class#getPackage()}). + * + *

    + * Example: + *

     package one.two;
    +   *
    +   * class MyClass {}
    +   **
    +   * // these assertions succeed:
    +   * assertThat(MyClass.class).hasPackage(Package.getPackage("one.two"));
    +   * assertThat(MyClass.class).hasPackage(MyClass.class.getPackage());
    +   *
    +   * // these assertions fail:
    +   * assertThat(MyClass.class).hasPackage(Package.getPackage("one"));
    +   * assertThat(MyClass.class).hasPackage(Package.getPackage(""));
    +   * assertThat(MyClass.class).hasPackage(Object.class.getPackage());
    + * + * @param aPackage 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 + */ + public SELF hasPackage(Package aPackage) { + classes.assertHasPackage(info, actual, aPackage); + return myself; + } + } diff --git a/src/main/java/org/assertj/core/error/ShouldHavePackage.java b/src/main/java/org/assertj/core/error/ShouldHavePackage.java new file mode 100644 index 00000000000..34a30fed4f1 --- /dev/null +++ b/src/main/java/org/assertj/core/error/ShouldHavePackage.java @@ -0,0 +1,67 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.error; + +import java.util.StringJoiner; + +/** + * Creates an error message indicating that a {@link Class} should have a given package. + * + * @author Matteo Mirk + */ +public class ShouldHavePackage extends BasicErrorMessageFactory { + private static final String SHOULD_HAVE_PACKAGE = new StringJoiner("%n", "%n", "").add("Expecting") + .add(" <%s>") + .add("to have package:") + .add(" <%s>") + .toString(); + private static final String BUT_HAD_NONE = new StringJoiner("%n", "%n", "").add("but had none.") + .toString(); + private static final String BUT_HAD = new StringJoiner("%n", "%n", "").add("but had:") + .add(" <%s>") + .toString(); + + /** + * Creates a new ShouldHavePackage with a {@link Package} instance. + * + * @param actual the actual value in the failed assertion. + * @param aPackage the expected package + * @return the created {@code ErrorMessageFactory}. + */ + public static ErrorMessageFactory shouldHavePackage(Class actual, Package aPackage) { + return shouldHavePackage(actual, aPackage.getName()); + } + + /** + * Creates a new ShouldHavePackage with a package name. + * + * @param actual the actual value in the failed assertion. + * @param packageName the expected package name + * @return the created {@code ErrorMessageFactory}. + */ + public static ErrorMessageFactory shouldHavePackage(Class actual, String packageName) { + final Package actualPackage = actual.getPackage(); + return (actualPackage == null) + ? new ShouldHavePackage(actual, packageName) + : new ShouldHavePackage(actual, packageName, actualPackage.getName()); + } + + private ShouldHavePackage(Class actual, String expectedPackage) { + super(SHOULD_HAVE_PACKAGE + BUT_HAD_NONE, actual, expectedPackage); + } + + private ShouldHavePackage(Class actual, String expectedPackage, String actualPackage) { + super(SHOULD_HAVE_PACKAGE + BUT_HAD, actual, expectedPackage, actualPackage); + } + +} diff --git a/src/main/java/org/assertj/core/error/ShouldHaveSuperclass.java b/src/main/java/org/assertj/core/error/ShouldHaveSuperclass.java index aac2a665849..2b14980fa17 100644 --- a/src/main/java/org/assertj/core/error/ShouldHaveSuperclass.java +++ b/src/main/java/org/assertj/core/error/ShouldHaveSuperclass.java @@ -43,10 +43,9 @@ public class ShouldHaveSuperclass extends BasicErrorMessageFactory { */ public static ErrorMessageFactory shouldHaveSuperclass(Class actual, Class superclass) { Class actualSuperclass = actual.getSuperclass(); - if (actualSuperclass == null) { - return new ShouldHaveSuperclass(actual, superclass); - } - return new ShouldHaveSuperclass(actual, superclass, actualSuperclass); + return (actualSuperclass == null) + ? new ShouldHaveSuperclass(actual, superclass) + : new ShouldHaveSuperclass(actual, superclass, actualSuperclass); } private ShouldHaveSuperclass(Class actual, Class expectedSuperclass) { diff --git a/src/main/java/org/assertj/core/internal/Classes.java b/src/main/java/org/assertj/core/internal/Classes.java index 0104aac2be1..31fa6e333a4 100644 --- a/src/main/java/org/assertj/core/internal/Classes.java +++ b/src/main/java/org/assertj/core/internal/Classes.java @@ -33,6 +33,7 @@ import static org.assertj.core.error.ShouldHaveNoFields.shouldHaveNoDeclaredFields; import static org.assertj.core.error.ShouldHaveNoFields.shouldHaveNoPublicFields; import static org.assertj.core.error.ShouldHaveNoSuperclass.shouldHaveNoSuperclass; +import static org.assertj.core.error.ShouldHavePackage.shouldHavePackage; import static org.assertj.core.error.ShouldHaveSuperclass.shouldHaveSuperclass; import static org.assertj.core.error.ShouldNotBeNull.shouldNotBeNull; import static org.assertj.core.error.ShouldOnlyHaveFields.shouldOnlyHaveDeclaredFields; @@ -581,4 +582,45 @@ private static void assertNotNull(AssertionInfo info, Class actual) { private static void classParameterIsNotNull(Class clazz) { requireNonNull(clazz, "The class to compare actual with should not be null"); } + + /** + * Verifies that the actual {@code Class} has the given {@code packageName}. + * + * @param info contains information about the assertion. + * @param actual the "actual" {@code Class}. + * @param packageName the package that must be declared in the class. + * @throws NullPointerException if {@code packageName} is {@code null}. + * @throws AssertionError if {@code actual} is {@code null}. + * @throws AssertionError if {@code actual} does not have the given package name. + */ + public void assertHasPackage(AssertionInfo info, Class actual, String packageName) { + assertNotNull(info, actual); + requireNonNull(packageName, shouldNotBeNull("packageName").create()); + Package actualPackage = actual.getPackage(); + + if (actualPackage == null || !actualPackage.getName().equals(packageName)) { + throw failures.failure(info, shouldHavePackage(actual, packageName)); + } + } + + /** + * Verifies that the actual {@code Class} has the given {@code Package}. + * + * @param info contains information about the assertion. + * @param actual the "actual" {@code Class}. + * @param aPackage the package that must be declared in the class. + * @throws NullPointerException if {@code aPackage} is {@code null}. + * @throws AssertionError if {@code actual} is {@code null}. + * @throws AssertionError if {@code actual} does not have the given package. + */ + public void assertHasPackage(AssertionInfo info, Class actual, Package aPackage) { + assertNotNull(info, actual); + requireNonNull(aPackage, shouldNotBeNull("aPackage").create()); + Package actualPackage = actual.getPackage(); + + if (!aPackage.equals(actualPackage)) { + throw failures.failure(info, shouldHavePackage(actual, aPackage)); + } + } + } diff --git a/src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_Package_Test.java b/src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_Package_Test.java new file mode 100644 index 00000000000..f96dbcf16a0 --- /dev/null +++ b/src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_Package_Test.java @@ -0,0 +1,42 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.classes; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.assertj.core.api.ClassAssert; +import org.assertj.core.api.ClassAssertBaseTest; +import org.junit.jupiter.api.DisplayName; + +/** + * Tests for {@link ClassAssert#hasPackage(Package)}. + * + * @author Matteo Mirk + */ +@DisplayName("ClassAssert hasPackage(Package)") +class ClassAssert_hasPackage_with_Package_Test extends ClassAssertBaseTest { + + private static final Package PACKAGE = mock(Package.class); + + @Override + protected ClassAssert invoke_api_method() { + return assertions.hasPackage(PACKAGE); + } + + @Override + protected void verify_internal_effects() { + verify(classes).assertHasPackage(getInfo(assertions), getActual(assertions), PACKAGE); + } + +} diff --git a/src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_String_Test.java b/src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_String_Test.java new file mode 100644 index 00000000000..5a2efe5203e --- /dev/null +++ b/src/test/java/org/assertj/core/api/classes/ClassAssert_hasPackage_with_String_Test.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.api.classes; + +import static org.mockito.Mockito.verify; + +import org.assertj.core.api.ClassAssert; +import org.assertj.core.api.ClassAssertBaseTest; +import org.junit.jupiter.api.DisplayName; + +/** + * Tests for {@link ClassAssert#hasPackage(String)}. + * + * @author Matteo Mirk + */ +@DisplayName("ClassAssert hasPackage(String)") +class ClassAssert_hasPackage_with_String_Test extends ClassAssertBaseTest { + + private static final String PACKAGE = "org.assertj.core.api"; + + @Override + protected ClassAssert invoke_api_method() { + return assertions.hasPackage(PACKAGE); + } + + @Override + protected void verify_internal_effects() { + verify(classes).assertHasPackage(getInfo(assertions), getActual(assertions), PACKAGE); + } + +} diff --git a/src/test/java/org/assertj/core/error/ShouldHavePackage_create_Test.java b/src/test/java/org/assertj/core/error/ShouldHavePackage_create_Test.java new file mode 100644 index 00000000000..8949fe47275 --- /dev/null +++ b/src/test/java/org/assertj/core/error/ShouldHavePackage_create_Test.java @@ -0,0 +1,94 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.error; + +import static java.lang.String.format; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.error.ShouldHavePackage.shouldHavePackage; +import static org.assertj.core.presentation.StandardRepresentation.STANDARD_REPRESENTATION; + +import java.util.Collection; + +import org.assertj.core.description.Description; +import org.assertj.core.internal.TestDescription; +import org.assertj.core.presentation.Representation; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link ShouldHavePackage#create(Description, Representation)}. + * + * @author Stefano Cordio + */ +@DisplayName("ShouldHavePackage create") +class ShouldHavePackage_create_Test { + + @Test + void should_create_error_message_with_String_if_actual_has_package() { + // WHEN + String message = shouldHavePackage(Object.class, "java.util").create(new TestDescription("TEST"), + STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[TEST] %n" + + "Expecting%n" + + " %n" + + "to have package:%n" + + " <\"java.util\">%n" + + "but had:%n" + + " <\"java.lang\">")); + } + + @Test + void should_create_error_message_with_String_if_actual_has_no_package() { + // WHEN + String message = shouldHavePackage(Object[].class, "java.util").create(new TestDescription("TEST"), + STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[TEST] %n" + + "Expecting%n" + + " %n" + + "to have package:%n" + + " <\"java.util\">%n" + + "but had none.")); + } + + @Test + void should_create_error_message_with_Package_if_actual_has_package() { + // WHEN + String message = shouldHavePackage(Object.class, Collection.class.getPackage()).create(new TestDescription("TEST"), + STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[TEST] %n" + + "Expecting%n" + + " %n" + + "to have package:%n" + + " <\"java.util\">%n" + + "but had:%n" + + " <\"java.lang\">")); + } + + @Test + void should_create_error_message_with_Package_if_actual_has_no_package() { + // WHEN + String message = shouldHavePackage(Object[].class, Collection.class.getPackage()).create(new TestDescription("TEST"), + STANDARD_REPRESENTATION); + // THEN + then(message).isEqualTo(format("[TEST] %n" + + "Expecting%n" + + " %n" + + "to have package:%n" + + " <\"java.util\">%n" + + "but had none.")); + } + +} diff --git a/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java new file mode 100644 index 00000000000..76a1796feb8 --- /dev/null +++ b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java @@ -0,0 +1,70 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.internal.classes; + +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.error.ShouldHavePackage.shouldHavePackage; +import static org.assertj.core.error.ShouldNotBeNull.shouldNotBeNull; +import static org.assertj.core.test.TestData.someInfo; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; +import static org.assertj.core.util.FailureMessages.actualIsNull; + +import java.util.Collection; + +import org.assertj.core.internal.ClassesBaseTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("Classes assertHasPackage(Package)") +class Classes_assertHasPackage_with_Package_Test extends ClassesBaseTest { + + @Test + void should_pass_if_actual_declares_given_package() { + // WHEN/THEN + classes.assertHasPackage(someInfo(), Object.class, Object.class.getPackage()); + } + + @Test + void should_fail_if_actual_is_null() { + // WHEN + AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), + null, + Object.class.getPackage())); + // THEN + then(assertionError).hasMessage(actualIsNull()); + } + + @Test + void should_fail_if_aPackage_is_null() { + // GIVEN + Package aPackage = null; + // WHEN + Throwable thrown = catchThrowable(() -> classes.assertHasPackage(someInfo(), Object.class, aPackage)); + // THEN + then(thrown).isInstanceOf(NullPointerException.class) + .hasMessage(shouldNotBeNull("aPackage").create()); + } + + @Test + void should_fail_if_package_does_not_match() { + // GIVEN + Class actual = Object.class; + Package aPackage = Collection.class.getPackage(); + // WHEN + AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), actual, aPackage)); + // THEN + then(assertionError).hasMessage(shouldHavePackage(actual, aPackage).create()); + } + +} diff --git a/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java new file mode 100644 index 00000000000..3e97328c04d --- /dev/null +++ b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java @@ -0,0 +1,66 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2020 the original author or authors. + */ +package org.assertj.core.internal.classes; + +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.error.ShouldHavePackage.shouldHavePackage; +import static org.assertj.core.error.ShouldNotBeNull.shouldNotBeNull; +import static org.assertj.core.test.TestData.someInfo; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; +import static org.assertj.core.util.FailureMessages.actualIsNull; + +import org.assertj.core.internal.ClassesBaseTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("Classes assertHasPackage(String)") +class Classes_assertHasPackage_with_String_Test extends ClassesBaseTest { + + @Test + void should_pass_if_actual_has_given_package_name() { + // WHEN/THEN + classes.assertHasPackage(someInfo(), Object.class, "java.lang"); + } + + @Test + void should_fail_if_actual_is_null() { + // WHEN + AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), null, "java.lang")); + // THEN + then(assertionError).hasMessage(actualIsNull()); + } + + @Test + void should_fail_if_packageName_is_null() { + // GIVEN + String packageName = null; + // WHEN + Throwable thrown = catchThrowable(() -> classes.assertHasPackage(someInfo(), Object.class, packageName)); + // THEN + then(thrown).isInstanceOf(NullPointerException.class) + .hasMessage(shouldNotBeNull("packageName").create()); + } + + @Test + void should_fail_if_packageName_does_not_match() { + // GIVEN + Class actual = Object.class; + String packageName = "java.util"; + // WHEN + AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), actual, packageName)); + // THEN + then(assertionError).hasMessage(shouldHavePackage(actual, packageName).create()); + } + +} From 73101fc8b46d3cb78feafc1b84d9840b0a752f08 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 25 Oct 2020 22:48:25 +1300 Subject: [PATCH 056/191] Prepare 3.18.0 version --- pom.xml | 2 +- verify.bndrun | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 308425defe0..7dd7bc17794 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd "> 4.0.0 assertj-core - 3.17.3-SNAPSHOT + 3.18.0-SNAPSHOT jar AssertJ fluent assertions Rich and fluent assertions for testing for Java diff --git a/verify.bndrun b/verify.bndrun index 753b7ec172e..e4e98625294 100644 --- a/verify.bndrun +++ b/verify.bndrun @@ -30,8 +30,8 @@ # The version ranges will change as the version of # AssertJ and/or its dependencies change. -runbundles: \ - assertj-core;version='[3.17.3,3.17.4)',\ - assertj-core-tests;version='[3.17.3,3.17.4)',\ + assertj-core;version='[3.18.0,3.18.1)',\ + assertj-core-tests;version='[3.18.0,3.18.1)',\ junit-jupiter-api;version='[5.6.2,5.6.3)',\ junit-jupiter-engine;version='[5.6.2,5.6.3)',\ junit-platform-commons;version='[1.6.2,1.6.3)',\ From 82da23e3c321e7bc5bcf04b2e697b3e23ff5c6c4 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 25 Oct 2020 22:56:54 +1300 Subject: [PATCH 057/191] [maven-release-plugin] prepare release assertj-core-3.18.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 7dd7bc17794..62a3174aa8b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,8 @@ - + 4.0.0 assertj-core - 3.18.0-SNAPSHOT + 3.18.0 jar AssertJ fluent assertions Rich and fluent assertions for testing for Java @@ -17,7 +16,7 @@ scm:git:git@github.com:assertj/assertj-core.git scm:git:git@github.com:assertj/assertj-core.git git@github.com:assertj/assertj-core - HEAD + assertj-core-3.18.0 github From 65c7cc8bf11371ff0867f1899446b35a5e45fd7b Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 25 Oct 2020 22:57:05 +1300 Subject: [PATCH 058/191] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 62a3174aa8b..b524f808799 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 assertj-core - 3.18.0 + 3.18.1-SNAPSHOT jar AssertJ fluent assertions Rich and fluent assertions for testing for Java @@ -16,7 +16,7 @@ scm:git:git@github.com:assertj/assertj-core.git scm:git:git@github.com:assertj/assertj-core.git git@github.com:assertj/assertj-core - assertj-core-3.18.0 + HEAD github From 7462717adb5b5a8b2a476495cbcdce0cde2ec563 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 28 Oct 2020 16:32:25 +1300 Subject: [PATCH 059/191] Fix javadoc deprecation alternative --- .../org/assertj/core/api/AbstractCompletableFutureAssert.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java b/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java index c4da6d6d786..a2131d33bc9 100644 --- a/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractCompletableFutureAssert.java @@ -335,7 +335,7 @@ public SELF hasFailed() { *

    * Use matches with the following combination instead: * - *

     assertThat(future).matches (f -> f.isCompletedExceptionally() {@literal &&} !f.isCancelled());
    + *
     assertThat(future).matches (f -> f.isNotCompletedExceptionally() {@literal ||} f.isCancelled());
    * * This assertion is deprecated because its semantic is not obvious. *

    From edf25de52b7c587adb636566b1515fc501e35465 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 28 Oct 2020 17:20:58 +1300 Subject: [PATCH 060/191] Add javadoc for methods that needs to be implemented if one subclass AbstractIterableAssert --- .../core/api/AbstractIterableAssert.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/assertj/core/api/AbstractIterableAssert.java b/src/main/java/org/assertj/core/api/AbstractIterableAssert.java index aa865175e49..2023cb3c1d3 100644 --- a/src/main/java/org/assertj/core/api/AbstractIterableAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractIterableAssert.java @@ -2958,6 +2958,17 @@ private ELEMENT_ASSERT internalSingleElement() { return internalFirst(); } + /** + * This method is used in navigating assertions like {@link #first()}, {@link #last()} and {@link #element(int)} to build the + * assertion for the given element navigated to. + *

    + * Typical implementation is returning an {@link ObjectAssert} but it is possible to return a more specialized assertions + * should you know what type of elements the iterables contain. + * + * @param value the element value + * @param description describes the element, ex: "check first element" for {@link #first()}, used in assertion description. + * @return the assertion for the given element + */ protected abstract ELEMENT_ASSERT toAssert(ELEMENT value, String description); protected String navigationDescription(String propertyName) { @@ -3308,7 +3319,14 @@ protected TypeComparators getComparatorsForElementPropertyOrFieldTypes() { return comparatorsForElementPropertyOrFieldTypes; } - // use to build the assert instance with a filtered iterable + /** + * This methods is needed to build a new concrete instance of AbstractIterableAssert after a filtering operation is executed. + *

    + * If you create your own subclass of AbstractIterableAssert, simply returns an instance of it in this method. + * + * @param iterable the iterable used to build the concrete instance of AbstractIterableAssert + * @return concrete instance of AbstractIterableAssert + */ protected abstract SELF newAbstractIterableAssert(Iterable iterable); @SuppressWarnings({ "rawtypes", "unchecked" }) From 928c1e3f627dbfff1a5528ee3ce4488d2dadd6df Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Wed, 28 Oct 2020 17:22:48 +1300 Subject: [PATCH 061/191] Try to fix flaky test on windows --- .../CompletableFutureAssert_succeedsWithin_duration_Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/assertj/core/api/future/CompletableFutureAssert_succeedsWithin_duration_Test.java b/src/test/java/org/assertj/core/api/future/CompletableFutureAssert_succeedsWithin_duration_Test.java index 073a98c8175..27a422cf14f 100644 --- a/src/test/java/org/assertj/core/api/future/CompletableFutureAssert_succeedsWithin_duration_Test.java +++ b/src/test/java/org/assertj/core/api/future/CompletableFutureAssert_succeedsWithin_duration_Test.java @@ -50,7 +50,7 @@ void should_allow_assertion_on_future_result_when_completed_normally_within_time CompletableFuture future = completedFutureAfter(value, sleepDuration); // WHEN/THEN // using the same duration would fail depending on when the thread executing the future is started - assertThat(future).succeedsWithin(Duration.ofMillis(sleepDuration + 100)) + assertThat(future).succeedsWithin(Duration.ofMillis(sleepDuration + 500)) .isEqualTo(value); } From 4ddf78bddd52be9adbf34ebab715911f4e716319 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sat, 31 Oct 2020 18:29:40 +1300 Subject: [PATCH 062/191] Fix some warnings --- .../core/api/AbstractTemporalAssert_isCloseTo_Test.java | 2 -- .../api/Assertions_as_with_InstanceOfAssertFactory_Test.java | 1 - .../api/Assertions_assertThat_with_CompletionStage_Test.java | 4 +++- src/test/java/org/assertj/core/api/SoftAssertionsTest.java | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java b/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java index e48ee392c98..92b1dad6e60 100644 --- a/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java +++ b/src/test/java/org/assertj/core/api/AbstractTemporalAssert_isCloseTo_Test.java @@ -169,12 +169,10 @@ public static Object[][] parameters() { return parameters; } - @SuppressWarnings("unchecked") private static AbstractTemporalAssert nullAssert(ArgumentsAccessor arguments) { return arguments.get(0, AbstractTemporalAssert.class); } - @SuppressWarnings("unchecked") private static AbstractTemporalAssert temporalAssert(ArgumentsAccessor arguments) { return arguments.get(1, AbstractTemporalAssert.class); } diff --git a/src/test/java/org/assertj/core/api/Assertions_as_with_InstanceOfAssertFactory_Test.java b/src/test/java/org/assertj/core/api/Assertions_as_with_InstanceOfAssertFactory_Test.java index 5f173e86bf8..9458ae0003e 100644 --- a/src/test/java/org/assertj/core/api/Assertions_as_with_InstanceOfAssertFactory_Test.java +++ b/src/test/java/org/assertj/core/api/Assertions_as_with_InstanceOfAssertFactory_Test.java @@ -25,7 +25,6 @@ class Assertions_as_with_InstanceOfAssertFactory_Test { @Test - @SuppressWarnings("unchecked") void should_return_the_given_assert_factory() { // GIVEN InstanceOfAssertFactory> assertFactory = mock(InstanceOfAssertFactory.class); diff --git a/src/test/java/org/assertj/core/api/Assertions_assertThat_with_CompletionStage_Test.java b/src/test/java/org/assertj/core/api/Assertions_assertThat_with_CompletionStage_Test.java index 55f4b6a0c42..80785cf9b19 100644 --- a/src/test/java/org/assertj/core/api/Assertions_assertThat_with_CompletionStage_Test.java +++ b/src/test/java/org/assertj/core/api/Assertions_assertThat_with_CompletionStage_Test.java @@ -40,7 +40,9 @@ void should_create_Assert() { void should_initialise_actual() { CompletableFuture actual = assertThat(completionStage).actual; assertThat(actual).isDone() - .hasNotFailed(); + .isCompleted() + .isNotCancelled() + .isNotCompletedExceptionally(); } @Test diff --git a/src/test/java/org/assertj/core/api/SoftAssertionsTest.java b/src/test/java/org/assertj/core/api/SoftAssertionsTest.java index 273698de7d0..b286e133091 100644 --- a/src/test/java/org/assertj/core/api/SoftAssertionsTest.java +++ b/src/test/java/org/assertj/core/api/SoftAssertionsTest.java @@ -183,7 +183,7 @@ void should_be_able_to_catch_exceptions_thrown_by_map_assertions() { assertThat(errors.get(1)).hasMessageStartingWith(format("%nExpecting empty but was:<{\"54\"=\"55\"}>")); } - @SuppressWarnings({ "unchecked" }) + @SuppressWarnings({ "unchecked", "deprecation" }) @Test void should_be_able_to_catch_exceptions_thrown_by_all_proxied_methods() throws MalformedURLException { try { From 8abd04af537341ef2d92b50fe6e7fde09cd7b0a4 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sat, 31 Oct 2020 18:30:00 +1300 Subject: [PATCH 063/191] Disable flaky test on windows CI --- .../core/api/future/FutureAssert_failsWithin_Test.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/assertj/core/api/future/FutureAssert_failsWithin_Test.java b/src/test/java/org/assertj/core/api/future/FutureAssert_failsWithin_Test.java index cd8a43baf3b..3df4081c369 100644 --- a/src/test/java/org/assertj/core/api/future/FutureAssert_failsWithin_Test.java +++ b/src/test/java/org/assertj/core/api/future/FutureAssert_failsWithin_Test.java @@ -18,6 +18,7 @@ import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import static org.assertj.core.util.FailureMessages.actualIsNull; import static org.junit.jupiter.api.condition.OS.MAC; +import static org.junit.jupiter.api.condition.OS.WINDOWS; import java.time.Duration; import java.util.concurrent.CompletableFuture; @@ -72,7 +73,7 @@ void should_allow_assertion_on_future_exception_when_future_did_not_complete_wit } @Test - @DisabledOnOs(MAC) + @DisabledOnOs({ MAC, WINDOWS }) void should_fail_if_future_completes_within_given_timeout() { // GIVEN Future future = futureCompletingAfter(Duration.ofMillis(10)); @@ -83,7 +84,7 @@ void should_fail_if_future_completes_within_given_timeout() { } @Test - @DisabledOnOs(MAC) + @DisabledOnOs({ MAC, WINDOWS }) void should_fail_if_future_completes_within_given_timeout_Duration() { // GIVEN Future future = futureCompletingAfter(Duration.ofMillis(10)); From 6ae7dffd29acbb54c5127033cae95b21e1a2c426 Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Sat, 31 Oct 2020 10:49:33 +0100 Subject: [PATCH 064/191] Adjust tests style as per discussion in #2019 --- ...lasses_assertHasPackage_with_Package_Test.java | 15 ++++++++++----- ...Classes_assertHasPackage_with_String_Test.java | 13 ++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java index 76a1796feb8..f27ff83f626 100644 --- a/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java +++ b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_Package_Test.java @@ -31,16 +31,20 @@ class Classes_assertHasPackage_with_Package_Test extends ClassesBaseTest { @Test void should_pass_if_actual_declares_given_package() { + // GIVEN + Class actual = Object.class; + Package aPackage = actual.getPackage(); // WHEN/THEN - classes.assertHasPackage(someInfo(), Object.class, Object.class.getPackage()); + classes.assertHasPackage(someInfo(), actual, aPackage); } @Test void should_fail_if_actual_is_null() { + // GIVEN + Class actual = null; + Package aPackage = Object.class.getPackage(); // WHEN - AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), - null, - Object.class.getPackage())); + AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), actual, aPackage)); // THEN then(assertionError).hasMessage(actualIsNull()); } @@ -48,9 +52,10 @@ void should_fail_if_actual_is_null() { @Test void should_fail_if_aPackage_is_null() { // GIVEN + Class actual = Object.class; Package aPackage = null; // WHEN - Throwable thrown = catchThrowable(() -> classes.assertHasPackage(someInfo(), Object.class, aPackage)); + Throwable thrown = catchThrowable(() -> classes.assertHasPackage(someInfo(), actual, aPackage)); // THEN then(thrown).isInstanceOf(NullPointerException.class) .hasMessage(shouldNotBeNull("aPackage").create()); diff --git a/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java index 3e97328c04d..704c056eb2e 100644 --- a/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java +++ b/src/test/java/org/assertj/core/internal/classes/Classes_assertHasPackage_with_String_Test.java @@ -29,14 +29,20 @@ class Classes_assertHasPackage_with_String_Test extends ClassesBaseTest { @Test void should_pass_if_actual_has_given_package_name() { + // GIVEN + Class actual = Object.class; + String packageName = "java.lang"; // WHEN/THEN - classes.assertHasPackage(someInfo(), Object.class, "java.lang"); + classes.assertHasPackage(someInfo(), actual, packageName); } @Test void should_fail_if_actual_is_null() { + // GIVEN + Class actual = null; + String packageName = "java.lang"; // WHEN - AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), null, "java.lang")); + AssertionError assertionError = expectAssertionError(() -> classes.assertHasPackage(someInfo(), actual, packageName)); // THEN then(assertionError).hasMessage(actualIsNull()); } @@ -44,9 +50,10 @@ void should_fail_if_actual_is_null() { @Test void should_fail_if_packageName_is_null() { // GIVEN + Class actual = Object.class; String packageName = null; // WHEN - Throwable thrown = catchThrowable(() -> classes.assertHasPackage(someInfo(), Object.class, packageName)); + Throwable thrown = catchThrowable(() -> classes.assertHasPackage(someInfo(), actual, packageName)); // THEN then(thrown).isInstanceOf(NullPointerException.class) .hasMessage(shouldNotBeNull("packageName").create()); From b4b8b583dd2ab6a5c573988de3636ba42fb72125 Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 1 Nov 2020 18:21:14 +0100 Subject: [PATCH 065/191] Bump mockito.version from 3.5.15 to 3.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b524f808799..8316ea1abde 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 4.13 5.6.2 - 3.5.15 + 3.6.0 0.8.6 From 0f103046e074a13efd177361e4763f9b6e11a5d5 Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 1 Nov 2020 18:27:13 +0100 Subject: [PATCH 066/191] Bump junit-jupiter.version from 5.6.2 to 5.6.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8316ea1abde..19c4c54917f 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 5.2.0 4.13 - 5.6.2 + 5.6.3 3.6.0 0.8.6 From 1b823e353b1b7b1c49a5b2424ac1fb0d1fb833d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2020 09:12:20 +0100 Subject: [PATCH 067/191] Bump byte-buddy.version from 1.10.17 to 1.10.18 (#2031) Bumps `byte-buddy.version` from 1.10.17 to 1.10.18. Updates `byte-buddy` from 1.10.17 to 1.10.18 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.17...byte-buddy-1.10.18) Updates `byte-buddy-agent` from 1.10.17 to 1.10.18 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.10.17...byte-buddy-1.10.18) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 19c4c54917f..9fd8b05cee0 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ -html5 --allow-script-in-comments --no-module-directories - 1.10.17 + 1.10.18 1.2.0 5.2.0 From c11e82f6ea6fb4ef3f933657c38b187723acbc88 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 1 Nov 2020 14:58:23 +1300 Subject: [PATCH 068/191] Bump verify.bndrun assertj-core version --- verify.bndrun | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/verify.bndrun b/verify.bndrun index e4e98625294..dc8e094305a 100644 --- a/verify.bndrun +++ b/verify.bndrun @@ -30,8 +30,8 @@ # The version ranges will change as the version of # AssertJ and/or its dependencies change. -runbundles: \ - assertj-core;version='[3.18.0,3.18.1)',\ - assertj-core-tests;version='[3.18.0,3.18.1)',\ + assertj-core;version='[3.18.1,3.18.2)',\ + assertj-core-tests;version='[3.18.1,3.18.2)',\ junit-jupiter-api;version='[5.6.2,5.6.3)',\ junit-jupiter-engine;version='[5.6.2,5.6.3)',\ junit-platform-commons;version='[1.6.2,1.6.3)',\ From 41a6c4208f93f29327142d30a3cdaa1e49c329f4 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 1 Nov 2020 15:02:51 +1300 Subject: [PATCH 069/191] Code cleanup and minor test refactoring --- .../core/api/BDDAssertions_then_Test.java | 1 - ...l_method_assertions_in_assumptions_Test.java | 13 ++++++------- ...l_assertion_methods_in_assumptions_Test.java | 1 + ...l_assertion_methods_in_assumptions_Test.java | 5 ----- ...l_method_assertions_in_assumptions_Test.java | 17 +++++++++-------- .../IterableAssert_flatExtracting_Test.java | 6 +++--- ...sert_flatExtracting_with_SortedSet_Test.java | 17 +++++++++++------ 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/test/java/org/assertj/core/api/BDDAssertions_then_Test.java b/src/test/java/org/assertj/core/api/BDDAssertions_then_Test.java index 2d0151bc982..a203bba9586 100644 --- a/src/test/java/org/assertj/core/api/BDDAssertions_then_Test.java +++ b/src/test/java/org/assertj/core/api/BDDAssertions_then_Test.java @@ -68,7 +68,6 @@ void then_char() { then('z').isGreaterThan('a'); } - @SuppressWarnings("deprecation") @Test void then_Character() { then(new Character('A')).isEqualTo(new Character('A')); diff --git a/src/test/java/org/assertj/core/api/assumptions/Class_final_method_assertions_in_assumptions_Test.java b/src/test/java/org/assertj/core/api/assumptions/Class_final_method_assertions_in_assumptions_Test.java index 6746c50e3f3..cd4394750a2 100644 --- a/src/test/java/org/assertj/core/api/assumptions/Class_final_method_assertions_in_assumptions_Test.java +++ b/src/test/java/org/assertj/core/api/assumptions/Class_final_method_assertions_in_assumptions_Test.java @@ -15,6 +15,8 @@ import static org.assertj.core.api.Assumptions.assumeThat; import static org.assertj.core.api.assumptions.BaseAssumptionRunner.assumptionRunner; +import java.util.stream.Stream; + import org.assertj.core.api.ClassAssert; import org.assertj.core.api.ClassAssertBaseTest.AnnotatedClass; import org.assertj.core.api.ClassAssertBaseTest.AnotherAnnotation; @@ -22,18 +24,15 @@ import org.assertj.core.api.ProxyableClassAssert; import org.assertj.core.util.VisibleForTesting; -import java.util.stream.Stream; - /** * verify that assertions final methods in {@link ClassAssert} work with assumptions (i.e. that they are proxied correctly in {@link ProxyableClassAssert}). */ class Class_final_method_assertions_in_assumptions_Test extends BaseAssumptionsRunnerTest { + @SuppressWarnings("unchecked") public static Stream> provideAssumptionsRunners() { - return Stream.of( - assumptionRunner(AnnotatedClass.class, - value -> assumeThat(value).hasAnnotations(MyAnnotation.class, AnotherAnnotation.class), - value -> assumeThat(value).hasAnnotations(SafeVarargs.class, VisibleForTesting.class)) - ); + return Stream.of(assumptionRunner(AnnotatedClass.class, + value -> assumeThat(value).hasAnnotations(MyAnnotation.class, AnotherAnnotation.class), + value -> assumeThat(value).hasAnnotations(SafeVarargs.class, VisibleForTesting.class))); } } diff --git a/src/test/java/org/assertj/core/api/assumptions/Map_special_assertion_methods_in_assumptions_Test.java b/src/test/java/org/assertj/core/api/assumptions/Map_special_assertion_methods_in_assumptions_Test.java index 6abc43c42ff..8dee6a4e18b 100644 --- a/src/test/java/org/assertj/core/api/assumptions/Map_special_assertion_methods_in_assumptions_Test.java +++ b/src/test/java/org/assertj/core/api/assumptions/Map_special_assertion_methods_in_assumptions_Test.java @@ -36,6 +36,7 @@ */ class Map_special_assertion_methods_in_assumptions_Test extends BaseAssumptionsRunnerTest { + @SuppressWarnings("unchecked") public static Stream> provideAssumptionsRunners() { List names = asList("Dave", "Jeff"); diff --git a/src/test/java/org/assertj/core/api/assumptions/ObjectArray_special_assertion_methods_in_assumptions_Test.java b/src/test/java/org/assertj/core/api/assumptions/ObjectArray_special_assertion_methods_in_assumptions_Test.java index 113ddb9101f..cddd2890811 100644 --- a/src/test/java/org/assertj/core/api/assumptions/ObjectArray_special_assertion_methods_in_assumptions_Test.java +++ b/src/test/java/org/assertj/core/api/assumptions/ObjectArray_special_assertion_methods_in_assumptions_Test.java @@ -89,11 +89,6 @@ public static Stream> provideAssumptionsRunners() { .containsAnyOf(bart, lisa), value -> assumeThat(value).flatExtracting("children") .containsAnyOf(homer, fred)), - assumptionRunner(array(homer, fred), - value -> assumeThat(value).flatExtracting(childrenExtractor) - .containsAnyOf(bart, lisa), - value -> assumeThat(value).flatExtracting(childrenExtractor) - .containsAnyOf(homer, fred)), assumptionRunner(array(homer, fred), value -> assumeThat(value).flatExtracting(CartoonCharacter::getChildren) .containsAnyOf(bart, lisa), diff --git a/src/test/java/org/assertj/core/api/assumptions/Predicate_final_method_assertions_in_assumptions_Test.java b/src/test/java/org/assertj/core/api/assumptions/Predicate_final_method_assertions_in_assumptions_Test.java index 3f60abab85c..49f2f4bf8da 100644 --- a/src/test/java/org/assertj/core/api/assumptions/Predicate_final_method_assertions_in_assumptions_Test.java +++ b/src/test/java/org/assertj/core/api/assumptions/Predicate_final_method_assertions_in_assumptions_Test.java @@ -28,15 +28,16 @@ */ class Predicate_final_method_assertions_in_assumptions_Test extends BaseAssumptionsRunnerTest { + @SuppressWarnings("unchecked") public static Stream> provideAssumptionsRunners() { Predicate> ballSportPredicate = sport -> sport.value.contains("ball"); - return Stream.of( - assumptionRunner(ballSportPredicate, - value -> assumeThat(value).accepts(entry("sport", "football"), entry("sport", "basketball")), - value -> assumeThat(value).accepts(entry("sport", "boxing"), entry("sport", "marathon"))), - assumptionRunner(ballSportPredicate, - value -> assumeThat(value).rejects(entry("sport", "boxing"), entry("sport", "marathon")), - value -> assumeThat(value).rejects(entry("sport", "football"), entry("sport", "basketball"))) - ); + return Stream.of(assumptionRunner(ballSportPredicate, + value -> assumeThat(value).accepts(entry("sport", "football"), + entry("sport", "basketball")), + value -> assumeThat(value).accepts(entry("sport", "boxing"), entry("sport", "marathon"))), + assumptionRunner(ballSportPredicate, + value -> assumeThat(value).rejects(entry("sport", "boxing"), entry("sport", "marathon")), + value -> assumeThat(value).rejects(entry("sport", "football"), + entry("sport", "basketball")))); } } diff --git a/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_Test.java b/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_Test.java index dac8347b7bd..b71ca468166 100644 --- a/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_Test.java +++ b/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_Test.java @@ -64,7 +64,7 @@ public List extract(CartoonCharacter input) { private final ThrowingExtractor, Exception> throwingExtractor = new ThrowingExtractor, Exception>() { @Override public List extractThrows(CartoonCharacter cartoonCharacter) throws Exception { - if (cartoonCharacter.getChildren().isEmpty()) { throw new Exception("no children"); } + if (cartoonCharacter.getChildren().isEmpty()) throw new Exception("no children"); return cartoonCharacter.getChildren(); } }; @@ -108,12 +108,12 @@ void should_allow_assertions_on_empty_result_lists() { } @Test - void should_throw_null_pointer_exception_when_extracting_from_null_with_extractor() { + void should_bubble_up_null_pointer_exception_from_extractor() { assertThatNullPointerException().isThrownBy(() -> assertThat(newArrayList(homer, null)).flatExtracting(childrenExtractor)); } @Test - void should_throw_null_pointer_exception_when_extracting_from_null() { + void should_bubble_up_null_pointer_exception_from_lambda_extractor() { assertThatNullPointerException().isThrownBy(() -> assertThat(newArrayList(homer, null)).flatExtracting(children)); } diff --git a/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_with_SortedSet_Test.java b/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_with_SortedSet_Test.java index 08d27d3bdb2..f42b1aade0d 100644 --- a/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_with_SortedSet_Test.java +++ b/src/test/java/org/assertj/core/api/iterable/IterableAssert_flatExtracting_with_SortedSet_Test.java @@ -110,13 +110,19 @@ void should_allow_assertions_on_empty_result_lists() { } @Test - void should_throw_null_pointer_exception_when_extracting_from_null_with_extractor() { - assertThatNullPointerException().isThrownBy(() -> assertThat(newSortedSet(homer, null)).flatExtracting(childrenExtractor)); + void should_bubble_up_null_pointer_exception_from_extractor() { + // GIVEN + SortedSet setWithNullElements = newSortedSet(homer, null); + // WHEN THEN + assertThatNullPointerException().isThrownBy(() -> assertThat(setWithNullElements).flatExtracting(childrenExtractor)); } @Test - void should_throw_null_pointer_exception_when_extracting_from_null() { - assertThatNullPointerException().isThrownBy(() -> assertThat(newSortedSet(homer, null)).flatExtracting(children)); + void should_bubble_up_null_pointer_exception_from_lambda_extractor() { + // GIVEN + SortedSet setWithNullElements = newSortedSet(homer, null); + // WHEN THEN + assertThatNullPointerException().isThrownBy(() -> assertThat(setWithNullElements).flatExtracting(children)); } @Test @@ -235,7 +241,6 @@ void flatExtracting_should_keep_assertion_state_with_extractor() { assertThat(comparatorForElementFieldsWithNamesOf(assertion).get("foo")).isSameAs(ALWAY_EQUALS_STRING); } - @Test void flatExtracting_should_keep_assertion_state() { // GIVEN @@ -289,7 +294,7 @@ void flatExtracting_with_ThrowingExtractor_should_keep_assertion_state() { } private static SortedSet newSortedSet(CartoonCharacter... cartoonCharacters) { - TreeSet cartoonCharacterSortedSet = new TreeSet<>(comparing(CartoonCharacter::getName)); + TreeSet cartoonCharacterSortedSet = new TreeSet<>(comparing(t -> t == null ? "" : t.getName())); for (CartoonCharacter cartoonCharacter : cartoonCharacters) { cartoonCharacterSortedSet.add(cartoonCharacter); } From 4b500d2fec3e61c6700dc54247c49b7cf893d283 Mon Sep 17 00:00:00 2001 From: Joel Costigliola Date: Sun, 1 Nov 2020 15:03:44 +1300 Subject: [PATCH 070/191] =?UTF-8?q?Add=20map(=E2=80=A6)=20and=20flatMap(?= =?UTF-8?q?=E2=80=A6)=20to=20iterable=20assertions=20as=20aliases=20of=20e?= =?UTF-8?q?xtracting/flatExtracting.=20Fixes=20#2023?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/api/AbstractIterableAssert.java | 453 ++++++++++++++---- .../org/assertj/core/api/IterableAssert.java | 20 +- .../java/org/assertj/core/api/ListAssert.java | 18 + .../core/api/BDDSoftAssertionsTest.java | 80 +++- .../assertj/core/api/SoftAssertionsTest.java | 90 +++- ...meThat_with_various_java_8_types_Test.java | 2 - .../BaseAssumptionsRunnerTest.java | 3 - ...assertion_methods_in_assumptions_Test.java | 39 +- ...assertion_methods_in_assumptions_Test.java | 356 +++++++------- 9 files changed, 799 insertions(+), 262 deletions(-) diff --git a/src/main/java/org/assertj/core/api/AbstractIterableAssert.java b/src/main/java/org/assertj/core/api/AbstractIterableAssert.java index 2023cb3c1d3..de632786273 100644 --- a/src/main/java/org/assertj/core/api/AbstractIterableAssert.java +++ b/src/main/java/org/assertj/core/api/AbstractIterableAssert.java @@ -798,7 +798,7 @@ public SELF usingDefaultElementComparator() { /** * Verifies that the actual {@link Iterable} contains at least one of the given values. *

    - * Example : + * Example: *

     Iterable<String> abc = Arrays.asList("a", "b", "c");
        *
        * // assertions will pass
    @@ -829,7 +829,7 @@ public SELF containsAnyOf(@SuppressWarnings("unchecked") ELEMENT... values) {
       /**
        * Verifies that the {@link Iterable} under test contains at least one of the given {@link Iterable} elements.
        * 

    - * Example : + * Example: *

     Iterable<String> abc = Arrays.asList("a", "b", "c");
        *
        * // assertions will pass
    @@ -861,9 +861,9 @@ public SELF containsAnyElementsOf(Iterable iterable) {
        * Iterable becoming the Iterable under test.
        * 

    * It allows you to test a property/field of the Iterable's elements instead of testing the elements themselves, which - * can be be much less work ! + * can be be much less work! *

    - * Let's take a look at an example to make things clearer : + * Let's take a look at an example to make things clearer: *

     // build a list of TolkienCharacters: a TolkienCharacter has a name, and age and a Race (a specific class)
        * // they can be public field or properties, both can be extracted.
        * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    @@ -877,13 +877,13 @@ public SELF containsAnyElementsOf(Iterable iterable) {
        * fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN);
        * fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN));
        *
    -   * // let's verify the names of the TolkienCharacters in fellowshipOfTheRing :
    +   * // let's verify the names of the TolkienCharacters in fellowshipOfTheRing:
        *
        * assertThat(fellowshipOfTheRing).extracting("name")
        *           .contains("Boromir", "Gandalf", "Frodo")
        *           .doesNotContain("Sauron", "Elrond");
        *
    -   * // you can extract nested properties/fields like the name of the race :
    +   * // you can extract nested properties/fields like the name of the race:
        *
        * assertThat(fellowshipOfTheRing).extracting("race.name")
        *                                .contains("Hobbit", "Elf")
    @@ -953,8 +953,8 @@ public AbstractListAssert, Object, ObjectAssert
    -   * Let's take a look at an example to make things clearer :
    -   * 
     // Build a array of WesterosHouse, a WesterosHouse has a method: public String sayTheWords()
    +   * Let's take a look at an example to make things clearer:
    +   * 
     // Build an array of WesterosHouse, a WesterosHouse has a method: public String sayTheWords()
        *
        * List<WesterosHouse> greatHouses = new ArrayList<WesterosHouse>();
        * greatHouses.add(new WesterosHouse("Stark", "Winter is Coming"));
    @@ -1001,8 +1001,8 @@ public AbstractListAssert, Object, ObjectAssert
    -   * Let's take an example to make things clearer :
    -   * 
     // Build a array of WesterosHouse, a WesterosHouse has a method: public String sayTheWords()
    +   * Let's take an example to make things clearer:
    +   * 
     // Build an array of WesterosHouse, a WesterosHouse has a method: public String sayTheWords()
        * List<WesterosHouse> greatHouses = new ArrayList<WesterosHouse>();
        * greatHouses.add(new WesterosHouse("Stark", "Winter is Coming"));
        * greatHouses.add(new WesterosHouse("Lannister", "Hear Me Roar!"));
    @@ -1049,9 +1049,9 @@ public 

    AbstractListAssert, P, ObjectAssert

    > extracti * Iterable becoming the Iterable under test. *

    * It allows you to test a property/field of the Iterable's elements instead of testing the elements themselves, - * which can be much less work ! + * which can be much less work! *

    - * Let's take an example to make things clearer : + * Let's take an example to make things clearer: *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
        * // they can be public field or properties, both can be extracted.
        * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    @@ -1065,12 +1065,12 @@ public 

    AbstractListAssert, P, ObjectAssert

    > extracti * fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN); * fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN)); * - * // let's verify the names of TolkienCharacter in fellowshipOfTheRing : + * // let's verify the names of TolkienCharacter in fellowshipOfTheRing: * assertThat(fellowshipOfTheRing).extracting("name", String.class) * .contains("Boromir", "Gandalf", "Frodo") * .doesNotContain("Sauron", "Elrond"); * - * // you can extract nested property/field like the name of Race : + * // you can extract nested property/field like the name of Race: * assertThat(fellowshipOfTheRing).extracting("race.name", String.class) * .contains("Hobbit", "Elf") * .doesNotContain("Orc");

    @@ -1146,7 +1146,7 @@ public

    AbstractListAssert, P, ObjectAssert

    > extracti * extract "id", "name" and "email" then each Tuple data will be composed of id, name and email extracted from the * element of the initial Iterable (the Tuple's data order is the same as the given fields/properties order). *

    - * Let's take an example to make things clearer : + * Let's take an example to make things clearer: *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
        * // they can be public field or properties, both can be extracted.
        * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    @@ -1160,14 +1160,14 @@ public 

    AbstractListAssert, P, ObjectAssert

    > extracti * fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN); * fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN)); * - * // let's verify 'name' and 'age' of some TolkienCharacter in fellowshipOfTheRing : + * // let's verify 'name' and 'age' of some TolkienCharacter in fellowshipOfTheRing: * assertThat(fellowshipOfTheRing).extracting("name", "age") * .contains(tuple("Boromir", 37), * tuple("Sam", 38), * tuple("Legolas", 1000)); * * - * // extract 'name', 'age' and Race name values : + * // extract 'name', 'age' and Race name values: * assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name") * .contains(tuple("Boromir", 37, "Man"), * tuple("Sam", 38, "Hobbit"), @@ -1229,12 +1229,12 @@ public AbstractListAssert, Tuple, ObjectAssert> /** * Extract the values from Iterable's elements under test by applying an extracting function on them. The returned - * iterable becomes a new object under test. + * iterable becomes the instance under test. *

    * It allows to test values from the elements more safely than by using {@link #extracting(String)}, as it * doesn't utilize introspection. *

    - * Let's have a look at an example : + * Let's have a look at an example: *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
        * // they can be public field or properties, both can be extracted.
        * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    @@ -1252,8 +1252,7 @@ public AbstractListAssert, Tuple, ObjectAssert>
        * assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getRace).contains(HOBBIT);
    * * Note that the order of extracted property/field values is consistent with the iteration order of the Iterable under - * test, for example if it's a {@link HashSet}, you won't be able to make any assumptions on the extracted values - * order. + * test, for example if it's a {@link HashSet}, you won't be able to make any assumptions on the extracted values order. * * @param the type of elements extracted. * @param extractor the object transforming input object to desired one @@ -1261,20 +1260,58 @@ public AbstractListAssert, Tuple, ObjectAssert> */ @CheckReturnValue public AbstractListAssert, V, ObjectAssert> extracting(Function extractor) { + return internalExtracting(extractor); + } + + private AbstractListAssert, V, ObjectAssert> internalExtracting(Function extractor) { List values = FieldsOrPropertiesExtractor.extract(actual, extractor); return newListAssertInstanceForMethodsChangingElementType(values); } + /** + * Maps the Iterable's elements under test by applying a mapping function, the resulting list becomes the instance under test. + *

    + * This allows to test values from the elements more safely than by using {@link #extracting(String)}. + *

    + * Let's have a look at an example: + *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
    +   * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    +   *
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Frodo", 33, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Sam", 38, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Gandalf", 2020, MAIA));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Legolas", 1000, ELF));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Pippin", 28, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Gimli", 139, DWARF));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN);
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN));
    +   *
    +   * // fellowship has hobbitses, right, my precioussss?
    +   * assertThat(fellowshipOfTheRing).map(TolkienCharacter::getRace)
    +   *                                .contains(HOBBIT);
    + * + * Note that the order of mapped values is consistent with the order of the Iterable under test, for example if + * it's a {@link HashSet}, you won't be able to make any assumptions on the extracted values order. + * + * @param the type of elements resulting of the map operation. + * @param mapper the {@link Function} transforming input object to desired one + * @return a new assertion object whose object under test is the list of values extracted + * @since 3.19.0 + */ + public AbstractListAssert, V, ObjectAssert> map(Function mapper) { + return internalExtracting(mapper); + } + /** * Extract the values from Iterable's elements under test by applying an extracting function (which might throw an - * exception) on them. The returned iterable becomes a new object under test. + * exception) on them. The returned iterable becomes the instance under test. *

    * Any checked exception raised in the extractor is rethrown wrapped in a {@link RuntimeException}. *

    * It allows to test values from the elements more safely than by using {@link #extracting(String)}, as it * doesn't utilize introspection. *

    - * Let's have a look at an example : + * Let's have a look at an example: *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
        * // they can be public field or properties, both can be extracted.
        * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    @@ -1307,8 +1344,49 @@ public  AbstractListAssert, V, ObjectAssert> extracti
        */
       @CheckReturnValue
       public  AbstractListAssert, V, ObjectAssert> extracting(ThrowingExtractor extractor) {
    -    List values = FieldsOrPropertiesExtractor.extract(actual, extractor);
    -    return newListAssertInstanceForMethodsChangingElementType(values);
    +    return internalExtracting(extractor);
    +  }
    +
    +  /**
    +   * Maps the Iterable's elements by applying the given mapping function (which might throw an exception), the returned list
    +   * becomes the instance under test.
    +   * 

    + * Any checked exception raised in the function is rethrown wrapped in a {@link RuntimeException}. + *

    + * This allows to test values from the elements more safely than by using {@link #extracting(String)}. + *

    + * Let's have a look at an example: + *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
    +   * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    +   *
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Frodo", 33, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Sam", 38, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Gandalf", 2020, MAIA));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Legolas", 1000, ELF));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Pippin", 28, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Gimli", 139, DWARF));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN);
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN));
    +   *
    +   * assertThat(fellowshipOfTheRing).map(input -> {
    +   *   if (input.getAge() < 20) {
    +   *     throw new Exception("age < 20");
    +   *   }
    +   *   return input.getName();
    +   * }).contains("Frodo");
    + * + * Note that the order of mapped values is consistent with the order of the Iterable under test, for example if it's a + * {@link HashSet}, you won't be able to make any assumptions on the extracted values order. + * + * @param the exception type of {@link ThrowingExtractor} + * @param the type of elements extracted. + * @param mapper the function transforming input object to desired one + * @return a new assertion object whose object under test is the list of values extracted + * @since 3.19.0 + */ + @CheckReturnValue + public AbstractListAssert, V, ObjectAssert> map(ThrowingExtractor mapper) { + return internalExtracting(mapper); } /* @@ -1327,15 +1405,14 @@ private AbstractListAssert, V, ObjectAssert> newList } /** - * Extract the Iterable values from Iterable's elements under test by applying an Iterable extracting function on them - * and concatenating the result lists. The returned iterable becomes a new object under test. - *

    - * It allows testing the results of extracting values that are represented by Iterables. + * Extracts Iterable elements values by applying a function and concatenates the result into a list that becomes the instance + * under test. *

    - * For example: + * Example: *

     CartoonCharacter bart = new CartoonCharacter("Bart Simpson");
        * CartoonCharacter lisa = new CartoonCharacter("Lisa Simpson");
        * CartoonCharacter maggie = new CartoonCharacter("Maggie Simpson");
    +   *
        * CartoonCharacter homer = new CartoonCharacter("Homer Simpson");
        * homer.getChildren().add(bart);
        * homer.getChildren().add(lisa);
    @@ -1351,13 +1428,11 @@ private  AbstractListAssert, V, ObjectAssert> newList
        * assertThat(parents).flatExtracting(CartoonCharacter::getChildren)
        *                    .containsOnly(bart, lisa, maggie, pebbles);
    * - * The order of extracted values is consistent with both the order of the collection itself, as well as the extracted - * collections. + * The extracted values order is consistent with both the order of the iterable itself as well as the extracted collections. * - * @param the type of elements extracted. - * @param extractor the object transforming input object to an {@code Iterable} of desired ones + * @param the type of extracted elements. + * @param extractor the {@link Function} transforming input object to an {@code Iterable} of desired ones * @return a new assertion object whose object under test is the list of values extracted - * @throws NullPointerException if one of the {@code Iterable}'s element is null. */ @CheckReturnValue public AbstractListAssert, V, ObjectAssert> flatExtracting(Function> extractor) { @@ -1365,16 +1440,50 @@ public AbstractListAssert, V, ObjectAssert> flatExtr } /** - * Extract the Iterable values from Iterable's elements under test by applying an Iterable extracting function (which - * might throw a checked exception) on them and concatenating the result lists. The returned iterable becomes a new object - * under test. + * Maps the Iterable's elements under test by applying the given {@link Function} and flattens the resulting collections in a + * list becoming the object under test. *

    - * It allows testing the results of extracting values that are represented by Iterables. + * Example: + *

     CartoonCharacter bart = new CartoonCharacter("Bart Simpson");
    +   * CartoonCharacter lisa = new CartoonCharacter("Lisa Simpson");
    +   * CartoonCharacter maggie = new CartoonCharacter("Maggie Simpson");
    +   *
    +   * CartoonCharacter homer = new CartoonCharacter("Homer Simpson");
    +   * homer.getChildren().add(bart);
    +   * homer.getChildren().add(lisa);
    +   * homer.getChildren().add(maggie);
    +   *
    +   * CartoonCharacter pebbles = new CartoonCharacter("Pebbles Flintstone");
    +   * CartoonCharacter fred = new CartoonCharacter("Fred Flintstone");
    +   * fred.getChildren().add(pebbles);
    +   *
    +   * List<CartoonCharacter> parents = list(homer, fred);
    +   *
    +   * // check children property which is a List<CartoonCharacter>
    +   * assertThat(parents).flatMap(CartoonCharacter::getChildren)
    +   *                    .containsOnly(bart, lisa, maggie, pebbles);
    + * + * The mapped values order is consistent with both the order of the iterable itself as well as the mapped collections. + * + * @param the type of mapped elements. + * @param mapper the {@link Function} transforming input object to an {@code Iterable} of desired ones + * @return a new assertion object whose object under test is the list of values extracted + * @since 3.19.0 + */ + @CheckReturnValue + public AbstractListAssert, V, ObjectAssert> flatMap(Function> mapper) { + return doFlatExtracting(mapper); + } + + /** + * Extracts Iterable elements values by applying a function (which might throw a checked exception) on them and + * concatenates/flattens the result into a single list that becomes the instance under test. *

    - * For example: + * Example: *

     CartoonCharacter bart = new CartoonCharacter("Bart Simpson");
        * CartoonCharacter lisa = new CartoonCharacter("Lisa Simpson");
        * CartoonCharacter maggie = new CartoonCharacter("Maggie Simpson");
    +   *
        * CartoonCharacter homer = new CartoonCharacter("Homer Simpson");
        * homer.getChildren().add(bart);
        * homer.getChildren().add(lisa);
    @@ -1390,14 +1499,12 @@ public  AbstractListAssert, V, ObjectAssert> flatExtr
        * assertThat(parents).flatExtracting(CartoonCharacter::getChildren)
        *                    .containsOnly(bart, lisa, maggie, pebbles);
    * - * The order of extracted values is consistent with both the order of the collection itself, as well as the extracted - * collections. + * The extracted values order is consistent with both the order of the iterable itself as well as the extracted collections. * - * @param the type of elements extracted. + * @param the type of extracted values. * @param the exception type of {@link ThrowingExtractor} * @param extractor the object transforming input object to an {@code Iterable} of desired ones * @return a new assertion object whose object under test is the list of values extracted - * @throws NullPointerException if one of the {@code Iterable}'s element is null. * @since 3.7.0 */ @CheckReturnValue @@ -1405,6 +1512,43 @@ public AbstractListAssert, return doFlatExtracting(extractor); } + /** + * Maps the Iterable's elements under test by applying a mapping function (which might throw a checked exception) and + * concatenates/flattens the result into a single list that becomes the instance under test. + *

    + * Example: + *

     CartoonCharacter bart = new CartoonCharacter("Bart Simpson");
    +   * CartoonCharacter lisa = new CartoonCharacter("Lisa Simpson");
    +   * CartoonCharacter maggie = new CartoonCharacter("Maggie Simpson");
    +   *
    +   * CartoonCharacter homer = new CartoonCharacter("Homer Simpson");
    +   * homer.getChildren().add(bart);
    +   * homer.getChildren().add(lisa);
    +   * homer.getChildren().add(maggie);
    +   *
    +   * CartoonCharacter pebbles = new CartoonCharacter("Pebbles Flintstone");
    +   * CartoonCharacter fred = new CartoonCharacter("Fred Flintstone");
    +   * fred.getChildren().add(pebbles);
    +   *
    +   * List<CartoonCharacter> parents = list(homer, fred);
    +   *
    +   * // check children property where getChildren() can throw an Exception!
    +   * assertThat(parents).flatMap(CartoonCharacter::getChildren)
    +   *                    .containsOnly(bart, lisa, maggie, pebbles);
    + * + * The mapped values order is consistent with both the order of the iterable itself as well as the mapped collections. + * + * @param the type of mapped values. + * @param the exception type of {@link ThrowingExtractor} + * @param mapper the object transforming input object to an {@code Iterable} of desired ones + * @return a new assertion object whose object under test is the list of values extracted + * @since 3.19.0 + */ + @CheckReturnValue + public AbstractListAssert, V, ObjectAssert> flatMap(ThrowingExtractor, EXCEPTION> mapper) { + return doFlatExtracting(mapper); + } + private AbstractListAssert, V, ObjectAssert> doFlatExtracting(Function> extractor) { List result = FieldsOrPropertiesExtractor.extract(actual, extractor).stream() .flatMap(Collection::stream) @@ -1413,18 +1557,18 @@ private AbstractListAssert, V, ObjectAssert> doFlatE } /** - * Extract multiple values from each {@code Iterable}'s element according to the given {@code Function}s - * and concatenate/flatten the extracted values in a list that is used as the new object under test. + * Extracts multiple values from each {@code Iterable}'s element according to the given {@code Function}s and + * concatenates/flattens them in a list that becomes the instance under test. *

    - * If extracted values were not flattened, instead of a simple list like (given 2 extractors) : - *

    element1.value1, element1.value2, element2.value1, element2.value2, ...  
    - * we would get a list of list like : - *
    list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    + * If extracted values were not flattened, instead of a simple list like (given 2 extractors): + *
      element1.value1, element1.value2, element2.value1, element2.value2, ...  
    + * we would get a list of list like: + *
      list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    *

    - * Code example: + * Example: *

     // fellowshipOfTheRing is a List<TolkienCharacter>
        *
    -   * // values are extracted in order and flattened : age1, name1, age2, name2, age3 ...
    +   * // values are extracted in order and flattened: age1, name1, age2, name2, age3 ...
        * assertThat(fellowshipOfTheRing).flatExtracting(TolkienCharacter::getAge,
        *                                                TolkienCharacter::getName)
        *                                .contains(33 ,"Frodo",
    @@ -1432,33 +1576,68 @@ private  AbstractListAssert, V, ObjectAssert> doFlatE
        *                                          87, "Aragorn");
    * * The resulting extracted values list is ordered by {@code Iterable}'s element first and then extracted values, - * this is why is in the example that age values come before names. + * this is why is in the example age values come before names. * * @param extractors all the extractors to apply on each actual {@code Iterable}'s elements * @return a new assertion object whose object under test is a flattened list of all extracted values. */ @CheckReturnValue public AbstractListAssert, Object, ObjectAssert> flatExtracting(@SuppressWarnings("unchecked") Function... extractors) { + return doFlaExtracting(extractors); + } + + /** + * Maps multiple values from each {@code Iterable}'s element according to the given {@code Function}s + * and concatenates/flattens them in a list that becomes the instance under test. + *

    + * If mapped values were not flattened, instead of a simple list like (given 2 extractors): + *

      element1.value1, element1.value2, element2.value1, element2.value2, ...  
    + * we would get a list of list like: + *
      list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    + *

    + * Example: + *

     // fellowshipOfTheRing is a List<TolkienCharacter>
    +   *
    +   * // values are extracted in order and flattened: age1, name1, age2, name2, age3 ...
    +   * assertThat(fellowshipOfTheRing).flatMap(TolkienCharacter::getAge,
    +   *                                         TolkienCharacter::getName)
    +   *                                .contains(33 ,"Frodo",
    +   *                                          1000, "Legolas",
    +   *                                          87, "Aragorn");
    + * + * The resulting mapped values list is ordered by {@code Iterable}'s element first and then mapped values, this is why is + * in the example age values come before names. + * + * @param mappers all the mappers to apply on each actual {@code Iterable}'s elements + * @return a new assertion object whose object under test is a flattened list of all mapped values. + * @since 3.19.0 + */ + @CheckReturnValue + public AbstractListAssert, Object, ObjectAssert> flatMap(@SuppressWarnings("unchecked") Function... mappers) { + return doFlaExtracting(mappers); + } + + @SafeVarargs + private final AbstractListAssert, Object, ObjectAssert> doFlaExtracting(Function... extractors) { Stream actualStream = stream(actual.spliterator(), false); - List result = actualStream.flatMap(element -> Stream.of(extractors) - .map(extractor -> extractor.apply(element))) + List result = actualStream.flatMap(element -> Stream.of(extractors).map(extractor -> extractor.apply(element))) .collect(toList()); return newListAssertInstanceForMethodsChangingElementType(result); } /** - * Extract multiple values from each {@code Iterable}'s element according to the given {@link ThrowingExtractor}s - * and concatenate/flatten the extracted values in a list that is used as the new object under test. + * Extracts multiple values from each {@code Iterable}'s element according to the given {@link ThrowingExtractor}s + * and concatenates/flattens them in a list that becomes the object under test. *

    - * If extracted values were not flattened, instead of a simple list like (given 2 extractors) : - *

    element1.value1, element1.value2, element2.value1, element2.value2, ...  
    - * we would get a list of list like : - *
    list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    + * If extracted values were not flattened, instead of a simple list like (given 2 extractors): + *
      element1.value1, element1.value2, element2.value1, element2.value2, ...  
    + * we would get a list of list like: + *
      list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    *

    - * Code example: + * Example: *

     // fellowshipOfTheRing is a List<TolkienCharacter>
        *
    -   * // values are extracted in order and flattened : age1, name1, age2, name2, age3 ...
    +   * // values are extracted in order and flattened: age1, name1, age2, name2, age3 ...
        * assertThat(fellowshipOfTheRing).flatExtracting(input -> {
        *   if (input.getAge() < 20) {
        *     throw new Exception("age < 20");
    @@ -1474,7 +1653,7 @@ public AbstractListAssert, Object, ObjectAssert
        *
        * The resulting extracted values list is ordered by {@code Iterable}'s element first and then extracted values,
    -   * this is why is in the example that age values come before names.
    +   * this is why is in the example age values come before names.
        *
        * @param  the exception type of {@link ThrowingExtractor}
        * @param extractors all the extractors to apply on each actual {@code Iterable}'s elements
    @@ -1483,18 +1662,62 @@ public AbstractListAssert, Object, ObjectAssert AbstractListAssert, Object, ObjectAssert> flatExtracting(@SuppressWarnings("unchecked") ThrowingExtractor... extractors) {
    +    return doFlatExtracting(extractors);
    +  }
    +
    +  /**
    +   * Maps multiple values from each {@code Iterable}'s element according to the given {@link ThrowingExtractor}s and
    +   * concatenates/flattens them in a list that becomes the object under test.
    +   * 

    + * If mapped values were not flattened, instead of a simple list like (given 2 mappers): + *

      element1.value1, element1.value2, element2.value1, element2.value2, ...  
    + * we would get a list of list like: + *
      list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    + *

    + * Example: + *

     // fellowshipOfTheRing is a List<TolkienCharacter>
    +   *
    +   * // values are extracted in order and flattened: age1, name1, age2, name2, age3 ...
    +   * assertThat(fellowshipOfTheRing).flatMap(input -> {
    +   *   if (input.getAge() < 20) {
    +   *     throw new Exception("age < 20");
    +   *   }
    +   *   return input.getName();
    +   * }, input2 -> {
    +   *   if (input2.getAge() < 20) {
    +   *     throw new Exception("age < 20");
    +   *   }
    +   *   return input2.getAge();
    +   * }).contains(33 ,"Frodo",
    +   *     1000, "Legolas",
    +   *     87, "Aragorn");
    + * + * The resulting mapped values list is ordered by {@code Iterable}'s element first and then mapped values, this is why is in + * the example age values come before names. + * + * @param the exception type of {@link ThrowingExtractor} + * @param mappers all the mappers to apply on each actual {@code Iterable}'s elements + * @return a new assertion object whose object under test is a flattened list of all extracted values. + * @since 3.19.0 + */ + @CheckReturnValue + public AbstractListAssert, Object, ObjectAssert> flatMap(@SuppressWarnings("unchecked") ThrowingExtractor... mappers) { + return doFlatExtracting(mappers); + } + + @SafeVarargs + private final AbstractListAssert, Object, ObjectAssert> doFlatExtracting(ThrowingExtractor... mappers) { Stream actualStream = stream(actual.spliterator(), false); - List result = actualStream.flatMap(element -> Stream.of(extractors) - .map(extractor -> extractor.apply(element))) + List result = actualStream.flatMap(element -> Stream.of(mappers).map(extractor -> extractor.apply(element))) .collect(toList()); return newListAssertInstanceForMethodsChangingElementType(result); } /** - * Extract from Iterable's elements the Iterable/Array values corresponding to the given property/field name and - * concatenate them into a single list becoming the new object under test. + * Extract Iterable's elements values corresponding to the given property/field name and concatenates them into a list becoming + * the new instance under test. *

    - * It allows testing the elements of extracting values that are represented by iterables or arrays. + * This allows testing the elements extracted values that are iterables or arrays. *

    * For example: *

     CartoonCharacter bart = new CartoonCharacter("Bart Simpson");
    @@ -1515,8 +1738,7 @@ public  AbstractListAssert
    * - * The order of extracted values is consisted with both the order of the collection itself, as well as the extracted - * collections. + * The order of extracted values is consisted with both the order of the iterable itself as well as the extracted collections. * * @param fieldOrPropertyName the object transforming input object to an Iterable of desired ones * @return a new assertion object whose object under test is the list of values extracted @@ -1553,12 +1775,11 @@ public AbstractListAssert, Object, ObjectAssert - * The Tuple data corresponds to the extracted values from the Iterable's elements, for instance if you pass functions + * The {@link Tuple} data correspond to the extracted values from the Iterable's elements, for instance if you pass functions * extracting "id", "name" and "email" values then each Tuple data will be composed of an id, a name and an email - * extracted from the element of the initial Iterable (the Tuple's data order is the same as the given functions - * order). + * extracted from the element of the initial Iterable (the Tuple's data order is the same as the given functions order). *

    - * Let's take a look at an example to make things clearer : + * Let's take a look at an example to make things clearer: *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
        * // they can be public field or properties, both can be extracted.
        * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    @@ -1572,7 +1793,7 @@ public AbstractListAssert, Object, ObjectAssert, Object, ObjectAssert, Tuple, ObjectAssert> extracting(@SuppressWarnings("unchecked") Function... extractors) {
    +    return doExtracting(extractors);
    +  }
    +
    +  @SafeVarargs
    +  private final AbstractListAssert, Tuple, ObjectAssert> doExtracting(Function... extractors) {
         // combine all extractors into one function
         Function tupleExtractor = objectToExtractValueFrom -> new Tuple(Stream.of(extractors)
                                                                                               .map(extractor -> extractor.apply(objectToExtractValueFrom))
    @@ -1605,19 +1831,72 @@ public AbstractListAssert, Tuple, ObjectAssert>
         return newListAssertInstanceForMethodsChangingElementType(tuples);
       }
     
    +  /**
    +   * Use the given {@link Function}s to map the {@link Iterable}'s elements into a {@link List} of {@link Tuple}s
    +   * (a simple data structure containing the mapped values), this new list becoming the object under test.
    +   * 

    + * This allows you to test values from the {@link Iterable}'s elements instead of testing the elements themselves, which + * sometimes can be much less work! + *

    + * The {@link Tuple} data correspond to the extracted values from the Iterable's elements, for instance if you pass functions + * mapping "id", "name" and "email" values then each {@code Tuple} data will be composed of an id, a name and an email + * mapped from the element of the initial Iterable (the Tuple's data order is the same as the given functions order). + *

    + * Let's take a look at an example to make things clearer: + *

     // Build a list of TolkienCharacter, a TolkienCharacter has a name, and age and a Race (a specific class)
    +   * // they can be public field or properties, both can be extracted.
    +   * List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
    +   *
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Frodo", 33, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Sam", 38, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Gandalf", 2020, MAIA));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Legolas", 1000, ELF));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Pippin", 28, HOBBIT));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Gimli", 139, DWARF));
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN);
    +   * fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN));
    +   *
    +   * // let's verify 'name', 'age' and Race of some TolkienCharacter in fellowshipOfTheRing:
    +   * assertThat(fellowshipOfTheRing).map(TolkienCharacter::getName,
    +   *                                     character -> character.getAge(),
    +   *                                     TolkienCharacter::getRace)
    +   *                                .containsOnly(tuple("Frodo", 33, HOBBIT),
    +   *                                              tuple("Sam", 38, HOBBIT),
    +   *                                              tuple("Gandalf", 2020, MAIA),
    +   *                                              tuple("Legolas", 1000, ELF),
    +   *                                              tuple("Pippin", 28, HOBBIT),
    +   *                                              tuple("Gimli", 139, DWARF),
    +   *                                              tuple("Aragorn", 87, MAN),
    +   *                                              tuple("Boromir", 37, MAN));
    + * You can use lambda expression or a method reference to extract the expected values. + *

    + * Use {@link Tuple#tuple(Object...)} to initialize the expected values. + *

    + * Note that the order of the extracted tuples list is consistent with the iteration order of the Iterable under test, + * for example if it's a {@link HashSet}, you won't be able to make any assumptions on the extracted tuples order. + * + * @param mappers the mapper functions to extract a value from an element of the Iterable under test. + * @return a new assertion object whose object under test is the list of Tuples containing the extracted values. + * @since 3.19.0 + */ + @CheckReturnValue + public AbstractListAssert, Tuple, ObjectAssert> map(@SuppressWarnings("unchecked") Function... mappers) { + return doExtracting(mappers); + } + /** * Extract the given property/field values from each {@code Iterable}'s element and * flatten the extracted values in a list that is used as the new object under test. *

    - * Given 2 properties, if the extracted values were not flattened, instead having a simple list like : - *

    element1.value1, element1.value2, element2.value1, element2.value2, ...  
    - * ... we would get a list of list : - *
    list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    + * Given 2 properties, if the extracted values were not flattened, instead having a simple list like: + *
      element1.value1, element1.value2, element2.value1, element2.value2, ...  
    + * ... we would get a list of list: + *
      list(element1.value1, element1.value2), list(element2.value1, element2.value2), ...  
    *

    - * Code example: + * Example: *

     // fellowshipOfTheRing is a List<TolkienCharacter>
        *
    -   * // values are extracted in order and flattened : age1, name1, age2, name2, age3 ...
    +   * // values are extracted in order and flattened: age1, name1, age2, name2, age3 ...
        * assertThat(fellowshipOfTheRing).flatExtracting("age", "name")
        *                                .contains(33 ,"Frodo",
        *                                          1000, "Legolas",
    @@ -2386,7 +2665,7 @@ public SELF filteredOnNull(String propertyOrFieldName) {
        * Filters the iterable under test keeping only elements having a property or field matching the filter expressed with
        * the {@link FilterOperator}, the property/field is specified by {@code propertyOrFieldName} parameter.
        * 

    - * The existing filters are : + * The existing filters are: *