transform List.get and Map.getExisting use fun instead of param obj

moreover:
- move param objects to own package so that they are not automatically
  available when one adds an `import ch.tutteli.atrium.api.infix.*`
  statement
- simplify MapAssertionsSpec by introducing mfun2 which covers most
  types (also in fluent-en_GB)
This commit is contained in:
Robert Stoll
2020-02-29 23:08:06 +01:00
parent 382ce93bdb
commit 7514f44844
23 changed files with 328 additions and 373 deletions

View File

@@ -4,21 +4,16 @@ import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.feature0
import ch.tutteli.atrium.specs.feature1
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.withFeatureSuffix
class Fun0AssertionsSpec : ch.tutteli.atrium.specs.integration.Fun0AssertionsSpec(
"toThrow" to Companion::toThrowFeature,
"toThrow" to Companion::toThrow,
("toThrow" to ::toThrowFeature).withFeatureSuffix(),
"toThrow" to ::toThrow,
feature0<() -> Int, Int>(Expect<() -> Int>::notToThrow),
feature1<() -> Int, Expect<Int>.() -> Unit, Int>(Expect<() -> Int>::notToThrow),
"", "» "
) {
companion object {
fun toThrowFeature(expect: Expect<out () -> Any?>) = expect.toThrow<IllegalArgumentException>()
fun toThrow(expect: Expect<out () -> Any?>, assertionCreator: Expect<IllegalArgumentException>.() -> Unit) =
expect.toThrow<IllegalArgumentException> { assertionCreator() }
}
@Suppress("unused", "UNUSED_VALUE", "UNUSED_VARIABLE")
private fun ambiguityTest() {
val a1: Expect<() -> Any?> = notImplemented()
@@ -37,3 +32,9 @@ class Fun0AssertionsSpec : ch.tutteli.atrium.specs.integration.Fun0AssertionsSpe
val r8: Expect<Int> = a2.notToThrow {}
}
}
private fun toThrowFeature(expect: Expect<out () -> Any?>) =
expect.toThrow<IllegalArgumentException>()
private fun toThrow(expect: Expect<out () -> Any?>, assertionCreator: Expect<IllegalArgumentException>.() -> Unit) =
expect.toThrow<IllegalArgumentException> { assertionCreator() }

View File

@@ -3,10 +3,11 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.withNullableSuffix
object IterableAllAssertionsSpec : ch.tutteli.atrium.specs.integration.IterableAllAssertionsSpec(
fun1(Expect<Iterable<Double>>::all),
fun1(Expect<Iterable<Double?>>::all),
fun1(Expect<Iterable<Double?>>::all).withNullableSuffix(),
"◆ ", "❗❗ ", "", "» ", "▶ ", "◾ "
) {
@Suppress("unused", "UNUSED_VALUE")

View File

@@ -2,46 +2,26 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.utils.mapArguments
import ch.tutteli.atrium.specs.fun0
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.*
import kotlin.jvm.JvmName
import kotlin.reflect.KFunction3
private fun <K, V, T> mfun2(
f: KFunction3<Expect<Map<out K, V>>, Pair<K, T>, Array<out Pair<K, T>>, Expect<Map<out K, V>>>
) = fun2(f)
class MapAssertionsSpec : ch.tutteli.atrium.specs.integration.MapAssertionsSpec(
fun2<Map<out String, Int>, Pair<String, Int>, Array<out Pair<String, Int>>>(Expect<Map<out String, Int>>::contains),
fun2<Map<out String?, Int?>, Pair<String?, Int?>, Array<out Pair<String?, Int?>>>(Expect<Map<out String?, Int?>>::contains),
"${containsKeyWithValueAssertionsFun.name} ${KeyValue::class.simpleName}" to Companion::containsKeyValue,
"${containsKeyWithNullableValueAssertionsFun.name} ${KeyValue::class.simpleName}" to Companion::containsNullable,
mfun2<String, Int, Int>(Expect<Map<out String, Int>>::contains),
mfun2<String?, Int?, Int?>(Expect<Map<out String?, Int?>>::contains).withNullableSuffix(),
mfun2<String, Int, Expect<Int>.() -> Unit>(::contains).adjustName { "$it ${KeyValue::class.simpleName}" },
mfun2<String?, Int?, (Expect<Int>.() -> Unit)?>(::contains).adjustName { "$it ${KeyValue::class.simpleName}" }.withNullableSuffix(),
fun1(Expect<Map<out String, *>>::containsKey),
fun1(Expect<Map<out String?, *>>::containsKey),
fun1(Expect<Map<out String?, *>>::containsKey).withNullableSuffix(),
fun1(Expect<Map<out String, *>>::containsNotKey),
fun1(Expect<Map<out String?, *>>::containsNotKey),
fun1(Expect<Map<out String?, *>>::containsNotKey).withNullableSuffix(),
fun0(Expect<Map<*, *>>::isEmpty),
fun0(Expect<Map<*, *>>::isNotEmpty)
) {
companion object {
//@formatter:off
private val containsKeyWithValueAssertionsFun : KFunction3<Expect<Map<out String, Int>>, KeyValue<String, Int>, Array<out KeyValue<String, Int>>, Expect<Map<out String, Int>>> = Expect<Map<out String, Int>>::contains
private val containsKeyWithNullableValueAssertionsFun : KFunction3<Expect<Map<out String?, Int?>>, KeyValue<String?, Int>, Array<out KeyValue<String?, Int>>, Expect<Map<out String?, Int?>>> = Expect<Map<out String?, Int?>>::contains
//@formatter:on
fun containsKeyValue(
expect: Expect<Map<out String, Int>>,
keyValue: Pair<String, Expect<Int>.() -> Unit>,
otherKeyValues: Array<out Pair<String, Expect<Int>.() -> Unit>>
) = mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect.contains(first, *others)
}
fun containsNullable(
expect: Expect<Map<out String?, Int?>>,
keyValue: Pair<String?, (Expect<Int>.() -> Unit)?>,
otherKeyValues: Array<out Pair<String?, (Expect<Int>.() -> Unit)?>>
) = mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect.contains(first, *others)
}
}
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
@@ -208,3 +188,20 @@ class MapAssertionsSpec : ch.tutteli.atrium.specs.integration.MapAssertionsSpec(
}
}
private fun contains(
expect: Expect<Map<out String, Int>>,
keyValue: Pair<String, Expect<Int>.() -> Unit>,
otherKeyValues: Array<out Pair<String, Expect<Int>.() -> Unit>>
) = mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect.contains(first, *others)
}
@JvmName("containsNullable")
private fun contains(
expect: Expect<Map<out String?, Int?>>,
keyValue: Pair<String?, (Expect<Int>.() -> Unit)?>,
otherKeyValues: Array<out Pair<String?, (Expect<Int>.() -> Unit)?>>
) = mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect.contains(first, *others)
}

View File

@@ -0,0 +1,18 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.feature
import kotlin.reflect.KProperty1
/**
* Parameter object which contains a [description] of a feature along with an [extractor]
* which actually extracts the feature out of a subject of an assertion.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and the remaining arguments are the required arguments in case of a `KFunctionX`
* where `X` > 1.
*
* @property description The description of the feature.
* @property extractor The extractor which extracts the feature out of the subject of the assertion.
* @since 0.10.0
*/
data class Feature<T, R>(val description: String, val extractor: (T) -> R)

View File

@@ -0,0 +1,25 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.feature
import ch.tutteli.atrium.creating.Expect
import kotlin.reflect.KProperty1
/**
* Parameter object which contains a [description] of a feature along with an [extractor]
* which actually extracts the feature out of a subject of an assertion + an [assertionCreator]
* which defines assertions for the feature.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX`, the last an [assertionCreator]-lambda and the remaining arguments in-between the
* required arguments in case of a `KFunctionX` where `X` > 1.
*
* @property description The description of the feature.
* @property extractor The extractor which extracts the feature out of the subject of the assertion.
* @property assertionCreator The `assertionCreator`-lambda which defines assertions for the feature.
*
* @since 0.10.0
*/
data class FeatureWithCreator<T, R>(
val description: String,
val extractor: (T) -> R,
val assertionCreator: Expect<R>.() -> Unit
)

View File

@@ -0,0 +1,22 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.feature
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.creating.MetaFeatureOption
import ch.tutteli.atrium.domain.creating.MetaFeature
/**
* Parameter object which combines a lambda with a [MetaFeatureOption] receiver (called [provider])
* and an [assertionCreator].
*
* Use the function `of({ ... }) { ... }` to create this representation where the first
* argument is a lambda with a [MetaFeatureOption] as receiver which has to create a [MetaFeature]
* where the subject of the assertion is available via implicit parameter `it`.
* Usually you use [f][MetaFeatureOption.f] to create a [MetaFeature],
* e.g. `feature of({ f(it::size) }) { o toBe 3 }`
*
* @since 0.10.0
*/
data class MetaFeatureOptionWithCreator<T, R>(
val provider: MetaFeatureOption<T>.(T) -> MetaFeature<R>,
val assertionCreator: Expect<R>.() -> Unit
)

View File

@@ -0,0 +1,14 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.list
import ch.tutteli.atrium.creating.Expect
/**
* Parameter object which combines an [index] of type [Int] with an [assertionCreator] which defines assertions for
* a resulting feature of type [E].
*
* Use the function `index(Int) { ... }` to create this representation.
*
* @since 0.10.0
*/
data class IndexWithCreator<E>(val index: Int, val assertionCreator: Expect<E>.() -> Unit)

View File

@@ -1,40 +0,0 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders
import ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders.impl.ListGetStepImpl
import ch.tutteli.atrium.creating.Expect
/**
* Represents the extension point for another step after a `get index`-step within a
* sophisticated `get` assertion building process for [List].
*
* @param E The element type of the [List].
* @param T A subtype of [List].
*/
interface ListGetStep<E, T : List<E>> {
/**
* The [Expect] for which this assertion is created
*/
val expect: Expect<T>
/**
* The given index which will be used to perform the [List.get].
*/
val index: Int
/**
* Makes the assertion that the given [index] is within the bounds of [Expect.subject] and that
* the corresponding entry holds all assertions the given [assertionCreator] creates for it.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if a created [Expect]s (by calling [assertionCreator])
* does not hold.
* @throws IllegalArgumentException in case the given [assertionCreator] did not create a single assertion.
*/
infix fun assertIt(assertionCreator: Expect<E>.() -> Unit): Expect<T>
companion object {
fun <E, T : List<E>> create(expect: Expect<T>, index: Int): ListGetStep<E, T> =
ListGetStepImpl(expect, index)
}
}

View File

@@ -1,14 +0,0 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders.ListGetStep
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
internal class ListGetStepImpl<E, T : List<E>>(
override val expect: Expect<T>,
override val index: Int
) : ListGetStep<E, T> {
override infix fun assertIt(assertionCreator: Expect<E>.() -> Unit): Expect<T> =
ExpectImpl.list.get(expect, index).addToInitial(assertionCreator)
}

View File

@@ -0,0 +1,14 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.map
import ch.tutteli.atrium.creating.Expect
/**
* Parameter object which combines an [key] of type [K] with an [assertionCreator] which defines assertions for
* a resulting feature of type [V].
*
* Use the function `key(...) { ... }` to create this representation where the first parameter corresponds
* to the [key] and the second is the [assertionCreator]
*
* @since 0.10.0
*/
data class KeyWithCreator<out K, V>(val key: K, val assertionCreator: Expect<V>.() -> Unit)

View File

@@ -1,47 +0,0 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.map.get.builders
import ch.tutteli.atrium.api.infix.en_GB.creating.map.get.builders.impl.MapGetOptionImpl
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.creating.AssertionPlant
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.SubjectProvider
/**
* Represents the extension point for another option after a `get key`-step within a
* sophisticated `get` assertion building process for [Map].
*
* @param K The key type of the [Map].
* @param V the value type of the [Map].
* @param T A subtype of [Map].
*/
interface MapGetOption<K, V, T : Map<out K, V>> {
/**
* The [AssertionPlant] for which this assertion is created
*/
val expect: Expect<T>
/**
* The given key which will be used to perform the [Map.get].
*/
val key: K
/**
* Makes the assertion that the [Assert.subject][SubjectProvider.subject] contains the previously specified [key] and that the
* corresponding value holds all assertions the given [assertionCreator] might create for it.
*
* @return This expect to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if a created [Assertion]s (by calling [assertionCreator])
* does not hold.
* @throws IllegalArgumentException in case the given [assertionCreator] did not create a single assertion.
*/
infix fun assertIt(assertionCreator: Expect<V>.() -> Unit): Expect<T>
companion object {
/**
* Creates a [MapGetOption] based on the given [expect] and [key].
*/
fun <K, V, T : Map<out K, V>> create(expect: Expect<T>, key: K): MapGetOption<K, V, T> =
MapGetOptionImpl(expect, key)
}
}

View File

@@ -1,14 +0,0 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.map.get.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.creating.map.get.builders.MapGetOption
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
internal class MapGetOptionImpl<K, V, T : Map<out K, V>>(
override val expect: Expect<T>,
override val key: K
) : MapGetOption<K, V, T> {
override infix fun assertIt(assertionCreator: Expect<V>.() -> Unit): Expect<T> =
expect.addAssertion(ExpectImpl.map.getExisting(expect, key).collect(assertionCreator))
}

View File

@@ -1,5 +1,8 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.feature.Feature
import ch.tutteli.atrium.api.infix.en_GB.creating.feature.FeatureWithCreator
import ch.tutteli.atrium.api.infix.en_GB.creating.feature.MetaFeatureOptionWithCreator
import ch.tutteli.atrium.assertions.AssertionGroup
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.FeatureExpect
@@ -41,8 +44,11 @@ infix fun <T, R> Expect<T>.feature(f: KFunction1<T, R>): FeatureExpect<T, R> =
* creates a new [Expect] for it and
* returns it so that subsequent calls are based on the feature.
*
* Use `of(K..., ...)` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and potentially the required arguments for a `KFunctionX` where `X` > 1.
* Use `of(K..., ...)` to create a [Feature] where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and potentially the required arguments for a `KFunctionX` where `X` > 1.
*
* @param of Use `of(K..., ...)` to create a [Feature] where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and potentially the required arguments for a `KFunctionX` where `X` > 1.
*
* @return The newly created [Expect] for the extracted feature.
*
@@ -57,9 +63,13 @@ infix fun <T, R> Expect<T>.feature(of: Feature<T, R>): FeatureExpect<T, R> =
* applies an assertion group based on the given [FeatureWithCreator.assertionCreator] for the feature and
* returns the initial [Expect] with the current subject.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX`, the last an `assertionCreator`-lambda and the remaining arguments in-between the
* required arguments in case of a `KFunctionX` where `X` > 1.
* Use `of(K..., ...) { ... }` to create a [FeatureWithCreator] where the first argument is the extractor in
* form of a [KProperty1] or a `KFunctionX`, the last an `assertionCreator`-lambda and the remaining arguments
* in-between the required arguments in case of a `KFunctionX` where `X` > 1.
*
* @param of Use `of(K..., ...) { ... }` to create a [FeatureWithCreator] where the first argument is the extractor in
* form of a [KProperty1] or a `KFunctionX`, the last an `assertionCreator`-lambda and the remaining arguments
* in-between the required arguments in case of a `KFunctionX` where `X` > 1.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] in case the created [AssertionGroup] does not hold.
@@ -138,43 +148,6 @@ infix fun <T, R> Expect<T>.feature(of: MetaFeatureOptionWithCreator<T, R>): Expe
fun <T, R> MetaFeatureOption<T>.f(description: String, provider: R): MetaFeature<R> =
MetaFeature(description, provider)
/**
* Parameter object which contains a [description] of a feature along with an [extractor]
* which actually extracts the feature out of a subject of an assertion.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and the remaining arguments are the required arguments in case of a `KFunctionX`
* where `X` > 1.
*
* @property description The description of the feature.
* @property extractor The extractor which extracts the feature out of the subject of the assertion.
* @since 0.10.0
*/
data class Feature<T, R>(val description: String, val extractor: (T) -> R)
/**
* Parameter object which contains a [description] of a feature along with an [extractor]
* which actually extracts the feature out of a subject of an assertion + an [assertionCreator]
* which defines assertions for the feature.
*
* Use `of(K..., ...) { ... }` to create this representation where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX`, the last an [assertionCreator]-lambda and the remaining arguments in-between the
* required arguments in case of a `KFunctionX` where `X` > 1.
*
* @property description The description of the feature.
* @property extractor The extractor which extracts the feature out of the subject of the assertion.
* @property assertionCreator The `assertionCreator`-lambda which defines assertions for the feature.
*
* @since 0.10.0
*/
data class FeatureWithCreator<T, R>(
val description: String,
val extractor: (T) -> R,
val assertionCreator: Expect<R>.() -> Unit
)
//@formatter:off
/**
* Helper function to create a [Feature] based on a [KFunction2] + arguments.
@@ -244,23 +217,6 @@ fun <T, A1, A2, A3, A4, R> of(f: KFunction5<T, A1, A2, A3, A4, R>, a1: A1, a2: A
//@formatter:on
/**
* Parameter object which combines a lambda with a [MetaFeatureOption] receiver (called [provider])
* and an [assertionCreator].
*
* Use the function `of({ ... }) { ... }` to create this representation where the first
* argument is a lambda with a [MetaFeatureOption] as receiver which has to create a [MetaFeature]
* where the subject of the assertion is available via implicit parameter `it`.
* Usually you use [f][MetaFeatureOption.f] to create a [MetaFeature],
* e.g. `feature of({ f(it::size) }) { o toBe 3 }`
*
* @since 0.10.0
*/
data class MetaFeatureOptionWithCreator<T, R>(
val provider: MetaFeatureOption<T>.(T) -> MetaFeature<R>,
val assertionCreator: Expect<R>.() -> Unit
)
/**
* Helper function to create a [MetaFeatureOptionWithCreator] based on a lambda with
* [MetaFeatureOption] receiver (has to return a [MetaFeature]) and an [assertionCreator].
@@ -268,4 +224,8 @@ data class MetaFeatureOptionWithCreator<T, R>(
fun <T, R> of(
provider: MetaFeatureOption<T>.(T) -> MetaFeature<R>,
assertionCreator: Expect<R>.() -> Unit
): MetaFeatureOptionWithCreator<T, R> = MetaFeatureOptionWithCreator(provider, assertionCreator)
): MetaFeatureOptionWithCreator<T, R> =
MetaFeatureOptionWithCreator(
provider,
assertionCreator
)

View File

@@ -1,6 +1,6 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders.ListGetStep
import ch.tutteli.atrium.api.infix.en_GB.creating.list.IndexWithCreator
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
@@ -9,15 +9,26 @@ import ch.tutteli.atrium.domain.builders.ExpectImpl
* returns an [Expect] for the element at that position.
*
* @return The newly created [Expect] for the element at position [index].
* @throws AssertionError if the given [index] is out of bound.
* @throws AssertionError Might throw an [AssertionError] if the given [index] is out of bound.
*/
infix fun <E, T : List<E>> Expect<T>.get(index: Int): Expect<E> =
ExpectImpl.list.get(this, index).getExpectOfFeature()
/**
* Prepares the assertion about the return value of calling [get][List.get] with the given [index].
* Expects that the given [index][IndexWithCreator.index] is within the bounds of the subject of the assertion
* (a [List]) and that the element at that position holds all assertions the given
* [IndexWithCreator.assertionCreator] creates for it.
*
* @return A fluent builder to finish the assertion.
* Use the function `index(Int) { ... }` to create an [IndexWithCreator].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the given [index] is out of bound or
* if the assertion made is not correct.
*/
infix fun <E, T : List<E>> Expect<T>.get(index: Index): ListGetStep<E, T> =
ListGetStep.create(this, index.index)
infix fun <E, T : List<E>> Expect<T>.get(index: IndexWithCreator<E>): Expect<T> =
ExpectImpl.list.get(this, index.index).addToInitial(index.assertionCreator)
/**
* Helper function to create an [IndexWithCreator] based on the given [index] and [assertionCreator].
*/
fun <E> index(index: Int, assertionCreator: Expect<E>.() -> Unit) = IndexWithCreator(index, assertionCreator)

View File

@@ -1,6 +1,7 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.map.get.builders.MapGetOption
import ch.tutteli.atrium.api.infix.en_GB.creating.map.KeyWithCreator
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
@@ -87,12 +88,23 @@ infix fun <K, V, T : Map<out K, V>> Expect<T>.getExisting(key: K): Expect<V> =
ExpectImpl.map.getExisting(this, key).getExpectOfFeature()
/**
* Prepares the assertion about the return value of calling [get][Map.get] with the given [key].
* Expects that the subject of the assertion (a [Map]) contains the given [key] and that
* the corresponding value holds all assertions the given [KeyWithCreator.assertionCreator] creates for it.
*
* @return A fluent builder to finish the assertion.
* */
infix fun <K, V, T : Map<out K, V>> Expect<T>.getExisting(key: Key<K>): MapGetOption<K, V, T> =
MapGetOption.create(this, key.key)
* @param key Use the function `key(...) { ... }` to create a [KeyWithCreator] where the first parameter corresponds
* to the key and the second is the `assertionCreator`-lambda
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] the given [key] does not exist or
* if the assertion made is not correct.
*/
infix fun <K, V, T : Map<out K, V>> Expect<T>.getExisting(key: KeyWithCreator<K, V>): Expect<T> =
ExpectImpl.map.getExisting(this, key.key).addToInitial(key.assertionCreator)
/**
* Helper function to create an [KeyWithCreator] based on the given [key] and [assertionCreator].
*/
fun <K, V> key(key: K, assertionCreator: Expect<V>.() -> Unit) = KeyWithCreator(key, assertionCreator)
/**
@@ -178,3 +190,4 @@ fun <K, V, T : Map<out K, V>> Expect<T>.asEntries(): Expect<Set<Map.Entry<K, V>>
infix fun <K, V, T : Map<out K, V>> Expect<T>.asEntries(
assertionCreator: Expect<Set<Map.Entry<K, V>>>.() -> Unit
): Expect<T> = apply { asEntries().addAssertionsCreatedBy(assertionCreator) }

View File

@@ -9,14 +9,7 @@ import ch.tutteli.atrium.domain.builders.utils.VarArgHelper
class All<out T>(override val expected: T, override vararg val otherExpected: T) : VarArgHelper<T>
/**
* Wrapper for a single index -- can be used as distinguishable type for an overload where Int is already in use.
*/
data class Index(val index: Int)
data class Key<out K>(val key: K)
/**
* Parameter object to express a key/value [Pair] whose value type is a lambda with an
* Parameter object to express a key/value [Pair] whose value type is a nullable lambda with an
* [Expect] receiver, which means one can either pass a lambda or `null`.
*/
data class KeyValue<out K, V : Any>(val key: K, val valueAssertionCreatorOrNull: (Expect<V>.() -> Unit)?) {
@@ -25,8 +18,6 @@ data class KeyValue<out K, V : Any>(val key: K, val valueAssertionCreatorOrNull:
"KeyValue(key=$key, value=${if (valueAssertionCreatorOrNull == null) "null" else "lambda"})"
}
/**
* Parameter object to express `Pair<K, V>, vararg Pair<K, V>`.
*/

View File

@@ -81,7 +81,7 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
//regression for #298, should compile without the need for E : Any or List<E?>
@Suppress("unused")
fun <E> Expect<List<E>>.firstIs(value: E) = o get Index(0) assertIt { o toBe value }
fun <E> Expect<List<E>>.firstIs(value: E) = o get index(0) { o toBe value }
}
private fun toBeNull(expect: Expect<Int?>) = expect toBe null

View File

@@ -6,7 +6,7 @@ import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
class CollectionAssertionsSpec : ch.tutteli.atrium.specs.integration.CollectionAssertionsSpec(
"toBe ${Empty::class.simpleName}" to ::isEmpty,
"toBe ${Empty::class.simpleName}" to ::isNotEmpty
"notToBe ${Empty::class.simpleName}" to ::isNotEmpty
) {
companion object : WithAsciiReporter()
@@ -28,5 +28,5 @@ class CollectionAssertionsSpec : ch.tutteli.atrium.specs.integration.CollectionA
}
}
fun isEmpty(expect: Expect<Collection<Int>>) = expect toBe Empty
fun isNotEmpty(expect: Expect<Collection<Int>>) = expect notToBe Empty
private fun isEmpty(expect: Expect<Collection<Int>>) = expect toBe Empty
private fun isNotEmpty(expect: Expect<Collection<Int>>) = expect notToBe Empty

View File

@@ -1,15 +1,14 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.name
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import ch.tutteli.atrium.specs.withNullableSuffix
class IterableAllAssertionsSpec : ch.tutteli.atrium.specs.integration.IterableAllAssertionsSpec(
fun1(Expect<Iterable<Double>>::all).name to ::all,
fun1(Expect<Iterable<Double?>>::all).withNullableSuffix().name to ::allNullable,
fun1(Expect<Iterable<Double>>::all),
fun1(Expect<Iterable<Double?>>::all).withNullableSuffix(),
"* ", "(!) ", "- ", "» ", ">> ", "=> "
) {
companion object : WithAsciiReporter()
@@ -21,17 +20,11 @@ class IterableAllAssertionsSpec : ch.tutteli.atrium.specs.integration.IterableAl
var star: Expect<Iterable<*>> = notImplemented()
a1 = a1.all {}
a1 = a1 all {}
a1b = a1b.all {}
a1b = a1b.all(null)
a1b = a1b all {}
a1b = a1b all null
star = star.all {}
star = star all {}
}
}
private fun all(expect: Expect<Iterable<Double>>, assertionCreator: Expect<Double>.() -> Unit) =
expect all assertionCreator
private fun allNullable(expect: Expect<Iterable<Double?>>, assertionCreator: (Expect<Double>.() -> Unit)?) =
expect all assertionCreator

View File

@@ -1,18 +1,18 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.list.get.builders.ListGetStep
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.feature1
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import ch.tutteli.atrium.specs.withNullableSuffix
import kotlin.reflect.KFunction2
import kotlin.jvm.JvmName
class ListFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.ListFeatureAssertionsSpec(
feature1<List<Int>, Int, Int>(Expect<List<Int>>::get),
getIndexPair(),
fun2<List<Int>, Int, Expect<Int>.() -> Unit>(::get),
feature1<List<Int?>, Int, Int?>(Expect<List<Int?>>::get).withNullableSuffix(),
getIndexNullablePair()
fun2<List<Int?>, Int, Expect<Int?>.() -> Unit>(::get).withNullableSuffix()
) {
companion object : WithAsciiReporter()
@@ -24,29 +24,22 @@ class ListFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.ListFeatur
var star: Expect<out List<*>> = notImplemented()
a1 get 1
a1 = a1 get Index(1) assertIt { }
a1 = a1 get index(1) { }
a1b get 1
a1b = a1b get Index(1) assertIt { }
a1b = a1b get index(1) { }
star get 1
star = star get Index(1) assertIt { }
star = star get index(1) { }
}
}
private val getIndexFun: KFunction2<Expect<List<Int>>, Index, ListGetStep<Int, List<Int>>> = Expect<List<Int>>::get
private fun getIndexPair() = getIndexFun.name to ::getIndex
private fun get(expect: Expect<List<Int>>, index: Int, assertionCreator: Expect<Int>.() -> Unit) =
expect get index(index) { assertionCreator() }
private fun getIndex(expect: Expect<List<Int>>, index: Int, assertionCreator: Expect<Int>.() -> Unit) =
expect get Index(index) assertIt { assertionCreator() }
private val getIndexNullableFun: KFunction2<Expect<List<Int?>>, Index, ListGetStep<Int?, List<Int?>>> =
Expect<List<Int?>>::get
private fun getIndexNullablePair() = getIndexNullableFun.name to ::getIndexNullable
private fun getIndexNullable(
@JvmName("getNullable")
private fun get(
expect: Expect<List<Int?>>,
index: Int,
assertionCreator: Expect<Int?>.() -> Unit
) = expect get Index(index) assertIt { assertionCreator() }
) = expect get index(index) { assertionCreator() }

View File

@@ -3,86 +3,27 @@ package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.utils.mapArguments
import ch.tutteli.atrium.specs.*
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import kotlin.jvm.JvmName
import kotlin.reflect.KFunction3
private fun <K, V, T> mfun2(
f: KFunction3<Expect<Map<out K, V>>, Pair<K, T>, Array<out Pair<K, T>>, Expect<Map<out K, V>>>
) = fun2(f)
class MapAssertionsSpec : ch.tutteli.atrium.specs.integration.MapAssertionsSpec(
fun2(Companion::contains),
fun2(Companion::contains).name to Companion::containsNullable,
"${fun2(Companion::contains).name} ${KeyValue::class.simpleName}" to Companion::containsKeyWithValueAssertions,
"${fun2(Companion::contains).name} ${KeyValue::class.simpleName}" to Companion::containsKeyWithNullableValueAssertions,
fun1(Companion::containsKey),
fun1(Companion::containsNullableKey),
fun1(Companion::containsNotKey),
fun1(Companion::containsNotNullableKey),
/* string toBe, notToBe to avoid ambiguity error */
"toBe ${Empty::class.simpleName}" to Companion::isEmpty,
"notToBe ${Empty::class.simpleName}" to Companion::isNotEmpty
mfun2<String, Int, Int>(::contains),
mfun2<String?, Int?, Int?>(::contains).withNullableSuffix(),
mfun2<String, Int, Expect<Int>.() -> Unit>(::contains).adjustName { "$it ${KeyValue::class.simpleName}" },
mfun2<String?, Int?, (Expect<Int>.() -> Unit)?>(::contains).adjustName { "$it ${KeyValue::class.simpleName}" }.withNullableSuffix(),
fun1<Map<out String, *>, String>(::containsKey),
fun1<Map<out String?, *>, String?>(::containsKey).withNullableSuffix(),
fun1<Map<out String, *>, String>(::containsNotKey),
fun1<Map<out String?, *>, String?>(::containsNotKey).withNullableSuffix(),
"toBe ${Empty::class.simpleName}" to ::isEmpty,
"notToBe ${Empty::class.simpleName}" to ::isNotEmpty
) {
companion object {
private fun contains(
expect: Expect<Map<out String, Int>>,
pair: Pair<String, Int>,
otherPairs: Array<out Pair<String, Int>>
): Expect<Map<out String, Int>> {
return if (otherPairs.isEmpty()) {
expect contains (pair.first to pair.second)
} else {
expect contains Pairs(pair, *otherPairs)
}
}
private fun containsNullable(
expect: Expect<Map<out String?, Int?>>,
pair: Pair<String?, Int?>,
otherPairs: Array<out Pair<String?, Int?>>
): Expect<Map<out String?, Int?>> {
return if (otherPairs.isEmpty()) {
expect contains (pair.first to pair.second)
} else {
expect contains Pairs(pair, *otherPairs)
}
}
private fun containsKeyWithValueAssertions(
expect: Expect<Map<out String, Int>>,
keyValue: Pair<String, Expect<Int>.() -> Unit>,
otherKeyValues: Array<out Pair<String, Expect<Int>.() -> Unit>>
): Expect<Map<out String, Int>> {
return if (otherKeyValues.isEmpty()) {
expect contains KeyValue(keyValue.first, keyValue.second)
} else {
mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect contains All(first, *others)
}
}
}
private fun containsKeyWithNullableValueAssertions(
expect: Expect<Map<out String?, Int?>>,
keyValue: Pair<String?, (Expect<Int>.() -> Unit)?>,
otherKeyValues: Array<out Pair<String?, (Expect<Int>.() -> Unit)?>>
): Expect<Map<out String?, Int?>> {
return if (otherKeyValues.isEmpty()) {
expect contains KeyValue(keyValue.first, keyValue.second)
} else {
mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect contains All(first, *others)
}
}
}
private fun containsKey(expect: Expect<Map<out String, *>>, key: String) = expect containsKey key
private fun containsNullableKey(expect: Expect<Map<out String?, *>>, key: String?) = expect containsKey key
private fun containsNotKey(expect: Expect<Map<out String, *>>, key: String) = expect containsNotKey key
private fun containsNotNullableKey(expect: Expect<Map<out String?, *>>, key: String?) =
expect containsNotKey key
private fun isEmpty(expect: Expect<Map<*, *>>) = expect toBe Empty
private fun isNotEmpty(expect: Expect<Map<*, *>>) = expect notToBe Empty
}
companion object : WithAsciiReporter()
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
@@ -249,3 +190,76 @@ class MapAssertionsSpec : ch.tutteli.atrium.specs.integration.MapAssertionsSpec(
}
}
private fun contains(
expect: Expect<Map<out String, Int>>,
pair: Pair<String, Int>,
otherPairs: Array<out Pair<String, Int>>
): Expect<Map<out String, Int>> {
return if (otherPairs.isEmpty()) {
expect contains (pair.first to pair.second)
} else {
expect contains Pairs(pair, *otherPairs)
}
}
@JvmName("containsNullable")
private fun contains(
expect: Expect<Map<out String?, Int?>>,
pair: Pair<String?, Int?>,
otherPairs: Array<out Pair<String?, Int?>>
): Expect<Map<out String?, Int?>> {
return if (otherPairs.isEmpty()) {
expect contains (pair.first to pair.second)
} else {
expect contains Pairs(pair, *otherPairs)
}
}
@JvmName("containsKeyWithValueAssertions")
private fun contains(
expect: Expect<Map<out String, Int>>,
keyValue: Pair<String, Expect<Int>.() -> Unit>,
otherKeyValues: Array<out Pair<String, Expect<Int>.() -> Unit>>
): Expect<Map<out String, Int>> {
return if (otherKeyValues.isEmpty()) {
expect contains KeyValue(keyValue.first, keyValue.second)
} else {
mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect contains All(first, *others)
}
}
}
@JvmName("containsKeyWithNullableValueAssertions")
private fun contains(
expect: Expect<Map<out String?, Int?>>,
keyValue: Pair<String?, (Expect<Int>.() -> Unit)?>,
otherKeyValues: Array<out Pair<String?, (Expect<Int>.() -> Unit)?>>
): Expect<Map<out String?, Int?>> {
return if (otherKeyValues.isEmpty()) {
expect contains KeyValue(keyValue.first, keyValue.second)
} else {
mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) ->
expect contains All(first, *others)
}
}
}
private fun containsKey(expect: Expect<Map<out String, *>>, key: String) =
expect containsKey key
@JvmName("containsKeyNullable")
private fun containsKey(expect: Expect<Map<out String?, *>>, key: String?) =
expect containsKey key
private fun containsNotKey(expect: Expect<Map<out String, *>>, key: String) =
expect containsNotKey key
@JvmName("containsNotKeyNullable")
private fun containsNotKey(expect: Expect<Map<out String?, *>>, key: String?) =
expect containsNotKey key
private fun isEmpty(expect: Expect<Map<*, *>>) = expect toBe Empty
private fun isNotEmpty(expect: Expect<Map<*, *>>) = expect notToBe Empty

View File

@@ -2,6 +2,8 @@ package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.*
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import kotlin.jvm.JvmName
class MapFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.MapFeatureAssertionsSpec(
property<Map<String, Int>, Set<String>>(Expect<Map<String, Int>>::keys),
@@ -9,23 +11,11 @@ class MapFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.MapFeatureA
property<Map<String, Int>, Collection<Int>>(Expect<Map<String, Int>>::values),
fun1<Map<String, Int>, Expect<Collection<Int>>.() -> Unit>(Expect<Map<String, Int>>::values),
feature1<Map<String, Int>, String, Int>(Expect<Map<String, Int>>::getExisting),
fun2<Map<String, Int>, String, Expect<Int>.() -> Unit>(Companion::getExisting),
fun2<Map<String, Int>, String, Expect<Int>.() -> Unit>(::getExisting),
feature1<Map<String?, Int?>, String?, Int?>(Expect<Map<String?, Int?>>::getExisting).withNullableSuffix(),
fun2(Companion::getExisting).name to Companion::getExistingNullable
fun2<Map<String?, Int?>, String?, Expect<Int?>.() -> Unit>(::getExisting).withNullableSuffix()
) {
companion object {
private fun getExisting(
expect: Expect<Map<String, Int>>,
key: String,
assertionCreator: Expect<Int>.() -> Unit
): Expect<Map<String, Int>> = expect getExisting Key(key) assertIt { assertionCreator() }
private fun getExistingNullable(
expect: Expect<Map<String?, Int?>>,
key: String?,
assertionCreator: Expect<Int?>.() -> Unit
): Expect<Map<String?, Int?>> = expect getExisting Key(key) assertIt { assertionCreator() }
}
companion object : WithAsciiReporter()
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
@@ -40,9 +30,22 @@ class MapFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.MapFeatureA
a3 getExisting null as String?
star getExisting "a"
a1 = a1 getExisting Key("a") assertIt { }
a2 = a2 getExisting Key(1) assertIt { }
a3 = a3 getExisting Key(null) assertIt { }
star = star getExisting Key("a") assertIt { }
a1 = a1 getExisting key("a") { }
a2 = a2 getExisting key(1) { }
a3 = a3 getExisting key(null) { }
star = star getExisting key("a") { }
}
}
private fun getExisting(
expect: Expect<Map<String, Int>>,
key: String,
assertionCreator: Expect<Int>.() -> Unit
): Expect<Map<String, Int>> = expect getExisting key(key) { assertionCreator() }
@JvmName("getExistingNullable")
private fun getExisting(
expect: Expect<Map<String?, Int?>>,
key: String?,
assertionCreator: Expect<Int?>.() -> Unit
): Expect<Map<String?, Int?>> = expect getExisting key(key) { assertionCreator() }

View File

@@ -19,7 +19,7 @@ interface VarArgHelper<out T> {
/**
* Creates an [ArgumentMapperBuilder] which allows to map [expected] and [otherExpected].
*/
val mapArguments get() = ArgumentMapperBuilder(expected, otherExpected)
val mapArguments: ArgumentMapperBuilder<T> get() = ArgumentMapperBuilder(expected, otherExpected)
/**
* Returns the arguments as [List].