From e135750aedff2e3785c2e3bd6e0765fe06e0fc9b Mon Sep 17 00:00:00 2001 From: Joar Ekelund Date: Thu, 20 Feb 2020 12:08:46 +0100 Subject: [PATCH 1/2] implement mapAssertions with new infix API Co-authored-by: alexjdz Co-authored-by: jlundhol Co-authored-by: ashgreyship > --- .../creating/map/get/builders/MapGetOption.kt | 48 ++++ .../map/get/builders/impl/MapGetOptionImpl.kt | 14 + .../atrium/api/infix/en_GB/mapAssertions.kt | 178 +++++++++++++ .../api/infix/en_GB/parameterObjects.kt | 28 ++ .../infix/en_GB/MapAsEntriesAssertionsSpec.kt | 41 +++ .../api/infix/en_GB/MapAssertionsSpec.kt | 240 ++++++++++++++++++ .../infix/en_GB/MapFeatureAssertionsSpec.kt | 50 ++++ 7 files changed, 599 insertions(+) create mode 100644 apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt create mode 100644 apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt create mode 100644 apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt create mode 100644 apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAsEntriesAssertionsSpec.kt create mode 100644 apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt create mode 100644 apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt new file mode 100644 index 000000000..4a7c95f38 --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt @@ -0,0 +1,48 @@ +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.Assert +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> { + /** + * The [AssertionPlant] for which this assertion is created + */ + val plant: Expect + + /** + * 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 plant 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.() -> Unit): Expect + + companion object { + /** + * Creates a [MapGetOption] based on the given [plant] and [key]. + */ + fun > create(plant: Expect, key: K): MapGetOption + = MapGetOptionImpl(plant, key) + } +} + diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt new file mode 100644 index 000000000..d043387b5 --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt @@ -0,0 +1,14 @@ +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>( + override val plant: Expect, + override val key: K +) : MapGetOption { + + override infix fun assertIt(assertionCreator: Expect.() -> Unit): Expect + = plant.addAssertion(ExpectImpl.map.getExisting(plant, key).collect(assertionCreator)) +} diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt new file mode 100644 index 000000000..6cf12971b --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt @@ -0,0 +1,178 @@ +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.creating.Expect +import ch.tutteli.atrium.domain.builders.ExpectImpl + +/** + * Expects that the subject of the assertion (a [Map]) contains a key as defined by [keyValuePair]'s [Pair.first] + * with a corresponding value as defined by [keyValuePair]'s [Pair.second] + * + * Delegates to 'contains Pairs(keyValuePair)'. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.contains(keyValuePair: Pair) = + contains(Pairs(keyValuePair)) + +/** + * Expects the subject of the assertion (a [Map]) contains for each entry in [keyValuePairs], + * a key as defined by that entry's [Pair.first] with a corresponding value as defined by entry's [Pair.second]. + * + * Notice, that it does not search for unique matches. Meaning, if the map is `mapOf('a' to 1)` and one of the [Pair] + * in [keyValuePairs] is defined as `'a' to 1` and another one is defined as `'a' to 1` as well, then both match, + * even though they match the same entry. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.contains(keyValuePairs: Pairs) :Expect = + addAssertion(ExpectImpl.map.contains(this, keyValuePairs.toList())) + +/** + * Expects that the subject of the assertion (a [Map]) contains a key as defined by [keyValue]'s [KeyValue.key] + * with a corresponding value which either holds all assertions [keyValue]'s + * [KeyValue.valueAssertionCreatorOrNull] creates or needs to be `null` in case + * [KeyValue.valueAssertionCreatorOrNull] is defined as `null` + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +inline infix fun > Expect.contains(keyValue: KeyValue) :Expect = + contains(All(keyValue)) + +/** + * Expects that the subject of the assertion (a [Map]) contains for each [KeyValue] in [keyValues], + * a key as defined by [KeyValue.key] with a corresponding value which either holds all + * assertions [KeyValue]'s [KeyValue.valueAssertionCreatorOrNull] creates or needs to be `null` in case + * [KeyValue.valueAssertionCreatorOrNull] is defined as `null` + * + * Notice, that it does not search for unique matches. Meaning, if the map is `mapOf('a' to 1)` and one [KeyValue] in + * [keyValues] is defined as `Key('a') { isGreaterThan(0) }` and another one is defined as `Key('a') { isLessThan(2) }` + * , then both match, even though they match the same entry. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +inline infix fun > Expect.contains(keyValues: All>) + = addAssertion(ExpectImpl.map.containsKeyWithValueAssertions(this, V::class, keyValues.toList().map { it.toPair() })) + +/** + * Expects that the subject of the assertion (a [Map]) contains the given [key]. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.containsKey(key: K) = addAssertion(ExpectImpl.map.containsKey(this, key)) + +/** + * Expects that the subject of the assertion (a [Map]) does not contain the given [key]. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.containsNotKey(key: K) = + addAssertion(ExpectImpl.map.containsNotKey(this, key)) + +/** + * Expects that the subject of the assertion (a [Map]) contains the given [key], + * creates an [Expect] for the corresponding value and returns the newly created assertion container, + * so that further fluent calls are assertions about it. + * + * @return The newly created [Expect] for the feature. + * @throws AssertionError Might throw an [AssertionError] if the given [key] does not exist. + */ +infix fun > Expect.getExisting(key: K): Expect = + ExpectImpl.map.getExisting(this, key).getExpectOfFeature() + +/** + * Prepares the assertion about the return value of calling [get][Map.get] with the given [key]. + * + * @return A fluent builder to finish the assertion. + * */ +infix fun > Expect.getExisting(key: Key): MapGetOption + = MapGetOption.create(this, key.key) + + +/** + * Creates an [Expect] for the property [Map.keys] of the subject of the assertion, + * so that further fluent calls are assertions about it. + * + * @return The newly created [Expect] for the feature. + */ +val > Expect.keys: Expect> + get() = keys(this).getExpectOfFeature() + +/** + * Expects that the property [Map.keys] of the subject of the assertion + * holds all assertions the given [assertionCreator] creates for it and returns this assertion container. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.keys(assertionCreator: Expect>.() -> Unit): Expect = + keys(this).addToInitial(assertionCreator) + +private fun > keys(e: Expect) = ExpectImpl.feature.property(e, Map::keys) + +/** + * Expects that the subject of the assertion (a [Map]) is an empty [Map]. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.toBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) = addAssertion(ExpectImpl.map.isEmpty(this)) + +/** + * Expects that the subject of the assertion (a [Map]) is not an empty [Map]. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.notToBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) = addAssertion(ExpectImpl.map.isNotEmpty(this)) + +/** + * Creates an [Expect] for the property [Map.values] of the subject of the assertion, + * so that further fluent calls are assertions about it. + * + * @return The newly created [Expect] for the feature. + */ +val > Expect.values: Expect> + get() = values().getExpectOfFeature() + +/** + * Expects that the property [Map.keys] of the subject of the assertion + * holds all assertions the given [assertionCreator] creates for it and returns this assertion container. + * + * @return This assertion container to support a fluent API. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + */ +infix fun > Expect.values(assertionCreator: Expect>.() -> Unit): Expect = + values().addToInitial(assertionCreator) + +private fun > Expect.values() = ExpectImpl.feature.property(this, Map::values) + +/** + * Turns `Expect>` into `Expect>>`. + * + * The transformation as such is not reflected in reporting. + * Use `feature { f(it::entries) }` if you want to show the transformation in reporting. + * + * @return The newly created [Expect] for the transformed subject. + */ +fun > Expect.asEntries(): Expect>> = + ExpectImpl.changeSubject(this).unreported { it.entries } + +/** + * Turns `Expect>` into `Expect>>` and expects that it holds all assertions the given + * [assertionCreator] creates. + * + * The transformation as such is not reflected in reporting. + * Use `feature { f(it::entries) }` if you want to show the transformation in reporting. + * + * @return The newly created [Expect] for the transformed subject. + */ +infix fun > Expect.asEntries( + assertionCreator: Expect>>.() -> Unit +): Expect = apply { asEntries().addAssertionsCreatedBy(assertionCreator) } 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 227adbe66..9a3ce117d 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 @@ -1,6 +1,34 @@ package ch.tutteli.atrium.api.infix.en_GB +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.domain.builders.utils.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`. + */ +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"})" +} diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAsEntriesAssertionsSpec.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAsEntriesAssertionsSpec.kt new file mode 100644 index 000000000..1e6bced3d --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAsEntriesAssertionsSpec.kt @@ -0,0 +1,41 @@ +package ch.tutteli.atrium.api.infix.en_GB + +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.specs.feature0 +import ch.tutteli.atrium.specs.fun1 +import ch.tutteli.atrium.specs.notImplemented + +object MapAsEntriesAssertionsSpec : ch.tutteli.atrium.specs.integration.MapAsEntriesAssertionsSpec( + feature0, Set>>(Expect>::asEntries), + fun1, Expect>>.() -> Unit>(Expect>::asEntries) +) { + @Suppress("unused", "UNUSED_VALUE") + private fun ambiguityTest() { + var map: Expect> = notImplemented() + var subMap: Expect> = notImplemented() + var nullableKeyMap: Expect> = notImplemented() + var nullableValueMap: Expect> = notImplemented() + var nullableKeyValueMap: Expect> = notImplemented() + var readOnlyNullableKeyValueMap: Expect> = notImplemented() + + var starKeyMap: Expect> = notImplemented() + var starValueMap: Expect> = notImplemented() + + map asEntries {} + subMap asEntries {} + nullableKeyMap asEntries {} + nullableValueMap asEntries {} + nullableKeyValueMap asEntries {} + readOnlyNullableKeyValueMap asEntries {} + + map = map asEntries {} + subMap = subMap asEntries {} + nullableKeyMap = nullableKeyMap asEntries {} + nullableValueMap = nullableValueMap asEntries {} + nullableKeyValueMap = nullableKeyValueMap asEntries {} + readOnlyNullableKeyValueMap = readOnlyNullableKeyValueMap asEntries {} + + starKeyMap = starKeyMap asEntries {} + starValueMap = starValueMap asEntries {} + } +} diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt new file mode 100644 index 000000000..d97984b70 --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt @@ -0,0 +1,240 @@ +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.* + +class MapExpectionsSpec : 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 +) { + companion object { + private fun contains(plant: Expect>, pair: Pair, otherPairs: Array>): Expect> { + return if (otherPairs.isEmpty()) { + plant contains (pair.first to pair.second) + } else { + plant contains Pairs(pair, *otherPairs) + } + } + + private fun containsNullable(plant: Expect>, pair: Pair, otherPairs: Array>): Expect> { + return if (otherPairs.isEmpty()) { + plant contains (pair.first to pair.second) + } else { + plant contains Pairs(pair, *otherPairs) + } + } + + private fun containsKeyWithValueAssertions(plant: Expect>, keyValue: Pair.() -> Unit>, otherKeyValues: Array.() -> Unit>>) : Expect> { + return if (otherKeyValues.isEmpty()) { + plant contains KeyValue(keyValue.first, keyValue.second) + } else { + mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) -> + plant contains All(first, *others) + } + } + } + + private fun containsKeyWithNullableValueAssertions(plant: Expect>, keyValue: Pair.() -> Unit)?>, otherKeyValues: Array.() -> Unit)?>>): Expect> { + return if (otherKeyValues.isEmpty()) { + plant contains KeyValue(keyValue.first, keyValue.second) + } else { + mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) -> + plant contains All(first, *others) + } + } + } + + private fun containsKey(plant: Expect>, key: String) + = plant containsKey key + + private fun containsNullableKey(plant: Expect>, key: String?) + = plant containsKey key + + private fun containsNotKey(plant: Expect>, key: String) + = plant containsNotKey key + + private fun containsNotNullableKey(plant: Expect>, key: String?) + = plant containsNotKey key + + private fun isEmpty(plant: Expect>) + = plant toBe Empty + + private fun isNotEmpty(plant: Expect>) + = plant notToBe Empty + } + + @Suppress("unused", "UNUSED_VALUE") + private fun ambiguityTest() { + var map: Expect> = notImplemented() + var subMap: Expect> = notImplemented() + var nullableKeyMap: Expect> = notImplemented() + var nullableValueMap: Expect> = notImplemented() + var nullableKeyValueMap: Expect> = notImplemented() + var readOnlyNullableKeyValueMap: Expect> = notImplemented() + var starMap: Expect> = notImplemented() + + map contains (1 to "a") + map contains Pairs(1 to "a", 2 to "b") + map contains (KeyValue(1) {}) + map contains All(KeyValue(1) {}, KeyValue(2) {}) + map contains Pairs(1.0 to StringBuilder("a")) + map contains Pairs(12f to "a", 2L to StringBuilder("b")) + map contains (KeyValue(1) {}) + map contains All(KeyValue(1) {}, KeyValue(2) {}) + + subMap contains (1 to "a") + subMap contains Pairs(1 to "a", 2 to "b") + subMap contains (KeyValue(1) {}) + subMap contains All(KeyValue(1) {}, KeyValue(2) {}) + subMap contains (1.0 to StringBuilder("a")) + subMap contains Pairs(12f to "a", 2L to StringBuilder("b")) + subMap contains (KeyValue(1) {}) + subMap contains All(KeyValue(1) {}, KeyValue(2) {}) + + nullableKeyMap contains (1 to "a") + nullableKeyMap contains Pairs(1 to "a", 2 to "b") + nullableKeyMap contains (KeyValue(1) {}) + nullableKeyMap contains All(KeyValue(1) {}, KeyValue(2) {}) + nullableKeyMap contains (null to "a") + nullableKeyMap contains Pairs(null to "a", null to "b") + nullableKeyMap contains Pairs(null to "a", 2 to "b") + nullableKeyMap contains (KeyValue(null) {}) + nullableKeyMap contains All(KeyValue(null) {}, KeyValue(null) {}) + nullableKeyMap contains All(KeyValue(null) {}, KeyValue(2) {}) + + nullableValueMap contains (1 to "a") + nullableValueMap contains Pairs(1 to "a", 2 to "b") + nullableValueMap contains (KeyValue(1) {}) + nullableValueMap contains All(KeyValue(1) {}, KeyValue(2) {}) + nullableValueMap contains (1 to null) + nullableValueMap contains Pairs(1 to null, 2 to null) + nullableValueMap contains Pairs(1 to null, 2 to "a") + nullableValueMap contains (KeyValue(1, null)) + nullableValueMap contains All(KeyValue(1, null), KeyValue(2, null)) + nullableValueMap contains All(KeyValue(1, null), KeyValue(2) {}) + + nullableKeyValueMap contains (1 to "a") + nullableKeyValueMap contains Pairs(1 to "a", 2 to "b") + nullableKeyValueMap contains (KeyValue(1) {}) + nullableKeyValueMap contains All(KeyValue(1) {}, KeyValue(2) {}) + + nullableKeyValueMap contains (null to "a") + nullableKeyValueMap contains Pairs(null to "a", null to "b") + nullableKeyValueMap contains Pairs(null to "a", 2 to "b") + nullableKeyValueMap contains (KeyValue(null) {}) + nullableKeyValueMap contains All(KeyValue(null) {}, KeyValue(null) {}) + nullableKeyValueMap contains All(KeyValue(null) {}, KeyValue(2) {}) + + nullableKeyValueMap contains (1 to null) + nullableKeyValueMap contains Pairs(1 to null, 2 to null) + nullableKeyValueMap contains Pairs(1 to null, 2 to "a") + nullableKeyValueMap contains (KeyValue(1, null)) + nullableKeyValueMap contains All(KeyValue(1, null), KeyValue(2, null)) + nullableKeyValueMap contains All(KeyValue(1, null), KeyValue(2) {}) + + nullableKeyValueMap contains (null to null) + nullableKeyValueMap contains Pairs(null to null, null to null) + nullableKeyValueMap contains Pairs(1 to null, null to "a") + nullableKeyValueMap contains (KeyValue(null, null)) + nullableKeyValueMap contains All(KeyValue(null, null), KeyValue(null, null)) + nullableKeyValueMap contains All(KeyValue(1, null), KeyValue(null) {}) + + readOnlyNullableKeyValueMap contains (1 to "a") + readOnlyNullableKeyValueMap contains Pairs(1 to "a", 2 to "b") + readOnlyNullableKeyValueMap contains (KeyValue(1) {}) + readOnlyNullableKeyValueMap contains All(KeyValue(1) {}, KeyValue(2) {}) + + readOnlyNullableKeyValueMap contains (null to "a") + readOnlyNullableKeyValueMap contains Pairs(null to "a", null to "b") + readOnlyNullableKeyValueMap contains Pairs(null to "a", 2 to "b") + readOnlyNullableKeyValueMap contains (KeyValue(null) {}) + readOnlyNullableKeyValueMap contains All(KeyValue(null) {}, KeyValue(null) {}) + readOnlyNullableKeyValueMap contains All(KeyValue(null) {}, KeyValue(2) {}) + + readOnlyNullableKeyValueMap contains (1 to null) + readOnlyNullableKeyValueMap contains Pairs(1 to null, 2 to null) + readOnlyNullableKeyValueMap contains Pairs(1 to null, 2 to "a") + readOnlyNullableKeyValueMap contains (KeyValue(1, null)) + readOnlyNullableKeyValueMap contains All(KeyValue(1, null), KeyValue(2, null)) + readOnlyNullableKeyValueMap contains All(KeyValue(1, null), KeyValue(2) {}) + + readOnlyNullableKeyValueMap contains (null to null) + readOnlyNullableKeyValueMap contains Pairs(null to null, null to null) + readOnlyNullableKeyValueMap contains Pairs(1 to null, null to "a") + readOnlyNullableKeyValueMap contains (KeyValue(null, null)) + readOnlyNullableKeyValueMap contains All(KeyValue(null, null), KeyValue(null, null)) + readOnlyNullableKeyValueMap contains All(KeyValue(1, null), KeyValue(null) {}) + + readOnlyNullableKeyValueMap contains (1 to "a") + readOnlyNullableKeyValueMap contains Pairs(1 to "a", 2 to "b") + readOnlyNullableKeyValueMap contains (KeyValue(1) {}) + readOnlyNullableKeyValueMap contains All(KeyValue(1) {}, KeyValue(2) {}) + + starMap contains (null to "a") + starMap contains Pairs(null to "a", null to "b") + starMap contains Pairs(null to "a", 2 to "b") + starMap contains (KeyValue(null) {}) + starMap contains All(KeyValue(null) {}, KeyValue(null) {}) + starMap contains All(KeyValue(null) {}, KeyValue(2) {}) + + starMap contains (1 to null) + starMap contains Pairs(1 to null, 2 to null) + starMap contains Pairs(1 to null, 2 to "a") + starMap contains (KeyValue(1, null)) + starMap contains All(KeyValue(1, null), KeyValue(2, null)) + starMap contains All(KeyValue(1, null), KeyValue(2) {}) + + starMap contains (null to null) + starMap contains Pairs(null to null, null to null) + starMap contains Pairs(1 to null, null to "a") + starMap contains (KeyValue(null, null)) + starMap contains All(KeyValue(null, null), KeyValue(null, null)) + starMap contains All(KeyValue(1, null), KeyValue(null) {}) + + map containsKey 1 + map containsKey 1f + subMap containsKey 1 + subMap containsKey 1f + nullableKeyMap containsKey 1 + nullableKeyMap containsKey 1f + readOnlyNullableKeyValueMap containsKey 1 + readOnlyNullableKeyValueMap containsKey 1f + + map containsNotKey 1 + map containsNotKey 1f + subMap containsNotKey 1 + subMap containsNotKey 1f + nullableKeyMap containsNotKey 1 + nullableKeyMap containsNotKey 1f + readOnlyNullableKeyValueMap containsNotKey 1 + readOnlyNullableKeyValueMap containsNotKey 1f + + + map = map toBe Empty + subMap = subMap toBe Empty + nullableKeyMap = nullableKeyMap toBe Empty + nullableValueMap = nullableValueMap toBe Empty + nullableKeyValueMap = nullableKeyValueMap toBe Empty + readOnlyNullableKeyValueMap = readOnlyNullableKeyValueMap toBe Empty + starMap = starMap toBe Empty + + map = map notToBe Empty + subMap = subMap notToBe Empty + nullableKeyMap = nullableKeyMap notToBe Empty + nullableValueMap = nullableValueMap notToBe Empty + nullableKeyValueMap = nullableKeyValueMap notToBe Empty + readOnlyNullableKeyValueMap = readOnlyNullableKeyValueMap notToBe Empty + starMap = starMap notToBe Empty + + } +} diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt new file mode 100644 index 000000000..afa782ebd --- /dev/null +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt @@ -0,0 +1,50 @@ +package ch.tutteli.atrium.api.infix.en_GB + +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.specs.* + +class MapFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.MapFeatureAssertionsSpec( + property, Set>(Expect>::keys), + fun1, Expect>.() -> Unit>(Expect>::keys), + property, Collection>(Expect>::values), + fun1, Expect>.() -> Unit>(Expect>::values), + feature1, String, Int>(Expect>::getExisting), + fun2, String, Expect.() -> Unit>(Companion::getExisting), + feature1, String?, Int?>(Expect>::getExisting).withNullableSuffix(), + fun2(Companion::getExisting).name to Companion::getExistingNullable +) { + companion object { + private fun getExisting( + plant: Expect>, + key: String, + assertionCreator: Expect.() -> Unit + ): Expect> + = plant getExisting Key(key) assertIt { assertionCreator() } + + private fun getExistingNullable( + plant: Expect>, + key: String?, + assertionCreator: Expect.() -> Unit + ): Expect> + = plant getExisting Key(key) assertIt { assertionCreator() } + } + + @Suppress("unused", "UNUSED_VALUE") + private fun ambiguityTest() { + var a1: Expect> = notImplemented() + var a2: Expect> = notImplemented() + var a3: Expect> = notImplemented() + var star: Expect> = notImplemented() + + //TODO ideally this would not work as the map has not defined the key to be out + a1 getExisting 1 + a2 getExisting 1 + 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 { } + } +} From f4e9d5b7d7f45845bdb47a49d96c01de05e51d2e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 Feb 2020 10:04:14 +0100 Subject: [PATCH 2/2] format files, rename plant to expect --- .../creating/map/get/builders/MapGetOption.kt | 11 ++-- .../map/get/builders/impl/MapGetOptionImpl.kt | 10 ++-- .../atrium/api/infix/en_GB/mapAssertions.kt | 20 ++++--- .../api/infix/en_GB/parameterObjects.kt | 4 +- .../api/infix/en_GB/MapAssertionsSpec.kt | 59 +++++++++++-------- .../infix/en_GB/MapFeatureAssertionsSpec.kt | 10 ++-- 6 files changed, 62 insertions(+), 52 deletions(-) diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt index 4a7c95f38..179fb375b 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/MapGetOption.kt @@ -2,7 +2,6 @@ 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.Assert import ch.tutteli.atrium.creating.AssertionPlant import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.creating.SubjectProvider @@ -19,7 +18,7 @@ interface MapGetOption> { /** * The [AssertionPlant] for which this assertion is created */ - val plant: Expect + val expect: Expect /** * The given key which will be used to perform the [Map.get]. @@ -30,7 +29,7 @@ interface MapGetOption> { * 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 plant to support a fluent API. + * @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. @@ -39,10 +38,10 @@ interface MapGetOption> { companion object { /** - * Creates a [MapGetOption] based on the given [plant] and [key]. + * Creates a [MapGetOption] based on the given [expect] and [key]. */ - fun > create(plant: Expect, key: K): MapGetOption - = MapGetOptionImpl(plant, key) + fun > create(expect: Expect, key: K): MapGetOption = + MapGetOptionImpl(expect, key) } } diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt index d043387b5..ae34f177f 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/creating/map/get/builders/impl/MapGetOptionImpl.kt @@ -4,11 +4,11 @@ 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>( - override val plant: Expect, +internal class MapGetOptionImpl>( + override val expect: Expect, override val key: K ) : MapGetOption { - - override infix fun assertIt(assertionCreator: Expect.() -> Unit): Expect - = plant.addAssertion(ExpectImpl.map.getExisting(plant, key).collect(assertionCreator)) + + override infix fun assertIt(assertionCreator: Expect.() -> Unit): Expect = + expect.addAssertion(ExpectImpl.map.getExisting(expect, key).collect(assertionCreator)) } diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt index 6cf12971b..a51518408 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/main/kotlin/ch/tutteli/atrium/api/infix/en_GB/mapAssertions.kt @@ -13,7 +13,7 @@ import ch.tutteli.atrium.domain.builders.ExpectImpl * @return This assertion container to support a fluent API. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. */ -infix fun > Expect.contains(keyValuePair: Pair) = +infix fun > Expect.contains(keyValuePair: Pair) = contains(Pairs(keyValuePair)) /** @@ -27,7 +27,7 @@ infix fun > Expect.contains(keyValuePair: Pair) * @return This assertion container to support a fluent API. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. */ -infix fun > Expect.contains(keyValuePairs: Pairs) :Expect = +infix fun > Expect.contains(keyValuePairs: Pairs): Expect = addAssertion(ExpectImpl.map.contains(this, keyValuePairs.toList())) /** @@ -39,7 +39,7 @@ infix fun > Expect.contains(keyValuePairs: Pairs> Expect.contains(keyValue: KeyValue) :Expect = +inline infix fun > Expect.contains(keyValue: KeyValue): Expect = contains(All(keyValue)) /** @@ -55,8 +55,8 @@ inline infix fun > Expect.contains(key * @return This assertion container to support a fluent API. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. */ -inline infix fun > Expect.contains(keyValues: All>) - = addAssertion(ExpectImpl.map.containsKeyWithValueAssertions(this, V::class, keyValues.toList().map { it.toPair() })) +inline infix fun > Expect.contains(keyValues: All>) = + addAssertion(ExpectImpl.map.containsKeyWithValueAssertions(this, V::class, keyValues.toList().map { it.toPair() })) /** * Expects that the subject of the assertion (a [Map]) contains the given [key]. @@ -91,8 +91,8 @@ infix fun > Expect.getExisting(key: K): Expect = * * @return A fluent builder to finish the assertion. * */ -infix fun > Expect.getExisting(key: Key): MapGetOption - = MapGetOption.create(this, key.key) +infix fun > Expect.getExisting(key: Key): MapGetOption = + MapGetOption.create(this, key.key) /** @@ -122,7 +122,8 @@ private fun > keys(e: Expect) = ExpectImpl.feature.prope * @return This assertion container to support a fluent API. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. */ -infix fun > Expect.toBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) = addAssertion(ExpectImpl.map.isEmpty(this)) +infix fun > Expect.toBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) = + addAssertion(ExpectImpl.map.isEmpty(this)) /** * Expects that the subject of the assertion (a [Map]) is not an empty [Map]. @@ -130,7 +131,8 @@ infix fun > Expect.toBe(@Suppress("UNUSED_PARAMETER") Empty: Em * @return This assertion container to support a fluent API. * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. */ -infix fun > Expect.notToBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) = addAssertion(ExpectImpl.map.isNotEmpty(this)) +infix fun > Expect.notToBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) = + addAssertion(ExpectImpl.map.isNotEmpty(this)) /** * Creates an [Expect] for the property [Map.values] of the subject of the assertion, 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 9a3ce117d..ee945e498 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 @@ -29,6 +29,6 @@ class Pairs( */ 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"})" + override fun toString(): String = + "KeyValue(key=$key, value=${if (valueAssertionCreatorOrNull == null) "null" else "lambda"})" } diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt index d97984b70..0141e8ead 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapAssertionsSpec.kt @@ -18,59 +18,70 @@ class MapExpectionsSpec : ch.tutteli.atrium.specs.integration.MapAssertionsSpec( "notToBe ${Empty::class.simpleName}" to Companion::isNotEmpty ) { companion object { - private fun contains(plant: Expect>, pair: Pair, otherPairs: Array>): Expect> { + private fun contains( + expect: Expect>, + pair: Pair, + otherPairs: Array> + ): Expect> { return if (otherPairs.isEmpty()) { - plant contains (pair.first to pair.second) + expect contains (pair.first to pair.second) } else { - plant contains Pairs(pair, *otherPairs) + expect contains Pairs(pair, *otherPairs) } } - private fun containsNullable(plant: Expect>, pair: Pair, otherPairs: Array>): Expect> { + private fun containsNullable( + expect: Expect>, + pair: Pair, + otherPairs: Array> + ): Expect> { return if (otherPairs.isEmpty()) { - plant contains (pair.first to pair.second) + expect contains (pair.first to pair.second) } else { - plant contains Pairs(pair, *otherPairs) + expect contains Pairs(pair, *otherPairs) } } - private fun containsKeyWithValueAssertions(plant: Expect>, keyValue: Pair.() -> Unit>, otherKeyValues: Array.() -> Unit>>) : Expect> { + private fun containsKeyWithValueAssertions( + expect: Expect>, + keyValue: Pair.() -> Unit>, + otherKeyValues: Array.() -> Unit>> + ): Expect> { return if (otherKeyValues.isEmpty()) { - plant contains KeyValue(keyValue.first, keyValue.second) + expect contains KeyValue(keyValue.first, keyValue.second) } else { mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) -> - plant contains All(first, *others) + expect contains All(first, *others) } } } - private fun containsKeyWithNullableValueAssertions(plant: Expect>, keyValue: Pair.() -> Unit)?>, otherKeyValues: Array.() -> Unit)?>>): Expect> { + private fun containsKeyWithNullableValueAssertions( + expect: Expect>, + keyValue: Pair.() -> Unit)?>, + otherKeyValues: Array.() -> Unit)?>> + ): Expect> { return if (otherKeyValues.isEmpty()) { - plant contains KeyValue(keyValue.first, keyValue.second) + expect contains KeyValue(keyValue.first, keyValue.second) } else { mapArguments(keyValue, otherKeyValues).to { KeyValue(it.first, it.second) }.let { (first, others) -> - plant contains All(first, *others) + expect contains All(first, *others) } } } - private fun containsKey(plant: Expect>, key: String) - = plant containsKey key + private fun containsKey(expect: Expect>, key: String) = expect containsKey key - private fun containsNullableKey(plant: Expect>, key: String?) - = plant containsKey key + private fun containsNullableKey(expect: Expect>, key: String?) = expect containsKey key - private fun containsNotKey(plant: Expect>, key: String) - = plant containsNotKey key + private fun containsNotKey(expect: Expect>, key: String) = expect containsNotKey key - private fun containsNotNullableKey(plant: Expect>, key: String?) - = plant containsNotKey key + private fun containsNotNullableKey(expect: Expect>, key: String?) = + expect containsNotKey key - private fun isEmpty(plant: Expect>) - = plant toBe Empty + private fun isEmpty(expect: Expect>) = expect toBe Empty - private fun isNotEmpty(plant: Expect>) - = plant notToBe Empty + private fun isNotEmpty(expect: Expect>) = expect notToBe Empty } @Suppress("unused", "UNUSED_VALUE") diff --git a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt index afa782ebd..20ef9dbf2 100644 --- a/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt +++ b/apis/infix-en_GB/atrium-api-infix-en_GB-common/src/test/kotlin/ch/tutteli/atrium/api/infix/en_GB/MapFeatureAssertionsSpec.kt @@ -15,18 +15,16 @@ class MapFeatureAssertionsSpec : ch.tutteli.atrium.specs.integration.MapFeatureA ) { companion object { private fun getExisting( - plant: Expect>, + expect: Expect>, key: String, assertionCreator: Expect.() -> Unit - ): Expect> - = plant getExisting Key(key) assertIt { assertionCreator() } + ): Expect> = expect getExisting Key(key) assertIt { assertionCreator() } private fun getExistingNullable( - plant: Expect>, + expect: Expect>, key: String?, assertionCreator: Expect.() -> Unit - ): Expect> - = plant getExisting Key(key) assertIt { assertionCreator() } + ): Expect> = expect getExisting Key(key) assertIt { assertionCreator() } } @Suppress("unused", "UNUSED_VALUE")