diff --git a/apis/fluent/atrium-api-fluent/src/commonMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyExpectations.kt b/apis/fluent/atrium-api-fluent/src/commonMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyExpectations.kt index 9301e5bfa7..e56dc1fcf2 100644 --- a/apis/fluent/atrium-api-fluent/src/commonMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyExpectations.kt +++ b/apis/fluent/atrium-api-fluent/src/commonMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/anyExpectations.kt @@ -180,6 +180,41 @@ internal fun Expect<*>.toBeAnInstanceOf( inline fun Expect<*>.toBeAnInstanceOf(noinline assertionCreator: Expect.() -> Unit): Expect = toBeAnInstanceOf(TSub::class).transformAndAppend(assertionCreator) +/** + * Expects that the subject of `this` expectation *is not a* [TNotExpected] (the same type or a sub-type). + * + * Notice, this function has only one type parameter in order that you do not have to restate the type of the current + * subject. But that also means that we need to return `Expect<*>` or in other words, we loose the type of the subject. + * Which means, if you want to state a further expectation after [notToBeAnInstanceOf] then you most likely want to + * use the overload which expects one (or more) [KClass] instead which keeps the type of the initial subject. + * + * @param TNotExpected the type which we do not expect to be the same or a super-type of the subject of `this` + * expectation. + * @return an [Expect] for the subject of `this` expectation but untyped (with a star projection). + * + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.AnyExpectationSamples.notToBeAnInstanceOf + * + * @since 1.1.0 + */ +inline fun Expect<*>.notToBeAnInstanceOf(): Expect<*> = + notToBeAnInstanceOf(TNotExpected::class) + +/** + * Expects that the subject of `this` expectation *is not* the given [type] neither one of the [otherTypes] + * (the same type or a sub-type). + * + * @param type one of the types which we do not expect to be the same or a super-type of the subject of `this` + * expectation. + * @param otherTypes all types which we do not expect to be the same or a super-type of the subject of `this` + * expectation. + * @return an [Expect] for the subject of `this` expectation. + * + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.AnyExpectationSamples.notToBeAnInstanceOfKClasses + * + * @since 1.1.0 + */ +fun Expect.notToBeAnInstanceOf(type: KClass<*>, vararg otherTypes: KClass<*>): Expect = + _logicAppend { notToBeAnInstanceOf(type glue otherTypes) } /** * Expects that the subject of `this` expectation is not equal to [expected] and [otherValues]. diff --git a/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyExpectationsSpec.kt b/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyExpectationsSpec.kt index 40a884baf5..4fdc565b5b 100644 --- a/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyExpectationsSpec.kt +++ b/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/AnyExpectationsSpec.kt @@ -6,6 +6,7 @@ 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.KClass import kotlin.reflect.KFunction2 import kotlin.reflect.KProperty1 @@ -45,7 +46,9 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS "toBeAnInstanceOf" to Companion::toBeAnInstanceOfSuperType, ("toBeAnInstanceOf" to Companion::toBeAnInstanceOfSubTypeFeature).withFeatureSuffix(), "toBeAnInstanceOf" to Companion::toBeAnInstanceOfSubType, - + "notToBeAnInstanceOf" to Companion::notToBeAnInstanceOf, + "notToBeAnInstanceOf with kClass" to Companion::notToBeAnInstanceOfKClass, + "notToBeAnInstanceOf with kClasses" to Companion::notToBeAnInstanceOfKClasses, feature0(Expect::notToEqualNull), "notToEqualNull" to Companion::notToEqualNull, @@ -56,36 +59,46 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS companion object { private fun toEqualNull(expect: Expect) = expect.toEqual(null) - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfIntFeature(expect: Expect): Expect = expect.toBeAnInstanceOf() - @Suppress("RemoveExplicitTypeArguments") - private fun toBeAnInstanceOfInt(expect: Expect, assertionCreator: Expect.() -> Unit): Expect = + private fun toBeAnInstanceOfInt( + expect: Expect, + assertionCreator: Expect.() -> Unit + ): Expect = expect.toBeAnInstanceOf { assertionCreator() } - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSuperTypeFeature(expect: Expect): Expect = expect.toBeAnInstanceOf() - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSuperType( expect: Expect, assertionCreator: Expect.() -> Unit ): Expect = expect.toBeAnInstanceOf { assertionCreator() } - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSubTypeFeature(expect: Expect): Expect = expect.toBeAnInstanceOf() - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSubType( expect: Expect, assertionCreator: Expect.() -> Unit ): Expect = expect.toBeAnInstanceOf { assertionCreator() } + private fun notToBeAnInstanceOf(expect: Expect): Expect<*> = + expect.notToBeAnInstanceOf() + + private fun notToBeAnInstanceOfKClass(expect: Expect, kClass: KClass<*>): Expect = + expect.notToBeAnInstanceOf(kClass) + + private fun notToBeAnInstanceOfKClasses( + expect: Expect, + kClass: KClass<*>, + otherTypes: Array> + ): Expect = expect.notToBeAnInstanceOf(kClass, *otherTypes) + + private val andImmediate: KProperty1, Expect> = Expect::and fun getAndImmediatePair(): Pair.() -> Expect> = andImmediate.name to Expect::and diff --git a/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt b/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt index 4c62df037b..e62ada933d 100644 --- a/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt +++ b/apis/fluent/atrium-api-fluent/src/commonTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/AnyExpectationSamples.kt @@ -166,6 +166,28 @@ class AnyExpectationSamples { } } + + @Test + fun notToBeAnInstanceOf() { + val n: Number = 16L + expect(n).notToBeAnInstanceOf() + fails { + // fails because n is actually instance of Long + expect(n).notToBeAnInstanceOf() + } + } + + + @Test + fun notToBeAnInstanceOfKClasses() { + val n: Number = 16L + expect(n).notToBeAnInstanceOf(Int::class, Float::class, Double::class) + fails { + // fails because n is actually instance of Long + expect(n).notToBeAnInstanceOf(Int::class, Long::class) + } + } + @Test fun notToEqualOneOf() { expect(99).notToEqualOneOf(1, 2, 3, 4) diff --git a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyExpectations.kt b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyExpectations.kt index d4f86d1ea7..806a003e34 100644 --- a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyExpectations.kt +++ b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/anyExpectations.kt @@ -1,5 +1,6 @@ package ch.tutteli.atrium.api.infix.en_GB +import ch.tutteli.atrium.api.infix.en_GB.creating.Types import ch.tutteli.atrium.api.infix.en_GB.creating.Values import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.creating.Expect @@ -192,6 +193,57 @@ internal fun Expect<*>.toBeAnInstanceOf( inline infix fun Expect<*>.toBeAnInstanceOf(noinline assertionCreator: Expect.() -> Unit): Expect = toBeAnInstanceOf(TSub::class).transformAndAppend(assertionCreator) + +/** + * Expects that the subject of `this` expectation *is not a* [TNotExpected] (the same type or a sub-type). + * + * Notice, this function has only one type parameter in order that you do not have to restate the type of the current + * subject. But that also means that we need to return `Expect<*>` or in other words, we loose the type of the subject. + * Which means, if you want to state a further expectation after [notToBeAnInstanceOf] then you most likely want to + * use the overload which expects one (or more) [KClass] instead which keeps the type of the initial subject. + * + * @param TNotExpected the type which we do not expect to be the same or a super-type of the subject of `this` + * expectation. + * @return an [Expect] for the subject of `this` expectation but untyped (with a star projection). + * + * @sample ch.tutteli.atrium.api.infix.en_GB.samples.AnyExpectationSamples.notToBeAnInstanceOf + * + * @since 1.1.0 + */ +inline fun Expect<*>.notToBeAnInstanceOf(): Expect<*> = + notToBeAnInstanceOf(TNotExpected::class) + +/** + * Expects that the subject of `this` expectation *is not* one of the given [types] + * (the same type or a sub-type). + * + * @param type the type which we do not expect to be the same or a super-type of the subject of `this` + * expectation. + * @return an [Expect] for the subject of `this` expectation. + * + * @sample ch.tutteli.atrium.api.infix.en_GB.samples.AnyExpectationSamples.notToBeAnInstanceOfKClass + * + * @since 1.1.0 + */ +infix fun Expect.notToBeAnInstanceOf(type: KClass<*>): Expect = + notToBeAnInstanceOf(types(type)) + +/** + * Expects that the subject of `this` expectation *is not* one of the given [types] + * (the same type or a sub-type). + * + * @param types the types which we do not expect to be the same or a super-type of the subject of `this` + * expectation. + * @return an [Expect] for the subject of `this` expectation. + * + * @sample ch.tutteli.atrium.api.infix.en_GB.samples.AnyExpectationSamples.notToBeAnInstanceOfKClasses + * + * @since 1.1.0 + */ +infix fun Expect.notToBeAnInstanceOf(types: Types): Expect = + _logicAppend { notToBeAnInstanceOf(types.toList()) } + + /** * Expects that the subject of `this` expectation is not equal to any value of [values]. * diff --git a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Types.kt b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Types.kt new file mode 100644 index 0000000000..a4244248ba --- /dev/null +++ b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Types.kt @@ -0,0 +1,17 @@ +package ch.tutteli.atrium.api.infix.en_GB.creating + +import ch.tutteli.atrium.logic.utils.Group +import ch.tutteli.atrium.logic.utils.VarArgHelper +import kotlin.reflect.KClass + +/** + * Represents a [Group] of multiple values. + * + * Use the function `types(kclass, ...)` to create this representation. + * + * @since 1.1.0 + */ +class Types internal constructor( + override val expected: KClass<*>, + override val otherExpected: Array> +) : VarArgHelper> diff --git a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Values.kt b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Values.kt index 57f4ebe3f0..a75d89f5ed 100644 --- a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Values.kt +++ b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/Values.kt @@ -2,7 +2,7 @@ package ch.tutteli.atrium.api.infix.en_GB.creating import ch.tutteli.atrium.logic.utils.Group import ch.tutteli.atrium.logic.utils.VarArgHelper - +//TODO 1.1.0 check if Note is still valid, I think we don't make it invariant, so remove the note again /** * Represents a [Group] of multiple values. * diff --git a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/helperFunctions.kt b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/helperFunctions.kt index adb1b75e27..bf458a9ba7 100644 --- a/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/helperFunctions.kt +++ b/apis/infix/atrium-api-infix/src/commonMain/kotlin/ch/tutteli/atrium/api/infix/en_GB/helperFunctions.kt @@ -11,6 +11,7 @@ import ch.tutteli.atrium.logic.creating.iterablelike.contains.reporting.InAnyOrd import ch.tutteli.atrium.logic.creating.iterablelike.contains.reporting.InOrderOnlyReportingOptions import ch.tutteli.atrium.logic.creating.typeutils.IterableLike import ch.tutteli.atrium.logic.creating.typeutils.MapLike +import kotlin.reflect.KClass /** * Helper function to create an [All] based on the given [t] and [ts] @@ -313,3 +314,15 @@ fun values( reportOptionsInAnyOrderOnly: InAnyOrderOnlyReportingOptions.() -> Unit ): WithInAnyOrderOnlyReportingOptions> = WithInAnyOrderOnlyReportingOptions(reportOptionsInAnyOrderOnly, Values(value, otherValues)) + + +/** + * Helper function to create a [Types] based on the given [type] and [otherTypes] + * -- allows expressing `KClass<*>, vararg KClass<*>`. + * + * @since 1.1.0 + */ +fun types( + type: KClass<*>, + vararg otherTypes: KClass<*>, +): Types = Types(type, otherTypes) diff --git a/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyExpectationsSpec.kt b/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyExpectationsSpec.kt index 32b670228a..f31a6aca48 100644 --- a/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyExpectationsSpec.kt +++ b/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/AnyExpectationsSpec.kt @@ -6,6 +6,7 @@ import ch.tutteli.atrium.specs.fun2 import ch.tutteli.atrium.specs.notImplemented import ch.tutteli.atrium.specs.withFeatureSuffix import ch.tutteli.atrium.specs.withNullableSuffix +import kotlin.reflect.KClass import kotlin.reflect.KFunction2 class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsSpec( @@ -44,6 +45,9 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS "toBeAnInstanceOf" to Companion::toBeAnInstanceOfSuperType, ("toBeAnInstanceOf" to Companion::toBeAnInstanceOfSubTypeFeature).withFeatureSuffix(), "toBeAnInstanceOf" to Companion::toBeAnInstanceOfSubType, + "notToBeAnInstanceOf" to Companion::notToBeAnInstanceOf, + "notToBeAnInstanceOf with kClass" to Companion::notToBeAnInstanceOfKClass, + "notToBeAnInstanceOf with kClasses" to Companion::notToBeAnInstanceOfKClasses, ("notToEqualNull" to Companion::notToEqualNullFeature).withFeatureSuffix(), "notToEqualNull" to Companion::notToEqualNull, @@ -55,30 +59,24 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS companion object { private fun toEqualNull(expect: Expect) = expect toEqual null - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfIntFeature(expect: Expect): Expect = expect.toBeAnInstanceOf() - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfInt(expect: Expect, assertionCreator: Expect.() -> Unit): Expect = expect.toBeAnInstanceOf { assertionCreator() } - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSuperTypeFeature(expect: Expect): Expect = expect.toBeAnInstanceOf() - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSuperType( expect: Expect, assertionCreator: Expect.() -> Unit ): Expect = expect.toBeAnInstanceOf { assertionCreator() } - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSubTypeFeature(expect: Expect): Expect = expect.toBeAnInstanceOf() - @Suppress("RemoveExplicitTypeArguments") private fun toBeAnInstanceOfSubType( expect: Expect, assertionCreator: Expect.() -> Unit @@ -90,6 +88,18 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS e and o } + private fun notToBeAnInstanceOf(expect: Expect): Expect<*> = + expect notToBeAnInstanceOf SuperType::class + + private fun notToBeAnInstanceOfKClass(expect: Expect, kClass: KClass<*>): Expect = + expect notToBeAnInstanceOf kClass + + private fun notToBeAnInstanceOfKClasses( + expect: Expect, + kClass: KClass<*>, + otherTypes: Array> + ): Expect = expect notToBeAnInstanceOf types(kClass, *otherTypes) + private val andLazyName: KFunction2, Expect.() -> Unit, Expect> = Expect::and private fun getAndLazyPair(): Pair.(Expect.() -> Unit) -> Expect> = andLazyName.name to { e: Expect, assertionCreator: Expect.() -> Unit -> @@ -148,6 +158,9 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS a1 notToBeTheInstance 1.2 a1.toBeAnInstanceOf() a1.toBeAnInstanceOf {} + a1.notToBeAnInstanceOf() + a1 notToBeAnInstanceOf Int::class + a1 notToBeAnInstanceOf types(Int::class, Double::class) a1 notToEqualOneOf values(1, 2) a1 notToEqualOneIn listOf(1, 1.2) a1 because of("hello") { toEqual(1) } @@ -162,6 +175,9 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS a1b notToBeTheInstance 1.2 a1b.toBeAnInstanceOf() a1b.toBeAnInstanceOf {} + a1b.notToBeAnInstanceOf() + a1b notToBeAnInstanceOf Int::class + a1b notToBeAnInstanceOf types(Int::class, Double::class) a1b notToEqualOneOf values(1, 2) a1b notToEqualOneIn listOf(1, 1.2) diff --git a/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/AnyExpectationSamples.kt b/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/AnyExpectationSamples.kt index c4cd81f6a7..8d347df5a9 100644 --- a/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/AnyExpectationSamples.kt +++ b/apis/infix/atrium-api-infix/src/commonTest/kotlin/ch/tutteli/atrium/api/infix/en_GB/samples/AnyExpectationSamples.kt @@ -1,9 +1,7 @@ - package ch.tutteli.atrium.api.infix.en_GB.samples import ch.tutteli.atrium.api.infix.en_GB.* import ch.tutteli.atrium.api.verbs.expect -import ch.tutteli.atrium.creating.Expect import kotlin.test.Test class AnyExpectationSamples { @@ -154,6 +152,36 @@ class AnyExpectationSamples { } } + @Test + fun notToBeAnInstanceOf() { + val n: Number = 16L + expect(n).notToBeAnInstanceOf() + fails { + // fails because n is actually instance of Long + expect(n).notToBeAnInstanceOf() + } + } + + @Test + fun notToBeAnInstanceOfKClass() { + val n: Number = 16L + expect(n) notToBeAnInstanceOf Int::class + fails { + // fails because n is actually instance of Long + expect(n) notToBeAnInstanceOf Long::class + } + } + + @Test + fun notToBeAnInstanceOfKClasses() { + val n: Number = 16L + expect(n) notToBeAnInstanceOf types(Int::class, Float::class, Double::class) + fails { + // fails because n is actually instance of Long + expect(n) notToBeAnInstanceOf types(Int::class, Long::class) + } + } + @Test fun andFeature() { // `and` is just a filler word does not have any behaviour diff --git a/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt b/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt index 2a2e284b81..b2fbcd35f1 100644 --- a/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt +++ b/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/AnyAssertions.kt @@ -38,4 +38,6 @@ interface AnyAssertions { reason: String, assertionCreator: (Expect.() -> Unit) ): Assertion + + fun notToBeAnInstanceOf(container: AssertionContainer, notExpectedTypes: List>): Assertion } diff --git a/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/creating/transformers/SubjectChangerBuilder.kt b/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/creating/transformers/SubjectChangerBuilder.kt index e64f106b30..2fa6c934b6 100644 --- a/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/creating/transformers/SubjectChangerBuilder.kt +++ b/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/creating/transformers/SubjectChangerBuilder.kt @@ -81,7 +81,6 @@ interface SubjectChangerBuilder { .withTransformation { Option.someIf(subType.isInstance(it)) { subType.cast(it) } } - /** * Uses the given [description] and [representation] to represent the change by delegating to the other overload * which expects a [Translatable] instead of a [String]. diff --git a/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt b/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt index 7adbc6a064..b5685f0e8d 100644 --- a/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt +++ b/logic/atrium-logic/src/commonMain/kotlin/ch/tutteli/atrium/logic/impl/DefaultAnyAssertions.kt @@ -91,12 +91,27 @@ class DefaultAnyAssertions : AnyAssertions { assertionCreator: Expect.() -> Unit ): Assertion { val assertion = container.collect(assertionCreator) - return assertionBuilder.invisibleGroup.withAssertions( - assertion, - assertionBuilder.explanatoryGroup - .withInformationType(withIndent = false) - .withAssertion(assertionBuilder.createDescriptive(BECAUSE, Text(reason), falseProvider)) - .build() - ).build() + return assertionBuilder.invisibleGroup + .withAssertions( + assertion, + assertionBuilder.explanatoryGroup + .withInformationType(withIndent = false) + .withAssertion(assertionBuilder.createDescriptive(BECAUSE, Text(reason), falseProvider)) + .build() + ).build() } + + override fun notToBeAnInstanceOf( + container: AssertionContainer, + notExpectedTypes: List> + ): Assertion = assertionBuilder.invisibleGroup + .withAssertions( + notExpectedTypes.map { notExpectedType -> + container.createDescriptiveAssertion( + NOT_TO_BE_AN_INSTANCE_OF, + notExpectedType + ) { !notExpectedType.isInstance(it) } + } + ).build() + } diff --git a/logic/atrium-logic/src/generated/commonMain/ch/tutteli/atrium/logic/any.kt b/logic/atrium-logic/src/generated/commonMain/ch/tutteli/atrium/logic/any.kt index 0edded8ddf..0940315a3a 100644 --- a/logic/atrium-logic/src/generated/commonMain/ch/tutteli/atrium/logic/any.kt +++ b/logic/atrium-logic/src/generated/commonMain/ch/tutteli/atrium/logic/any.kt @@ -31,6 +31,9 @@ fun AssertionContainer.isNotIn(expected: Iterable): Assertion = impl.i fun AssertionContainer.because(reason: String, assertionCreator: (Expect.() -> Unit)): Assertion = impl.because(this, reason, assertionCreator) +fun AssertionContainer.notToBeAnInstanceOf(notExpectedTypes: List>): Assertion = impl.notToBeAnInstanceOf(this, notExpectedTypes) + + @OptIn(ExperimentalNewExpectTypes::class) private inline val AssertionContainer.impl: AnyAssertions get() = getImpl(AnyAssertions::class) { DefaultAnyAssertions() } diff --git a/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/integration/AnyExpectationsSpec.kt b/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/integration/AnyExpectationsSpec.kt index 010615de51..946d59d6b2 100644 --- a/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/integration/AnyExpectationsSpec.kt +++ b/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/integration/AnyExpectationsSpec.kt @@ -6,6 +6,7 @@ import ch.tutteli.atrium.assertions.DescriptiveAssertion import ch.tutteli.atrium.core.polyfills.format import ch.tutteli.atrium.core.polyfills.fullName import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.logic.utils.expectLambda import ch.tutteli.atrium.reporting.Text import ch.tutteli.atrium.specs.* import ch.tutteli.atrium.specs.integration.MapLikeToContainSpecBase.Companion.separator @@ -14,6 +15,7 @@ import ch.tutteli.atrium.translations.DescriptionComparableExpectation.TO_BE_GRE import ch.tutteli.atrium.translations.DescriptionComparableExpectation.TO_BE_LESS_THAN import org.spekframework.spek2.Spek import org.spekframework.spek2.style.specification.Suite +import kotlin.reflect.KClass abstract class AnyExpectationsSpec( toEqualInt: Fun1, @@ -53,6 +55,9 @@ abstract class AnyExpectationsSpec( toBeAnInstanceOfSuperType: Feature1.() -> Unit, SuperType>, toBeAnInstanceOfSubTypeFeature: Feature0, toBeAnInstanceOfSubType: Feature1.() -> Unit, SubType>, + notToBeInstanceOfSuperType: Feature0, + notToBeInstanceOfKClass: Feature1, Any>, + notToBeInstanceOfKClasses: Feature2, Array>, Any>, notToBeNullFeature: Feature0, notToBeNull: Feature1.() -> Unit, Int>, @@ -89,6 +94,18 @@ abstract class AnyExpectationsSpec( notToBeNullFeature.forSubjectLess(), notToBeNull.forSubjectLess { toEqual(1) } ) {}) + include(object : SubjectLessSpec( + "$describePrefix[Any] ", + notToBeInstanceOfSuperType.let { + it.name to expectLambda { it.lambda.invoke(this) } + }, + notToBeInstanceOfKClass.let { + it.name to expectLambda { it.lambda.invoke(this, Int::class) } + }, + notToBeInstanceOfKClasses.let { + it.name to expectLambda { it.lambda.invoke(this, Int::class, arrayOf(Long::class, String::class)) } + }, + ) {}) include(object : AssertionCreatorSpec( describePrefix, 1, @@ -764,6 +781,162 @@ abstract class AnyExpectationsSpec( } } + describeFun(notToBeInstanceOfSuperType) { + val notToBeInstanceOfSuperTypeFun = notToBeInstanceOfSuperType.lambda + val notToBeInstanceOfKClassFun = notToBeInstanceOfKClass.lambda + val notToBeInstanceOfKClassesFun = notToBeInstanceOfKClasses.lambda + context("subject is not in type hierarchy") { + it("${notToBeInstanceOfSuperType.name} -- does not throw") { + expect(1 as Any).notToBeInstanceOfSuperTypeFun() + } + it("${notToBeInstanceOfKClass.name} -- does not throw") { + expect(1 as Any).notToBeInstanceOfKClassFun(String::class) + } + it("${notToBeInstanceOfKClasses.name} -- does not throw") { + expect(1 as Any).notToBeInstanceOfKClassesFun(String::class, arrayOf(Long::class, Double::class)) + } + } + + context("subject is the same type") { + it("${notToBeInstanceOfSuperType.name} -- throws") { + expect { + expect(SuperType() as Any).notToBeInstanceOfSuperTypeFun() + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + } + } + } + it("${notToBeInstanceOfKClass.name} -- throws") { + expect { + expect(SuperType() as Any).notToBeInstanceOfKClassFun(SuperType::class) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + } + } + } + it("${notToBeInstanceOfKClasses.name} -- throws if first type is SuperType") { + expect { + expect(SuperType() as Any).notToBeInstanceOfKClassesFun( + SuperType::class, + arrayOf(Long::class, Double::class) + ) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + notToContain(Long::class.simpleName!!) + notToContain(Double::class.simpleName!!) + } + } + } + it("${notToBeInstanceOfKClasses.name} -- throws if one of the other types is SuperType") { + expect { + expect(SuperType() as Any).notToBeInstanceOfKClassesFun( + Int::class, + arrayOf(Long::class, SuperType::class) + ) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + notToContain(Int::class.simpleName!!) + notToContain(Long::class.simpleName!!) + } + } + } + } + + context("subject is a subtype") { + it("${notToBeInstanceOfSuperType.name} -- throws") { + expect { + expect(SubType() as Any).notToBeInstanceOfSuperTypeFun() + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + } + } + } + it("${notToBeInstanceOfKClass.name} -- throws") { + expect { + expect(SubType() as Any).notToBeInstanceOfKClassFun(SuperType::class) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + } + } + } + it("${notToBeInstanceOfKClasses.name} -- throws if first type is a sub-type") { + expect { + expect(SubType() as Any).notToBeInstanceOfKClassesFun( + SuperType::class, + arrayOf(Long::class, Double::class) + ) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + notToContain(Long::class.simpleName!!) + notToContain(Double::class.simpleName!!) + } + } + } + it("${notToBeInstanceOfKClasses.name} -- throws if one of the other types is a sub-type ") { + expect { + expect(SubType() as Any).notToBeInstanceOfKClassesFun( + Int::class, + arrayOf(SuperType::class, Double::class) + ) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperType::class.simpleName!!) + notToContain(Int::class.simpleName!!) + notToContain(Long::class.simpleName!!) + } + } + } + + it("${notToBeInstanceOfKClasses.name} -- throws and lists all types which violates the expectation") { + expect { + expect(SubType() as Any).notToBeInstanceOfKClassesFun( + SuperInterface::class, + arrayOf(Double::class, SuperType::class, SubType::class, Long::class) + ) + }.toThrow { + message { + toContain(NOT_TO_BE_AN_INSTANCE_OF.getDefault()) + toContain(SuperInterface::class.simpleName!!) + toContain(SuperType::class.simpleName!!) + toContain(SubType::class.simpleName!!) + notToContain(Double::class.simpleName!!) + notToContain(Long::class.simpleName!!) + } + } + } + } + + context("subject is a supertype") { + it("${notToBeInstanceOfSuperType.name} -- does not throw") { + expect(object : SuperInterface {} as Any).notToBeInstanceOfSuperTypeFun() + } + it("${notToBeInstanceOfKClass.name} -- does not throw") { + expect(object : SuperInterface {} as Any).notToBeInstanceOfKClassFun(SuperType::class) + } + it("${notToBeInstanceOfKClasses.name} -- does not throw") { + expect(object : SuperInterface {} as Any).notToBeInstanceOfKClassesFun( + SubType::class, + arrayOf(Long::class, Double::class) + ) + } + } + } + prefixedDescribe("property `${andPair.name}` immediate") { it("returns the same container") { val container = expect(1) @@ -823,6 +996,7 @@ abstract class AnyExpectationsSpec( }) { data class DataClass(val isWhatever: Boolean) - open class SuperType + interface SuperInterface + open class SuperType : SuperInterface class SubType : SuperType() } diff --git a/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/testUtils.kt b/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/testUtils.kt index 2269b648c3..e4b25d120a 100644 --- a/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/testUtils.kt +++ b/misc/atrium-specs/src/commonMain/kotlin/ch/tutteli/atrium/specs/testUtils.kt @@ -2,7 +2,6 @@ package ch.tutteli.atrium.specs -import ch.tutteli.atrium.core.polyfills.format import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.logic.utils.expectLambda import ch.tutteli.atrium.translations.DescriptionAnyExpectation @@ -182,6 +181,20 @@ fun unifySignatures( ) } +// TODO I cant make this work any way I try, it seems to me like I wont be able to unify like this +//@JvmName("unifySignatures0Feature") +//fun unifySignatures( +// f0: Feature0, +// f1: Feature1.() -> Unit, T> +//): List.(Expect.() -> Unit) -> Expect, Boolean>> { +// val f0WithSubAssertion: Expect.(Expect.() -> Unit) -> Expect = +// { f: Expect.() -> Unit -> (f0.lambda)() } +// return listOf( +// Triple(f0.name, f0WithSubAssertion, false), +// Triple(f1.name, f1.lambda, true) +// ) +//} + @JvmName("unifySignatures1Feature") fun unifySignatures( f0: Feature1, diff --git a/translations/atrium-translations-de_CH/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt b/translations/atrium-translations-de_CH/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt index 5cedd9f126..917b83e0c4 100644 --- a/translations/atrium-translations-de_CH/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt +++ b/translations/atrium-translations-de_CH/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt @@ -26,5 +26,8 @@ enum class DescriptionAnyExpectation(override val value: String) : StringBasedTr NOT_TO_EQUAL_ONE_IN("entspricht keinem in"), /** @since 0.18.0 */ - BECAUSE("denn") + BECAUSE("denn"), + + /** @since 1.1.0 */ + NOT_TO_BE_AN_INSTANCE_OF("ist nicht eine Instanz vom Typ") } diff --git a/translations/atrium-translations-en_GB/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt b/translations/atrium-translations-en_GB/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt index ecebba13ac..854f7f63e4 100644 --- a/translations/atrium-translations-en_GB/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt +++ b/translations/atrium-translations-en_GB/src/commonMain/kotlin/ch/tutteli/atrium/translations/DescriptionAnyExpectation.kt @@ -26,5 +26,8 @@ enum class DescriptionAnyExpectation(override val value: String) : StringBasedTr NOT_TO_EQUAL_ONE_IN("not to equal one in"), /** @since 0.18.0 */ - BECAUSE("because") + BECAUSE("because"), + + /** @since 1.1.0 */ + NOT_TO_BE_AN_INSTANCE_OF("not to be an instance of") }