From b156fd7c36de762310c3661a274263c0b8b74c34 Mon Sep 17 00:00:00 2001 From: Anes Abismail Date: Wed, 29 Jul 2020 11:54:14 +0100 Subject: [PATCH 1/3] Add Any.isNoneOf and Any.isNotIn assertions --- .../atrium/api/fluent/en_GB/anyAssertions.kt | 32 +++++++++++++++++++ .../kotlin/ch/tutteli/atrium/logic/any.kt | 2 ++ .../ch/tutteli/atrium/logic/AnyAssertions.kt | 2 ++ .../atrium/logic/impl/DefaultAnyAssertions.kt | 10 ++++++ .../translations/DescriptionAnyAssertion.kt | 1 + .../translations/DescriptionAnyAssertion.kt | 3 +- 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt index 7d021d77f..610cc8676 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt @@ -3,6 +3,7 @@ package ch.tutteli.atrium.api.fluent.en_GB import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.logic.* import ch.tutteli.atrium.reporting.Reporter +import ch.tutteli.kbox.glue /** * Expects that the subject of the assertion is (equal to) [expected]. @@ -165,3 +166,34 @@ inline val Expect.and: Expect get() = this */ infix fun Expect.and(assertionCreator: Expect.() -> Unit): Expect = addAssertionsCreatedBy(assertionCreator) + +/** + * Expects that the subject of the assertion is not (equal to) [expected] and [otherValues]. + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.9.0 + */ +inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): Expect = + _logicAppend { + isNotIn(expected glue otherValues) + } + +/** + * Expects that the subject of the assertion is not (equal to) any value of [expected]. + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * @throws IllegalArgumentException in case the iterable is empty. + * + * @since 0.9.0 + */ +inline fun Expect.isNotIn(expected: Iterable): Expect { + require(expected.iterator().hasNext()) { "Iterable without elements are not allowed for this function." } + return _logicAppend { + isNotIn(expected.toList()) + } +} + + diff --git a/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt b/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt index cad49b4b0..f87301aca 100644 --- a/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt +++ b/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt @@ -27,3 +27,5 @@ fun AssertionContainer.notToBeNull(subType: KClass): ChangedSub //TODO restrict TSub with T once type parameter for upper bounds are supported: // https://youtrack.jetbrains.com/issue/KT-33262 is implemented fun AssertionContainer.isA(subType: KClass): ChangedSubjectPostStep = _anyImpl.isA(this, subType) + +fun AssertionContainer.isNotIn(expected: List): Assertion = _anyImpl.isNotIn(this, expected) diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt index ad94ac77d..b65096ae7 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt @@ -28,4 +28,6 @@ interface AnyAssertions { //TODO restrict TSub with T once type parameter for upper bounds are supported: // https://youtrack.jetbrains.com/issue/KT-33262 is implemented fun isA(container: AssertionContainer, subType: KClass): ChangedSubjectPostStep + + fun isNotIn(container: AssertionContainer, expected: List): Assertion } diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt index 39481e9b9..6c80946a2 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt @@ -1,6 +1,7 @@ package ch.tutteli.atrium.logic.impl import ch.tutteli.atrium.assertions.Assertion +import ch.tutteli.atrium.assertions.builders.assertionBuilder import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.domain.creating.changers.ChangedSubjectPostStep @@ -52,4 +53,13 @@ class DefaultAnyAssertions : AnyAssertions { container.changeSubject.reportBuilder() .downCastTo(subType) .build() + + override fun isNotIn(container: AssertionContainer, expected: List): Assertion { + val assertions = expected.map { assertionBuilder.representationOnly.failing.withRepresentation(it).build() } + return assertionBuilder.list + .withDescriptionAndEmptyRepresentation(IS_NONE_OF) + .withAssertions(assertions) + .build() + } + } diff --git a/translations/de_CH/atrium-translations-de_CH-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt b/translations/de_CH/atrium-translations-de_CH-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt index b96a00b67..dccb2cb40 100644 --- a/translations/de_CH/atrium-translations-de_CH-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt +++ b/translations/de_CH/atrium-translations-de_CH-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt @@ -12,4 +12,5 @@ enum class DescriptionAnyAssertion(override val value: String) : StringBasedTran IS_A("ist eine Instanz vom Typ"), IS_SAME("ist dieselbe Instanz wie"), IS_NOT_SAME("ist nicht dieselbe Instanz wie"), + IS_NONE_OF("ist nicht") } diff --git a/translations/en_GB/atrium-translations-en_GB-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt b/translations/en_GB/atrium-translations-en_GB-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt index 955459da7..1d36baae7 100644 --- a/translations/en_GB/atrium-translations-en_GB-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt +++ b/translations/en_GB/atrium-translations-en_GB-common/src/main/kotlin/ch/tutteli/atrium/translations/DescriptionAnyAssertion.kt @@ -11,5 +11,6 @@ enum class DescriptionAnyAssertion(override val value: String) : StringBasedTran NOT_TO_BE("does not equal"), IS_A("is instance of type"), IS_SAME("is the same instance as"), - IS_NOT_SAME("is not the same instance as") + IS_NOT_SAME("is not the same instance as"), + IS_NONE_OF("is none of") } From 3a3f0468e09ea925c61fcf0805fac725246a5052 Mon Sep 17 00:00:00 2001 From: Anes Abismail Date: Wed, 29 Jul 2020 22:27:31 +0100 Subject: [PATCH 2/3] Update Any.isNotIn to expect IterableLike --- .../atrium/api/fluent/en_GB/anyAssertions.kt | 22 +++++++------ .../atrium/api/infix/en_GB/anyAssertions.kt | 32 +++++++++++++++++++ .../kotlin/ch/tutteli/atrium/logic/any.kt | 2 +- .../ch/tutteli/atrium/logic/AnyAssertions.kt | 2 +- .../domain/builders/utils/VarArgHelper.kt | 25 ++++++++------- 5 files changed, 60 insertions(+), 23 deletions(-) diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt index 610cc8676..3fefd7da5 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt @@ -1,6 +1,8 @@ package ch.tutteli.atrium.api.fluent.en_GB import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.domain.builders.utils.iterableLikeToIterable +import ch.tutteli.atrium.domain.creating.typeutils.IterableLike import ch.tutteli.atrium.logic.* import ch.tutteli.atrium.reporting.Reporter import ch.tutteli.kbox.glue @@ -173,27 +175,27 @@ infix fun Expect.and(assertionCreator: Expect.() -> Unit): Expect = * @return An [Expect] for the current subject of the assertion. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. * - * @since 0.9.0 + * @since 0.13.0 */ inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): Expect = - _logicAppend { - isNotIn(expected glue otherValues) - } + _logicAppend { isNotIn(expected glue otherValues) } /** * Expects that the subject of the assertion is not (equal to) any value of [expected]. * + * Notice that a runtime check applies which assures that only [Iterable], [Sequence] or one of the [Array] types + * are passed. This function expects [IterableLike] (which is a typealias for [Any]) to avoid cluttering the API. + * * @return An [Expect] for the current subject of the assertion. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. * @throws IllegalArgumentException in case the iterable is empty. * - * @since 0.9.0 + * @since 0.13.0 */ -inline fun Expect.isNotIn(expected: Iterable): Expect { - require(expected.iterator().hasNext()) { "Iterable without elements are not allowed for this function." } - return _logicAppend { - isNotIn(expected.toList()) - } +inline fun Expect.isNotIn(expected: IterableLike): Expect { + val iterable = iterableLikeToIterable(expected) + require(iterable.iterator().hasNext()) { "Iterable without elements are not allowed for this function." } + return _logicAppend { isNotIn(iterable.toList()) } } diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt index 36361c9ee..c5d5c9577 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt @@ -1,8 +1,11 @@ package ch.tutteli.atrium.api.infix.en_GB import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.domain.builders.utils.iterableLikeToIterable +import ch.tutteli.atrium.domain.creating.typeutils.IterableLike import ch.tutteli.atrium.logic.* import ch.tutteli.atrium.reporting.Reporter +import ch.tutteli.kbox.glue /** * Expects that the subject of the assertion is (equal to) [expected]. @@ -224,3 +227,32 @@ inline val Expect.it: Expect get() : Expect = this * @since 0.12.0 */ inline val Expect.its: Expect get() : Expect = this + +/** + * Expects that the subject of the assertion is not (equal to) [expected] and [otherValues]. + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.13.0 + */ +inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): Expect = + _logicAppend { isNotIn(expected glue otherValues) } + +/** + * Expects that the subject of the assertion is not (equal to) any value of [expected]. + * + * Notice that a runtime check applies which assures that only [Iterable], [Sequence] or one of the [Array] types + * are passed. This function expects [IterableLike] (which is a typealias for [Any]) to avoid cluttering the API. + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * @throws IllegalArgumentException in case the iterable is empty. + * + * @since 0.13.0 + */ +inline fun Expect.isNotIn(expected: IterableLike): Expect { + val iterable = iterableLikeToIterable(expected) + require(iterable.iterator().hasNext()) { "Iterable without elements are not allowed for this function." } + return _logicAppend { isNotIn(iterable.toList()) } +} diff --git a/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt b/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt index f87301aca..35bb1c7eb 100644 --- a/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt +++ b/logic/atrium-logic-common/src/generated/kotlin/ch/tutteli/atrium/logic/any.kt @@ -28,4 +28,4 @@ fun AssertionContainer.notToBeNull(subType: KClass): ChangedSub // https://youtrack.jetbrains.com/issue/KT-33262 is implemented fun AssertionContainer.isA(subType: KClass): ChangedSubjectPostStep = _anyImpl.isA(this, subType) -fun AssertionContainer.isNotIn(expected: List): Assertion = _anyImpl.isNotIn(this, expected) +fun AssertionContainer.isNotIn(expected: Iterable): Assertion = _anyImpl.isNotIn(this, expected) diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt index b65096ae7..6ca323fac 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt @@ -29,5 +29,5 @@ interface AnyAssertions { // https://youtrack.jetbrains.com/issue/KT-33262 is implemented fun isA(container: AssertionContainer, subType: KClass): ChangedSubjectPostStep - fun isNotIn(container: AssertionContainer, expected: List): Assertion + fun isNotIn(container: AssertionContainer, expected: Iterable): Assertion } diff --git a/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt b/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt index 2e32f4e4f..a6f3ff0a2 100644 --- a/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt +++ b/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt @@ -37,18 +37,21 @@ interface VarArgHelper { * @throws IllegalArgumentException in case the iterable is empty. */ inline fun toVarArg(iterableLike: IterableLike): Pair> = + iterableToPair(iterableLikeToIterable(iterableLike)) + +inline fun iterableLikeToIterable(iterableLike: IterableLike): Iterable = when (iterableLike) { - is Sequence<*> -> iterableToPair(iterableLike.map { it as T }.asIterable()) - is Iterable<*> -> iterableToPair(iterableLike.map { it as T }) - is Array<*> -> iterableToPair(iterableLike.map { it as T }) - is CharArray -> iterableToPair(iterableLike.map { it as T }) - is ByteArray -> iterableToPair(iterableLike.map { it as T }) - is ShortArray -> iterableToPair(iterableLike.map { it as T }) - is IntArray -> iterableToPair(iterableLike.map { it as T }) - is LongArray -> iterableToPair(iterableLike.map { it as T }) - is FloatArray -> iterableToPair(iterableLike.map { it as T }) - is DoubleArray -> iterableToPair(iterableLike.map { it as T }) - is BooleanArray -> iterableToPair(iterableLike.map { it as T }) + is Sequence<*> -> iterableLike.map { it as T }.asIterable() + is Iterable<*> -> iterableLike.map { it as T } + is Array<*> -> iterableLike.map { it as T } + is CharArray -> iterableLike.map { it as T } + is ByteArray -> iterableLike.map { it as T } + is ShortArray -> iterableLike.map { it as T } + is IntArray -> iterableLike.map { it as T } + is LongArray -> iterableLike.map { it as T } + is FloatArray -> iterableLike.map { it as T } + is DoubleArray -> iterableLike.map { it as T } + is BooleanArray -> iterableLike.map { it as T } else -> throw IllegalArgumentException("toVarArg accepts arguments of types Iterable, Sequence, Array") } From bdd894b892d17f792cd6e7b55fe7d1a662932e5a Mon Sep 17 00:00:00 2001 From: Anes Abismail Date: Wed, 29 Jul 2020 22:28:29 +0100 Subject: [PATCH 3/3] Add tests for Any.isNoneOf and Any.isNotIn assertions and update error message, reflect IterableLike --- .../atrium/api/fluent/en_GB/anyAssertions.kt | 4 +- .../api/fluent/en_GB/AnyAssertionsSpec.kt | 12 +- .../atrium/api/infix/en_GB/anyAssertions.kt | 15 ++- .../api/infix/en_GB/AnyAssertionsSpec.kt | 30 ++++- .../atrium/logic/impl/DefaultAnyAssertions.kt | 10 +- .../domain/builders/utils/VarArgHelper.kt | 2 +- .../specs/integration/AnyAssertionsSpec.kt | 126 ++++++++++++++++-- .../specs/integration/IterableLikeSpec.kt | 2 +- 8 files changed, 178 insertions(+), 23 deletions(-) diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt index 3fefd7da5..ca9dafe5a 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyAssertions.kt @@ -177,7 +177,7 @@ infix fun Expect.and(assertionCreator: Expect.() -> Unit): Expect = * * @since 0.13.0 */ -inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): Expect = +fun Expect.isNoneOf(expected: T, vararg otherValues: T): Expect = _logicAppend { isNotIn(expected glue otherValues) } /** @@ -194,7 +194,7 @@ inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): E */ inline fun Expect.isNotIn(expected: IterableLike): Expect { val iterable = iterableLikeToIterable(expected) - require(iterable.iterator().hasNext()) { "Iterable without elements are not allowed for this function." } + require(iterable.iterator().hasNext()) { "IterableLike without elements are not allowed for this function." } return _logicAppend { isNotIn(iterable.toList()) } } diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyAssertionsSpec.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyAssertionsSpec.kt index 90bcce185..5955b2ca5 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyAssertionsSpec.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyAssertionsSpec.kt @@ -3,6 +3,7 @@ package ch.tutteli.atrium.api.fluent.en_GB import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.specs.feature0 import ch.tutteli.atrium.specs.fun1 +import ch.tutteli.atrium.specs.fun2 import ch.tutteli.atrium.specs.withFeatureSuffix import ch.tutteli.atrium.specs.withNullableSuffix import kotlin.reflect.KFunction2 @@ -25,6 +26,14 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( fun1(Expect::isNotSameAs), fun1(Expect::isNotSameAs).withNullableSuffix(), fun1(Expect::isNotSameAs).withNullableSuffix(), + fun2(Expect::isNoneOf), + fun2(Expect::isNoneOf), + fun2(Expect::isNoneOf).withNullableSuffix(), + fun2(Expect::isNoneOf).withNullableSuffix(), + fun1(Expect::isNotIn), + fun1(Expect::isNotIn), + fun1(Expect::isNotIn).withNullableSuffix(), + fun1(Expect::isNotIn).withNullableSuffix(), "${Expect::toBe.name}(null)" to Companion::toBeNull, fun1(Expect::toBeNullIfNullGivenElse), @@ -39,7 +48,8 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( "notToBeNull" to Companion::notToBeNull, getAndImmediatePair(), - getAndLazyPair() + getAndLazyPair(), + "⚬ " ) { companion object { diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt index c5d5c9577..c55942aa0 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyAssertions.kt @@ -1,11 +1,11 @@ package ch.tutteli.atrium.api.infix.en_GB +import ch.tutteli.atrium.api.infix.en_GB.creating.Values import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.domain.builders.utils.iterableLikeToIterable import ch.tutteli.atrium.domain.creating.typeutils.IterableLike import ch.tutteli.atrium.logic.* import ch.tutteli.atrium.reporting.Reporter -import ch.tutteli.kbox.glue /** * Expects that the subject of the assertion is (equal to) [expected]. @@ -229,15 +229,18 @@ inline val Expect.it: Expect get() : Expect = this inline val Expect.its: Expect get() : Expect = this /** - * Expects that the subject of the assertion is not (equal to) [expected] and [otherValues]. + * Expects that the subject of the assertion is not (equal to) in [values]. + * + * @param values The values which are not expected to be contained within the subject of the assertion + * -- use the function `values(t, ...)` to create a [Values]. * * @return An [Expect] for the current subject of the assertion. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. * * @since 0.13.0 */ -inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): Expect = - _logicAppend { isNotIn(expected glue otherValues) } +infix fun Expect.isNoneOf(values: Values): Expect = + _logicAppend { isNotIn(values.toList()) } /** * Expects that the subject of the assertion is not (equal to) any value of [expected]. @@ -251,8 +254,8 @@ inline fun Expect.isNoneOf(expected: T, vararg otherValues: T): E * * @since 0.13.0 */ -inline fun Expect.isNotIn(expected: IterableLike): Expect { +inline infix fun Expect.isNotIn(expected: IterableLike): Expect { val iterable = iterableLikeToIterable(expected) - require(iterable.iterator().hasNext()) { "Iterable without elements are not allowed for this function." } + require(iterable.iterator().hasNext()) { "IterableLike without elements are not allowed for this function." } return _logicAppend { isNotIn(iterable.toList()) } } diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyAssertionsSpec.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyAssertionsSpec.kt index a7e27c642..af3933028 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyAssertionsSpec.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyAssertionsSpec.kt @@ -2,6 +2,7 @@ package ch.tutteli.atrium.api.infix.en_GB import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.specs.fun1 +import ch.tutteli.atrium.specs.fun2 import ch.tutteli.atrium.specs.notImplemented import ch.tutteli.atrium.specs.testutils.WithAsciiReporter import ch.tutteli.atrium.specs.withFeatureSuffix @@ -25,6 +26,14 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( fun1(Expect::isNotSameAs), fun1(Expect::isNotSameAs).withNullableSuffix(), fun1(Expect::isNotSameAs).withNullableSuffix(), + fun2(Companion::isNoneOfInt), + fun2(Companion::isNoneOfDataClass), + fun2(Companion::isNoneOfIntNullable).withNullableSuffix(), + fun2(Companion::isNoneOfDataClassNullable).withNullableSuffix(), + fun1(Expect::isNotIn), + fun1(Expect::isNotIn), + fun1(Expect::isNotIn).withNullableSuffix(), + fun1(Expect::isNotIn).withNullableSuffix(), "${Expect::toBe.name}(null)" to Companion::toBeNull, fun1(Expect::toBeNullIfNullGivenElse), @@ -39,10 +48,11 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( "notToBeNull" to Companion::notToBeNull, getAndImmediatePair(), - getAndLazyPair() + getAndLazyPair(), + "- " ) { - companion object : WithAsciiReporter(){ + companion object : WithAsciiReporter() { private fun toBeNull(expect: Expect) = expect toBe null @Suppress("RemoveExplicitTypeArguments") @@ -81,6 +91,18 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( private fun notToBeNull(expect: Expect, assertionCreator: Expect.() -> Unit) = expect notToBeNull assertionCreator + + private fun isNoneOfInt(expect: Expect, expected: Int, otherValues: Array): Expect = + expect isNoneOf values(expected, *otherValues) + + private fun isNoneOfIntNullable(expect: Expect, expected: Int?, otherValues: Array): Expect = + expect isNoneOf values(expected, *otherValues) + + private fun isNoneOfDataClass(expect: Expect, expected: DataClass, otherValues: Array): Expect = + expect isNoneOf values(expected, *otherValues) + + private fun isNoneOfDataClassNullable(expect: Expect, expected: DataClass?, otherValues: Array): Expect = + expect isNoneOf values(expected, *otherValues) } @Suppress("unused") @@ -98,6 +120,8 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( a1 isNotSameAs 1.2 a1.isA() a1.isA {} + a1 isNoneOf values(1, 2) + a1 isNotIn listOf(1, 1.2) a1b toBe 1 a1b toBe 1.2 @@ -109,6 +133,8 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec( a1b isNotSameAs 1.2 a1b.isA() a1b.isA {} + a1b isNoneOf values(1, 2) + a1b isNotIn listOf(1, 1.2) a1b notToBeNull o toBe 1 a1b notToBeNull {} diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt index 6c80946a2..8bcd9a15e 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt @@ -2,6 +2,7 @@ package ch.tutteli.atrium.logic.impl import ch.tutteli.atrium.assertions.Assertion import ch.tutteli.atrium.assertions.builders.assertionBuilder +import ch.tutteli.atrium.core.trueProvider import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.domain.creating.changers.ChangedSubjectPostStep @@ -54,8 +55,13 @@ class DefaultAnyAssertions : AnyAssertions { .downCastTo(subType) .build() - override fun isNotIn(container: AssertionContainer, expected: List): Assertion { - val assertions = expected.map { assertionBuilder.representationOnly.failing.withRepresentation(it).build() } + override fun isNotIn(container: AssertionContainer, expected: Iterable): Assertion { + val assertions = expected.map { value -> + assertionBuilder.representationOnly + .withTest(container) { it != value } + .withRepresentation(value) + .build() + } return assertionBuilder.list .withDescriptionAndEmptyRepresentation(IS_NONE_OF) .withAssertions(assertions) diff --git a/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt b/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt index a6f3ff0a2..59fa6d396 100644 --- a/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt +++ b/misc/deprecated/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/utils/VarArgHelper.kt @@ -52,7 +52,7 @@ inline fun iterableLikeToIterable(iterableLike: IterableLike): Itera is FloatArray -> iterableLike.map { it as T } is DoubleArray -> iterableLike.map { it as T } is BooleanArray -> iterableLike.map { it as T } - else -> throw IllegalArgumentException("toVarArg accepts arguments of types Iterable, Sequence, Array") + else -> throw IllegalArgumentException("iterableLikeToIterable accepts arguments of types Iterable, Sequence, Array") } inline fun iterableToPair(iterable: Iterable): Pair> { diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/AnyAssertionsSpec.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/AnyAssertionsSpec.kt index ebc7f26aa..5a83559bb 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/AnyAssertionsSpec.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/AnyAssertionsSpec.kt @@ -32,6 +32,14 @@ abstract class AnyAssertionsSpec( isNotSameDataClass: Fun1, isNotSameNullableInt: Fun1, isNotSameNullableDataClass: Fun1, + isNoneOfInt: Fun2>, + isNoneOfDataClass: Fun2>, + isNoneOfNullableInt: Fun2>, + isNoneOfNullableDataClass: Fun2>, + isNotInInt: Fun1>, + isNotInDataClass: Fun1>, + isNotInNullableInt: Fun1>, + isNotInNullableDataClass: Fun1>, toBeNull: Fun0, toBeNullIfNullGivenElse: Fun1.() -> Unit)?>, @@ -49,6 +57,7 @@ abstract class AnyAssertionsSpec( andPair: Fun0, andLazyPair: Fun1.() -> Unit>, + listBulletPoint: String, describePrefix: String = "[Atrium] " ) : Spek({ @@ -58,6 +67,8 @@ abstract class AnyAssertionsSpec( notToBeInt.forSubjectLess(1), isSameInt.forSubjectLess(1), isNotSameInt.forSubjectLess(1), + isNoneOfInt.forSubjectLess(1, emptyArray()), + isNotInInt.forSubjectLess(listOf(1)), andPair.forSubjectLess(), andLazyPair.forSubjectLess { toBe(1) } ) {}) @@ -68,6 +79,8 @@ abstract class AnyAssertionsSpec( notToBeNullableInt.forSubjectLess(1), isSameNullableInt.forSubjectLess(1), isNotSameNullableInt.forSubjectLess(1), + isNoneOfNullableInt.forSubjectLess(1, emptyArray()), + isNotInNullableInt.forSubjectLess(listOf(1)), toBeNull.forSubjectLess(), isAIntFeature.forSubjectLess(), isAInt.forSubjectLess { toBe(1) }, @@ -107,13 +120,17 @@ abstract class AnyAssertionsSpec( toBe: Fun1, notToBe: Fun1, isSame: Fun1, - isNotSame: Fun1 + isNotSame: Fun1, + isNoneOf: Fun2>, + isNotIn: Fun1> ) { context(description) { val toBeFun = toBe.lambda val notToBeFun = notToBe.lambda val isSameFun = isSame.lambda val isNotSameFun = isNotSame.lambda + val isNoneOfFun = isNoneOf.lambda + val isNotInFun = isNotIn.lambda context("one equals the other") { it("${toBe.name} does not throw") { @@ -151,6 +168,36 @@ abstract class AnyAssertionsSpec( expectSubject.isNotSameFun(2) } } + context("one equals only one of the others") { + it("${isNoneOf.name} throws AssertionError") { + expect { + expectSubject.isNoneOfFun(1, arrayOf(2)) + }.toThrow { + message { + contains(IS_NONE_OF.getDefault(), "${listBulletPoint}1") + containsNot("$listBulletPoint 2") + } + } + } + it("${isNotIn.name} throws AssertionError") { + expect { + expectSubject.isNotInFun(listOf(1, 2)) + }.toThrow { + message { + contains(IS_NONE_OF.getDefault(), "${listBulletPoint}1") + containsNot("$listBulletPoint 2") + } + } + } + } + context("one does not equal to any of the others") { + it("${isNoneOf.name} does not throw") { + expectSubject.isNoneOfFun(2, arrayOf(3)) + } + it("${isNotIn.name} does not throw") { + expectSubject.isNotInFun(listOf(2, 3)) + } + } } } @@ -161,12 +208,16 @@ abstract class AnyAssertionsSpec( notToBe: Fun1, isSame: Fun1, isNotSame: Fun1, + isNoneOf: Fun2>, + isNotIn: Fun1>, test: DataClass ) { val toBeFun = toBe.lambda val notToBeFun = notToBe.lambda val isSameFun = isSame.lambda val isNotSameFun = isNotSame.lambda + val isNoneOfFun = isNoneOf.lambda + val isNotInFun = isNotIn.lambda context(description) { context("same") { @@ -186,6 +237,16 @@ abstract class AnyAssertionsSpec( expectSubject.isNotSameFun(test) }.toThrow() } + it("${isNoneOf.name} throws AssertionError") { + expect { + expectSubject.isNoneOfFun(test, emptyArray()) + }.toThrow { messageContains(IS_NONE_OF.getDefault()) } + } + it("${isNotIn.name} throws AssertionError") { + expect { + expectSubject.isNotInFun(listOf(test)) + }.toThrow { messageContains(IS_NONE_OF.getDefault()) } + } } context("not same but one equals the other") { val other = DataClass(true) @@ -205,6 +266,16 @@ abstract class AnyAssertionsSpec( it("${isNotSame.name} does not throw") { expectSubject.isNotSameFun(other) } + it("${isNoneOf.name} throws AssertionError") { + expect { + expectSubject.isNoneOfFun(other, emptyArray()) + }.toThrow { messageContains(IS_NONE_OF.getDefault()) } + } + it("${isNotIn.name} throws AssertionError") { + expect { + expectSubject.isNotInFun(listOf(other)) + }.toThrow { messageContains(IS_NONE_OF.getDefault()) } + } } context("one does not equal the other") { val other = DataClass(false) @@ -224,6 +295,12 @@ abstract class AnyAssertionsSpec( it("${isNotSame.name} does not throw") { expectSubject.isNotSameFun(other) } + it("${isNoneOf.name} does not throw") { + expectSubject.isNoneOfFun(other, emptyArray()) + } + it("${isNotIn.name} does not throw") { + expectSubject.isNotInFun(listOf(other)) + } } } } @@ -234,13 +311,18 @@ abstract class AnyAssertionsSpec( notToBe: Fun1, isSame: Fun1, isNotSame: Fun1, - value: T + isNoneOf: Fun2>, + isNotIn: Fun1>, + value: T, + emptyArray: Array ) { val toBeFun = toBe.lambda val notToBeFun = notToBe.lambda val isSameFun = isSame.lambda val isNotSameFun = isNotSame.lambda + val isNoneOfFun = isNoneOf.lambda + val isNotInFun = isNotIn.lambda val expectSubject = expect(null as T?) context(description) { @@ -261,6 +343,16 @@ abstract class AnyAssertionsSpec( expectSubject.isNotSameFun(null) }.toThrow { messageContains(IS_NOT_SAME.getDefault()) } } + it("${isNoneOf.name} throws AssertionError") { + expect { + expectSubject.isNoneOfFun(null, emptyArray) + }.toThrow { messageContains(IS_NONE_OF.getDefault()) } + } + it("${isNotIn.name} throws AssertionError") { + expect { + expectSubject.isNotInFun(listOf(null)) + }.toThrow { messageContains(IS_NONE_OF.getDefault()) } + } } context("one does not equal the other") { it("${toBe.name} throws AssertionError") { @@ -281,19 +373,27 @@ abstract class AnyAssertionsSpec( it("${isNotSame.name} does not throw") { expectSubject.isNotSameFun(value) } + it("${isNoneOf.name} does not throw") { + expectSubject.isNoneOfFun(value, emptyArray) + } + it("${isNotIn.name} does not throw") { + expectSubject.isNotInFun(listOf(value)) + } } } } - describeFun(toBeInt, notToBeInt, isSameInt, isNotSameInt) { - checkInt("primitive", expect(1), toBeInt, notToBeInt, isSameInt, isNotSameInt) + describeFun(toBeInt, notToBeInt, isSameInt, isNotSameInt, isNoneOfInt, isNotInInt) { + checkInt("primitive", expect(1), toBeInt, notToBeInt, isSameInt, isNotSameInt, isNoneOfInt, isNotInInt) checkInt( "nullable primitive", expect(1 as Int?), toBeNullableInt, notToBeNullableInt, isSameNullableInt, - isNotSameNullableInt + isNotSameNullableInt, + isNoneOfNullableInt, + isNotInNullableInt ) val subject = DataClass(true) @@ -304,6 +404,8 @@ abstract class AnyAssertionsSpec( notToBeDataClass, isSameDataClass, isNotSameDataClass, + isNoneOfDataClass, + isNotInDataClass, subject ) checkDataClass( @@ -313,6 +415,8 @@ abstract class AnyAssertionsSpec( notToBeNullableDataClass, isSameNullableDataClass, isNotSameNullableDataClass, + isNoneOfNullableDataClass, + isNotInNullableDataClass, subject ) @@ -322,7 +426,10 @@ abstract class AnyAssertionsSpec( notToBeNullableInt, isSameNullableInt, isNotSameNullableInt, - 2 + isNoneOfNullableInt, + isNotInNullableInt, + 2, + emptyArray() ) checkNull( "null as DataClass?", @@ -330,7 +437,10 @@ abstract class AnyAssertionsSpec( notToBeNullableDataClass, isSameNullableDataClass, isNotSameNullableDataClass, - subject + isNoneOfNullableDataClass, + isNotInNullableDataClass, + subject, + emptyArray() ) } @@ -503,7 +613,7 @@ abstract class AnyAssertionsSpec( expect(i).notToBeNullFun { isGreaterThan(2); isLessThan(0) } }.toThrow { messageContains(IS_GREATER_THAN.getDefault()) - if(hasExtraHint) messageContains(IS_LESS_THAN.getDefault()) + if (hasExtraHint) messageContains(IS_LESS_THAN.getDefault()) } } } diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableLikeSpec.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableLikeSpec.kt index 1b1cf4f32..cebd74826 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableLikeSpec.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableLikeSpec.kt @@ -38,7 +38,7 @@ abstract class IterableLikeSpec( expect { expect(subject).funIterableLike("test") }.toThrow { - messageContains("toVarArg accepts arguments of types Iterable, Sequence, Array") + messageContains("iterableLikeToIterable accepts arguments of types Iterable, Sequence, Array") } } }