Skip to content

Commit

Permalink
Merge pull request #1540 from robstoll/feature/#1400-notToBeAnInstanceOf
Browse files Browse the repository at this point in the history
Feature/#1400 not to be an instance of
  • Loading branch information
robstoll authored Sep 13, 2023
2 parents af500c7 + 3cca580 commit 7da8aa3
Show file tree
Hide file tree
Showing 17 changed files with 437 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,41 @@ internal fun <TSub : Any> Expect<*>.toBeAnInstanceOf(
inline fun <reified TSub : Any> Expect<*>.toBeAnInstanceOf(noinline assertionCreator: Expect<TSub>.() -> Unit): Expect<TSub> =
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 <reified TNotExpected : Any> 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 <T> Expect<T>.notToBeAnInstanceOf(type: KClass<*>, vararg otherTypes: KClass<*>): Expect<T> =
_logicAppend { notToBeAnInstanceOf(type glue otherTypes) }

/**
* Expects that the subject of `this` expectation is not equal to [expected] and [otherValues].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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<Int?, Int>(Expect<Int?>::notToEqualNull),
"notToEqualNull" to Companion::notToEqualNull,

Expand All @@ -56,36 +59,46 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS
companion object {
private fun toEqualNull(expect: Expect<Int?>) = expect.toEqual(null)

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfIntFeature(expect: Expect<out Any?>): Expect<Int> =
expect.toBeAnInstanceOf<Int>()

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfInt(expect: Expect<out Any?>, assertionCreator: Expect<Int>.() -> Unit): Expect<Int> =
private fun toBeAnInstanceOfInt(
expect: Expect<out Any?>,
assertionCreator: Expect<Int>.() -> Unit
): Expect<Int> =
expect.toBeAnInstanceOf<Int> { assertionCreator() }

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSuperTypeFeature(expect: Expect<out Any?>): Expect<SuperType> =
expect.toBeAnInstanceOf<SuperType>()

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSuperType(
expect: Expect<out Any?>,
assertionCreator: Expect<SuperType>.() -> Unit
): Expect<SuperType> =
expect.toBeAnInstanceOf<SuperType> { assertionCreator() }

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSubTypeFeature(expect: Expect<out Any?>): Expect<SubType> =
expect.toBeAnInstanceOf<SubType>()

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSubType(
expect: Expect<out Any?>,
assertionCreator: Expect<SubType>.() -> Unit
): Expect<SubType> =
expect.toBeAnInstanceOf<SubType> { assertionCreator() }

private fun notToBeAnInstanceOf(expect: Expect<Any>): Expect<*> =
expect.notToBeAnInstanceOf<SuperType>()

private fun notToBeAnInstanceOfKClass(expect: Expect<Any>, kClass: KClass<*>): Expect<Any> =
expect.notToBeAnInstanceOf(kClass)

private fun notToBeAnInstanceOfKClasses(
expect: Expect<Any>,
kClass: KClass<*>,
otherTypes: Array<out KClass<*>>
): Expect<Any> = expect.notToBeAnInstanceOf(kClass, *otherTypes)


private val andImmediate: KProperty1<Expect<Int>, Expect<Int>> = Expect<Int>::and
fun getAndImmediatePair(): Pair<String, Expect<Int>.() -> Expect<Int>> = andImmediate.name to Expect<Int>::and

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,28 @@ class AnyExpectationSamples {
}
}


@Test
fun notToBeAnInstanceOf() {
val n: Number = 16L
expect(n).notToBeAnInstanceOf<Int>()
fails {
// fails because n is actually instance of Long
expect(n).notToBeAnInstanceOf<Long>()
}
}


@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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -192,6 +193,57 @@ internal fun <TSub : Any> Expect<*>.toBeAnInstanceOf(
inline infix fun <reified TSub : Any> Expect<*>.toBeAnInstanceOf(noinline assertionCreator: Expect<TSub>.() -> Unit): Expect<TSub> =
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 <reified TNotExpected : Any> 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 <T> Expect<T>.notToBeAnInstanceOf(type: KClass<*>): Expect<T> =
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 <T> Expect<T>.notToBeAnInstanceOf(types: Types): Expect<T> =
_logicAppend { notToBeAnInstanceOf(types.toList()) }


/**
* Expects that the subject of `this` expectation is not equal to any value of [values].
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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<out KClass<*>>
) : VarArgHelper<KClass<*>>
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -313,3 +314,15 @@ fun <T> values(
reportOptionsInAnyOrderOnly: InAnyOrderOnlyReportingOptions.() -> Unit
): WithInAnyOrderOnlyReportingOptions<Values<T>> =
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)
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand All @@ -55,30 +59,24 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS
companion object {
private fun toEqualNull(expect: Expect<Int?>) = expect toEqual null

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfIntFeature(expect: Expect<out Any?>): Expect<Int> =
expect.toBeAnInstanceOf()

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfInt(expect: Expect<out Any?>, assertionCreator: Expect<Int>.() -> Unit): Expect<Int> =
expect.toBeAnInstanceOf<Int> { assertionCreator() }

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSuperTypeFeature(expect: Expect<out Any?>): Expect<SuperType> =
expect.toBeAnInstanceOf<SuperType>()

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSuperType(
expect: Expect<out Any?>,
assertionCreator: Expect<SuperType>.() -> Unit
): Expect<SuperType> =
expect.toBeAnInstanceOf<SuperType> { assertionCreator() }

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSubTypeFeature(expect: Expect<out Any?>): Expect<SubType> =
expect.toBeAnInstanceOf<SubType>()

@Suppress("RemoveExplicitTypeArguments")
private fun toBeAnInstanceOfSubType(
expect: Expect<out Any?>,
assertionCreator: Expect<SubType>.() -> Unit
Expand All @@ -90,6 +88,18 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS
e and o
}

private fun notToBeAnInstanceOf(expect: Expect<Any>): Expect<*> =
expect notToBeAnInstanceOf SuperType::class

private fun notToBeAnInstanceOfKClass(expect: Expect<Any>, kClass: KClass<*>): Expect<Any> =
expect notToBeAnInstanceOf kClass

private fun notToBeAnInstanceOfKClasses(
expect: Expect<Any>,
kClass: KClass<*>,
otherTypes: Array<out KClass<*>>
): Expect<Any> = expect notToBeAnInstanceOf types(kClass, *otherTypes)

private val andLazyName: KFunction2<Expect<Int>, Expect<Int>.() -> Unit, Expect<Int>> = Expect<Int>::and
private fun getAndLazyPair(): Pair<String, Expect<Int>.(Expect<Int>.() -> Unit) -> Expect<Int>> =
andLazyName.name to { e: Expect<Int>, assertionCreator: Expect<Int>.() -> Unit ->
Expand Down Expand Up @@ -148,6 +158,9 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS
a1 notToBeTheInstance 1.2
a1.toBeAnInstanceOf<Int>()
a1.toBeAnInstanceOf<Int> {}
a1.notToBeAnInstanceOf<Int>()
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) }
Expand All @@ -162,6 +175,9 @@ class AnyExpectationsSpec : ch.tutteli.atrium.specs.integration.AnyExpectationsS
a1b notToBeTheInstance 1.2
a1b.toBeAnInstanceOf<Int>()
a1b.toBeAnInstanceOf<Int> {}
a1b.notToBeAnInstanceOf<Int>()
a1b notToBeAnInstanceOf Int::class
a1b notToBeAnInstanceOf types(Int::class, Double::class)
a1b notToEqualOneOf values(1, 2)
a1b notToEqualOneIn listOf(1, 1.2)

Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -154,6 +152,36 @@ class AnyExpectationSamples {
}
}

@Test
fun notToBeAnInstanceOf() {
val n: Number = 16L
expect(n).notToBeAnInstanceOf<Int>()
fails {
// fails because n is actually instance of Long
expect(n).notToBeAnInstanceOf<Long>()
}
}

@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
Expand Down
Loading

0 comments on commit 7da8aa3

Please sign in to comment.