diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/featureAssertions.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/featureAssertions.kt index 68142486d..f26049fa8 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/featureAssertions.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/featureAssertions.kt @@ -42,7 +42,8 @@ fun Expect.feature( * creates a new [Expect] for it and * returns it so that subsequent calls are based on the feature. * - * @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion. + * @return The newly created [Expect] for the return value of calling the method [f] + * on the current subject of the assertion. * * @since 0.9.0 */ @@ -72,7 +73,8 @@ fun Expect.feature( * creates a new [Expect] for it and * returns it so that subsequent calls are based on the feature. * - * @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion. + * @return The newly created [Expect] for the return value of calling the method [f] + * on the current subject of the assertion. * * @since 0.9.0 */ @@ -106,7 +108,8 @@ fun Expect.feature( * creates a new [Expect] for it and * returns it so that subsequent calls are based on the feature. * - * @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion. + * @return The newly created [Expect] for the return value of calling the method [f] + * on the current subject of the assertion. * * @since 0.9.0 */ @@ -140,7 +143,8 @@ fun Expect.feature( * creates a new [Expect] for it and * returns it so that subsequent calls are based on the feature. * - * @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion. + * @return The newly created [Expect] for the return value of calling the method [f] + * on the current subject of the assertion. * * @since 0.9.0 */ @@ -174,7 +178,8 @@ fun Expect.feature( * creates a new [Expect] for it and * returns it so that subsequent calls are based on the feature. * - * @return The newly created [Expect] for the return value of calling [f] on the current subject of the assertion. + * @return The newly created [Expect] for the return value of calling the method [f] + * on the current subject of the assertion. * * @since 0.9.0 */ 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 23b0b10c2..093148973 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 @@ -171,10 +171,11 @@ inline infix fun Expect.and(@Suppress("UNUSED_PARAMETER") o: o): Expect Expect.and(assertionCreator: Expect.() -> Unit) = addAssertionsCreatedBy(assertionCreator) +infix fun Expect.and(assertionCreator: Expect.() -> Unit): Expect = + addAssertionsCreatedBy(assertionCreator) /** - * Inline property referring actually to `this` and allows to write nicer sub-assertions. + * Inline property referring actually to `this` and allows to write infix assertions within an assertion group block * * For instance, instead of: * ``` diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/ListGetStep.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/ListGetStep.kt index 07cdea536..ffc070a8b 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/ListGetStep.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/ListGetStep.kt @@ -23,7 +23,7 @@ interface ListGetStep> { /** * 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] might create for it. + * 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]) diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/impl/ListGetStepImpl.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/impl/ListGetStepImpl.kt index b837b17d0..a8f7a637d 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/impl/ListGetStepImpl.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/list/get/builders/impl/ListGetStepImpl.kt @@ -4,11 +4,11 @@ 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>( +internal class ListGetStepImpl>( override val expect: Expect, override val index: Int ) : ListGetStep { - override infix fun assertIt(assertionCreator: Expect.() -> Unit): Expect - = expect.addAssertion(ExpectImpl.list.get(expect, index).collect(assertionCreator)) + override infix fun assertIt(assertionCreator: Expect.() -> Unit): Expect = + ExpectImpl.list.get(expect, index).addToInitial(assertionCreator) } diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/featureAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/featureAssertions.kt new file mode 100644 index 000000000..5c0c7abb3 --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/featureAssertions.kt @@ -0,0 +1,271 @@ +package ch.tutteli.atrium.api.infix.en_GB + +import ch.tutteli.atrium.assertions.AssertionGroup +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.creating.FeatureExpect +import ch.tutteli.atrium.domain.builders.ExpectImpl +import ch.tutteli.atrium.domain.builders.creating.MetaFeatureOption +import ch.tutteli.atrium.domain.creating.MetaFeature +import kotlin.reflect.* + +/** + * Extracts the [property] out of the current subject of the assertion, + * creates a new [Expect] for it and + * returns it so that subsequent calls are based on the feature. + * + * @return The newly created [Expect] for the given [property]. + * + * @since 0.10.0 + */ +infix fun Expect.feature(property: KProperty1): FeatureExpect = + ExpectImpl.feature.property(this, property).getExpectOfFeature() + +/** + * Extracts the value which is returned when calling the method [f] on the current subject of the assertion, + * creates a new [Expect] for it and + * returns it so that subsequent calls are based on the feature. + * + * Use `feature of(...)` in case the method requires parameters or in case you want to define + * an assertion group block for it. + * + * @return The newly created [Expect] for the return value of calling the method [f] + * on the current subject of the assertion. + * + * @since 0.10.0 + */ +infix fun Expect.feature(f: KFunction1): FeatureExpect = + ExpectImpl.feature.f0(this, f).getExpectOfFeature() + +/** + * Extracts a feature out of the current subject of the assertion using the given [Feature.extractor], + * 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. + * + * @return The newly created [Expect] for the extracted feature. + * + * @since 0.10.0 + */ +infix fun Expect.feature(of: Feature): FeatureExpect = + ExpectImpl.feature.manualFeature(this, of.description, of.extractor).getExpectOfFeature() + +/** + * Extracts a feature out of the current subject of the assertion using the given [FeatureWithCreator.extractor], + * creates a new [Expect] for it, + * 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. + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] in case the created [AssertionGroup] does not hold. + * + * @since 0.10.0 + */ +infix fun Expect.feature(of: FeatureWithCreator): Expect = + ExpectImpl.feature.manualFeature(this, of.description, of.extractor).addToInitial(of.assertionCreator) + + +/** + * Extracts a feature out of the current subject of the assertion, + * based on the given [provider], + * creates a new [Expect] for it and + * returns it so that subsequent calls are based on the feature. + * + * @param provider Creates 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 { f(it::size) }` + * + * @return The newly created [Expect] for the extracted feature. + * + * @since 0.10.0 + */ +infix fun Expect.feature(provider: MetaFeatureOption.(T) -> MetaFeature): FeatureExpect = + ExpectImpl.feature.genericSubjectBasedFeature(this) { MetaFeatureOption(this).provider(it) }.getExpectOfFeature() + +/** + * Extracts a feature out of the current subject of the assertion, + * based on the given [MetaFeatureOptionWithCreator] + * creates a new [Expect] for it, + * applies an assertion group based on the given [MetaFeatureOptionWithCreator.assertionCreator] for the feature and + * returns the initial [Expect] with the current subject. + * + * Note that you need to enable the new type inference of Kotlin (or use Kotlin 1.4 and above) in order that Kotlin + * is able to infer the types. + * As workaround you can use the overload which expects `MetaFeatureOption.(T) -> MetaFeature` + * and use `it` after the call (import from the package workaround). For instance: + * + * ``` + * // use + * expect(person) feature { f(it::age) } it { o toBe 20 } + * + * // instead of (which causes problems with Kotlin < 1.4) + * expect(person) feature of({ f(it::age) }) { o toBe 20 } + * ``` + * + * @param of Use the function `of({ ... }) { ... }` to create the [MetaFeatureOptionWithCreator] 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 }` + * + * @return An [Expect] for the current subject of the assertion. + * @since 0.10.0 + */ +infix fun Expect.feature(of: MetaFeatureOptionWithCreator): Expect = + ExpectImpl.feature.genericSubjectBasedFeature(this) { + MetaFeatureOption(this).(of.provider)(it) + }.addToInitial(of.assertionCreator) + +/** + * Creates a [MetaFeature] using the given [provider] and [description]. + * + * This can be used to create complex features with a custom description or as workaround where Kotlin is not able to + * infer the types properly. + * + * For instance: + * ``` + * expect(person) feature { f("first underage child", it.children.first { it < 18 }) } + * ``` + * + * @return The newly created [MetaFeature]. + */ +@Suppress("unused" /* unused receiver, but that's fine */) +fun MetaFeatureOption.f(description: String, provider: R): MetaFeature = + 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(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( + val description: String, + val extractor: (T) -> R, + val assertionCreator: Expect.() -> Unit +) + + +//@formatter:off +/** + * Helper function to create a [Feature] based on a [KFunction2] + arguments. + */ +fun of(f: KFunction2, a1: A1): Feature = + Feature(f.name) { f.invoke(it, a1) } + +/** + * Helper function to create a [Feature] based on a [KFunction3] + arguments. + */ +fun of(f: KFunction3, a1: A1, a2: A2): Feature = + Feature(f.name) { f.invoke(it, a1, a2) } + +/** + * Helper function to create a [Feature] based on a [KFunction4] + arguments. + */ +fun of(f: KFunction4, a1: A1, a2: A2, a3: A3): Feature = + Feature(f.name) { f.invoke(it, a1, a2, a3) } + +/** + * Helper function to create a [Feature] based on a [KFunction5] + arguments. + */ +fun of(f: KFunction5, a1: A1, a2: A2, a3: A3, a4: A4): Feature = + Feature(f.name) { f.invoke(it, a1, a2, a3, a4) } + +/** + * Helper function to create a [Feature] based on a [KFunction5] + arguments. + */ +fun of(f: KFunction6, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): Feature = + Feature(f.name) { f.invoke(it, a1, a2, a3, a4, a5) } + +/** + * Helper function to create a [FeatureWithCreator] based on a [KProperty1] + [assertionCreator]. + */ +fun of(property: KProperty1, assertionCreator: Expect.() -> Unit): FeatureWithCreator = + FeatureWithCreator(property.name, { property.invoke(it) }, assertionCreator) + +/** + * Helper function to create a [FeatureWithCreator] based on a [KFunction1] + [assertionCreator]. + */ +fun of(f: KFunction1, assertionCreator: Expect.() -> Unit): FeatureWithCreator = + FeatureWithCreator(f.name, { f.invoke(it) }, assertionCreator) + +/** + * Helper function to create a [FeatureWithCreator] based on a [KFunction2] + arguments + [assertionCreator]. + */ +fun of(f: KFunction2, a1: A1, assertionCreator: Expect.() -> Unit): FeatureWithCreator = + FeatureWithCreator(f.name, { f.invoke(it, a1) }, assertionCreator) + +/** + * Helper function to create a [FeatureWithCreator] based on a [KFunction3] + arguments + [assertionCreator]. + */ +fun of(f: KFunction3, a1: A1, a2: A2, assertionCreator: Expect.() -> Unit): FeatureWithCreator = + FeatureWithCreator(f.name, { f.invoke(it, a1, a2) }, assertionCreator) + +/** + * Helper function to create a [FeatureWithCreator] based on a [KFunction4] + arguments + [assertionCreator]. + */ +fun of(f: KFunction4, a1: A1, a2: A2, a3: A3, assertionCreator: Expect.() -> Unit): FeatureWithCreator = + FeatureWithCreator(f.name, { f.invoke(it, a1, a2, a3) }, assertionCreator) + +/** + * Helper function to create a [FeatureWithCreator] based on a [KFunction5] + arguments + [assertionCreator]. + */ +fun of(f: KFunction5, a1: A1, a2: A2, a3: A3, a4: A4, assertionCreator: Expect.() -> Unit): FeatureWithCreator = + FeatureWithCreator(f.name, { f.invoke(it, a1, a2, a3, a4) }, assertionCreator) +//@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( + val provider: MetaFeatureOption.(T) -> MetaFeature, + val assertionCreator: Expect.() -> Unit +) + +/** + * Helper function to create a [MetaFeatureOptionWithCreator] based on a lambda with + * [MetaFeatureOption] receiver (has to return a [MetaFeature]) and an [assertionCreator]. + */ +fun of( + provider: MetaFeatureOption.(T) -> MetaFeature, + assertionCreator: Expect.() -> Unit +): MetaFeatureOptionWithCreator = MetaFeatureOptionWithCreator(provider, assertionCreator) diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/keywords.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/keywords.kt index 8f1bbc3b9..e6b45b239 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/keywords.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/keywords.kt @@ -27,40 +27,48 @@ object Blank : Keyword /** * Represents the pseudo keyword `contain` as in [to] `contain`. + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. */ object contain : Keyword /** * Represents the pseudo keyword `case` as in [ignoring] `case`. + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. */ object case : Keyword /** * Represents the pseudo keyword `entries` as in [grouped] `entries`. + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. */ object entries : Keyword /** * Represents the pseudo keyword `group` as in [within] `group`. + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. */ object group : Keyword /** * Represents a filler, a pseudo keyword where there isn't really a good keyword. - * A reader should skip this filler without reading it. For instance, `contains o atleast 1...` should be read as + * A reader should skip this filler without reading it. For instance, `contains o atLeast 1...` should be read as * `contains at least once...` * + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. + * * @since 0.10.0 */ object o : Keyword /** * Represents the pseudo keyword `only` as in [and] `only`. + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. */ object only : Keyword /** * Represents the pseudo keyword `order` as in [inAny] `order`. + * It can be used for a parameter less function so that it has one parameter and thus can be used as infix function. */ object order : Keyword diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/parameterObjects.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/parameterObjects.kt index ee945e498..bcd7cef2b 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/parameterObjects.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/parameterObjects.kt @@ -3,32 +3,34 @@ package ch.tutteli.atrium.api.infix.en_GB import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.domain.builders.utils.VarArgHelper +/** + * Parameter object to express `T, vararg T`. + */ +class All(override val expected: T, override vararg val otherExpected: T) : VarArgHelper + /** * 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(val key: K) -/** - * Parameter object to express `T, vararg T` in the infix-api. - */ -class All(override val expected: T, override vararg val otherExpected: T) : VarArgHelper - -/** - * Parameter object to express `Pair, vararg Pair` in the infix-api. - */ -class Pairs( - override val expected: Pair, - override vararg val otherExpected: Pair -) : VarArgHelper> - /** * Parameter object to express a key/value [Pair] whose value type is a lambda with an - * [Assert][AssertionPlant] receiver, which means one can either pass a lambda or `null`. + * [Expect] receiver, which means one can either pass a lambda or `null`. */ data class KeyValue(val key: K, val valueAssertionCreatorOrNull: (Expect.() -> Unit)?) { fun toPair(): Pair.() -> Unit)?> = key to valueAssertionCreatorOrNull override fun toString(): String = "KeyValue(key=$key, value=${if (valueAssertionCreatorOrNull == null) "null" else "lambda"})" } + + + +/** + * Parameter object to express `Pair, vararg Pair`. + */ +class Pairs( + override val expected: Pair, + override vararg val otherExpected: Pair +) : VarArgHelper> diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/sequenceAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/sequenceAssertions.kt index 73e955711..63912e893 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/sequenceAssertions.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/sequenceAssertions.kt @@ -1,3 +1,5 @@ +package ch.tutteli.atrium.api.infix.en_GB + import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.domain.builders.ExpectImpl.changeSubject @@ -9,8 +11,8 @@ import ch.tutteli.atrium.domain.builders.ExpectImpl.changeSubject * * @return The newly created [Expect] for the transformed subject. */ -fun > Expect.asIterable(): Expect> - = changeSubject(this).unreported { it.asIterable() } +fun > Expect.asIterable(): Expect> = + changeSubject(this).unreported { it.asIterable() } /** * Expects that the subject of the assertion holds all assertions the given [assertionCreator] creates for diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/workaround/anyAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/workaround/anyAssertions.kt new file mode 100644 index 000000000..2c0b63365 --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/workaround/anyAssertions.kt @@ -0,0 +1,25 @@ +package ch.tutteli.atrium.api.infix.en_GB.workaround + +import ch.tutteli.atrium.api.infix.en_GB.and +import ch.tutteli.atrium.creating.Expect + +/** + * Can be used to create a group of sub assertions when using the fluent API. + * + * Intended to be used in combination with feature assertions where Kotlin < 1.4 is not able to infer the correct type. + * For instance: + * ``` + * // use + * expect(person) feature { f(it::age) } it { o toBe 20 } + * + * // instead of (which causes problems with Kotlin < 1.4) + * expect(person) feature of({ f(it::age) }) { o toBe 20 } + * ``` + * + * Note that this workaround will be removed in some minor version after a major version with Kotlin 1.4 support + * (most likely with Atrium v1.1.0 where Atrium v1.0.0 requires Kotlin 1.4) + * + * @return An [Expect] for the current subject of the assertion. + */ +@Suppress("NOTHING_TO_INLINE" /* inline so that one does not actually call `it` on binary level */) +inline infix fun Expect.it(noinline assertionCreator: Expect.() -> Unit): Expect = and(assertionCreator) diff --git a/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/NewFeatureAssertionsBuilder.kt b/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/NewFeatureAssertionsBuilder.kt index 4cf5ddc9c..0ecd76652 100644 --- a/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/NewFeatureAssertionsBuilder.kt +++ b/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/NewFeatureAssertionsBuilder.kt @@ -80,23 +80,16 @@ object NewFeatureAssertionsBuilder : NewFeatureAssertions { description: Translatable, provider: T.() -> R ): ExtractedFeaturePostStep = - genericFeature(expect, createMetaFeature(expect, description, provider)) + genericFeature(expect, ExpectImpl.feature.meta.create(expect, description, provider)) fun genericSubjectBasedFeature( expect: Expect, provider: (T) -> MetaFeature ): ExtractedFeaturePostStep = ExpectImpl.feature.genericFeature( expect, - expect.maybeSubject.fold(this::createFeatureSubjectNotDefined) { provider(it) } + ExpectImpl.feature.meta.createSubjectBased(expect, provider) ) - private fun createFeatureSubjectNotDefined(): MetaFeature = - MetaFeature( - ErrorMessages.DEDSCRIPTION_BASED_ON_SUBJECT, - RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED), - None - ) - override inline fun genericFeature( expect: Expect, metaFeature: MetaFeature @@ -107,30 +100,7 @@ object NewFeatureAssertionsBuilder : NewFeatureAssertions { description: String, provider: (T) -> R ): ExtractedFeaturePostStep = - genericFeature(expect, createMetaFeature(expect, description, provider)) - - private fun createMetaFeature( - expect: Expect, - description: String, - provider: (T) -> R - ): MetaFeature = createMetaFeature(expect, Untranslatable(description), provider) - - private fun createMetaFeature( - expect: Expect, - description: Translatable, - provider: (T) -> R - ): MetaFeature { - return expect.maybeSubject.fold({ - MetaFeature( - description, - RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED), - None - ) - }) { - val prop = provider(it) - MetaFeature(description, prop, Some(prop)) - } - } + genericFeature(expect, ExpectImpl.feature.meta.create(expect, description, provider)) /** * Returns [MetaFeatureBuilder] which helps to create a [MetaFeature]. @@ -143,6 +113,7 @@ object NewFeatureAssertionsBuilder : NewFeatureAssertions { * into an overload ambiguity, then either [p] (for property) or one of the `fN` functions (e.g. [f2] for * a function which expects 2 arguments). */ +//TODO move to API, this could potentially be different per API class MetaFeatureOption(private val expect: Expect) { /** @@ -338,4 +309,43 @@ object MetaFeatureBuilder { fun f5(expect: Expect<*>, f: KFunction5, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) = MetaFeature(coreFactory.newMethodCallFormatter().formatCall(f.name, arrayOf(a1, a2, a3, a4, a5)), f.invoke(a1, a2, a3, a4, a5)) //@formatter:on + + /** + * creates a [MetaFeature] which is entirely based on the subject (i.e. also the description). + */ + fun createSubjectBased( + expect: Expect, + provider: (T) -> MetaFeature + ): MetaFeature = expect.maybeSubject.fold(this::createFeatureSubjectNotDefined) { provider(it) } + + private fun createFeatureSubjectNotDefined(): MetaFeature = + MetaFeature( + ErrorMessages.DEDSCRIPTION_BASED_ON_SUBJECT, + RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED), + None + ) + + fun create( + expect: Expect, + description: String, + provider: (T) -> R + ): MetaFeature = create(expect, Untranslatable(description), provider) + + fun create( + expect: Expect, + description: Translatable, + provider: (T) -> R + ): MetaFeature { + return expect.maybeSubject.fold({ + MetaFeature( + description, + RawString.create(ErrorMessages.REPRESENTATION_BASED_ON_SUBJECT_NOT_DEFINED), + None + ) + }) { + val feature = provider(it) + MetaFeature(description, feature, Some(feature)) + } + } + } diff --git a/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/changers/impl/featureextractor/defaultImpls.kt b/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/changers/impl/featureextractor/defaultImpls.kt index 67f000595..c45509e06 100644 --- a/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/changers/impl/featureextractor/defaultImpls.kt +++ b/domain/builders/atrium-domain-builders-common/src/main/kotlin/ch/tutteli/atrium/domain/builders/creating/changers/impl/featureextractor/defaultImpls.kt @@ -4,6 +4,7 @@ import ch.tutteli.atrium.core.None import ch.tutteli.atrium.core.Option import ch.tutteli.atrium.core.Some import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.creating.FeatureExpect import ch.tutteli.atrium.domain.builders.creating.changers.FeatureExtractorBuilder import ch.tutteli.atrium.domain.builders.creating.changers.FeatureOptions import ch.tutteli.atrium.domain.creating.changers.ExtractedFeaturePostStep @@ -68,13 +69,13 @@ class FinalStepImpl( extractAndApply = { assertionCreator -> extractIt(this, Some(assertionCreator)) } ) - private fun extractIt(expect: Expect, subAssertions: Option.() -> Unit>) = + private fun extractIt(expect: Expect, maybeSubAssertions: Option.() -> Unit>): FeatureExpect = featureExtractor.extract( expect, featureOptions?.description ?: featureExtractionStep.description, featureExtractionStep.representationForFailure, featureExtraction, - subAssertions, + maybeSubAssertions, featureOptions?.representationInsteadOfFeature ) }