introduce partially fixed assertion group and use it for Map/List.get

showing whether a key exists in a Map or if the index is within its
bounds for List is kind of redundant information. It suffices that the
assertion group fails. Yet, we cannot use fixedClaimGroup since if
the pre-transformation (in this case extracting a feature) holds, then
the group can still fail if sub assertions fail. Thus

- introduce a group with a partially fixed part which forms an AND
  operation with the sub assertions.
- use it for List/Map.get
  - remove Description keys which are not longer required
This commit is contained in:
Robert Stoll
2019-01-01 18:49:37 +01:00
parent 97edab5c30
commit 7d6bc91495
17 changed files with 243 additions and 89 deletions

View File

@@ -19,50 +19,9 @@ import ch.tutteli.atrium.reporting.translating.Translatable
val AssertionBuilder.fixedClaimGroup: FixedClaimAssertionGroupTypeOption
get() = FixedClaimAssertionGroupTypeOptionImpl
interface FixedClaimAssertionGroupTypeOption: FixedClaimLikeAssertionGroupTypeOption<FixedClaimAssertionGroupFinalStep>
/**
* Option step which allows to specify the [AssertionGroup.type].
*/
interface FixedClaimAssertionGroupTypeOption {
/**
* Uses [ListAssertionGroupType] as [AssertionGroup.type].
*/
val withListType: FixedClaimAssertionGroupHoldsOption<ListAssertionGroupType>
/**
* Uses the given [type] as [AssertionGroup.type].
*
* @param type The [AssertionGroup.type].
*/
fun <T : AssertionGroupType> withType(type: T): FixedClaimAssertionGroupHoldsOption<T>
}
/**
* Option step which allows to specify the [AssertionGroup.holds].
*/
interface FixedClaimAssertionGroupHoldsOption<T : AssertionGroupType> {
/**
* The previously defined [AssertionGroup.type].
*/
val groupType: T
/**
* Defines the [AssertionGroup] holds.
*/
val holding: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, FixedClaimAssertionGroupFinalStep>>
/**
* Defines the [AssertionGroup] does not hold.
*/
val failing: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, FixedClaimAssertionGroupFinalStep>>
/**
* Uses the given [holds] as [AssertionGroup.holds].
*/
fun withClaim(holds: Boolean): AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, FixedClaimAssertionGroupFinalStep>>
interface FixedClaimAssertionGroupHoldsOption<T : AssertionGroupType> : FixedClaimLikeAssertionGroupHoldsOption<T, FixedClaimAssertionGroupFinalStep>{
companion object {
fun <T: AssertionGroupType> create(groupType: T): FixedClaimAssertionGroupHoldsOption<T>
= FixedClaimAssertionGroupHoldsOptionImpl(groupType)

View File

@@ -0,0 +1,57 @@
package ch.tutteli.atrium.domain.builders.assertions.builders
import ch.tutteli.atrium.assertions.*
import ch.tutteli.atrium.assertions.builders.AssertionGroupDescriptionAndRepresentationOption
import ch.tutteli.atrium.assertions.builders.AssertionsOption
/**
* Option step which allows to specify the [AssertionGroup.type].
*/
interface FixedClaimLikeAssertionGroupTypeOption<R> {
/**
* Uses [ListAssertionGroupType] as [AssertionGroup.type].
*/
val withFeatureType: FixedClaimLikeAssertionGroupHoldsOption<FeatureAssertionGroupType, R>
get() = withType(DefaultFeatureAssertionGroupType)
/**
* Uses [ListAssertionGroupType] as [AssertionGroup.type].
*/
val withListType: FixedClaimLikeAssertionGroupHoldsOption<ListAssertionGroupType, R>
get() = withType(DefaultListAssertionGroupType)
/**
* Uses the given [groupType] as [AssertionGroup.type].
*
* @param groupType The [AssertionGroup.type].
*/
fun <T : AssertionGroupType> withType(groupType: T): FixedClaimLikeAssertionGroupHoldsOption<T, R>
}
/**
* Option step which allows to specify the [AssertionGroup.holds] or another fixed part involved
* in calculating [AssertionGroup.holds].
*/
interface FixedClaimLikeAssertionGroupHoldsOption<T : AssertionGroupType, R> {
/**
* The previously defined [AssertionGroup.type].
*/
val groupType: T
/**
* Defines the [AssertionGroup] (or the fixed part) holds
*/
val holding: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, R>>
/**
* Defines the [AssertionGroup] (or the fixed part) does not hold.
*/
val failing: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, R>>
/**
* Uses the given [holds] as [AssertionGroup.holds] (or for the fixed part).
*/
fun withClaim(holds: Boolean): AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, R>>
}

View File

@@ -14,9 +14,16 @@ internal class DescriptiveAssertionWithFailureHintShowOptionImpl(
override val showForAnyFailure get(): DescriptiveLikeAssertionDescriptionOption<DescriptiveAssertionWithFailureHintFinalStep>
= createDescriptiveLikeAssertionDescriptionOption(trueProvider)
override fun showOnlyIf(predicate: () -> Boolean): DescriptiveLikeAssertionDescriptionOption<DescriptiveAssertionWithFailureHintFinalStep>
override fun showOnlyIf(
predicate: () -> Boolean
): DescriptiveLikeAssertionDescriptionOption<DescriptiveAssertionWithFailureHintFinalStep>
= createDescriptiveLikeAssertionDescriptionOption(predicate)
private fun createDescriptiveLikeAssertionDescriptionOption(predicate: () -> Boolean): DescriptiveLikeAssertionDescriptionOption<DescriptiveAssertionWithFailureHintFinalStep>
= DescriptiveLikeAssertionDescriptionOption.create(test, { t, d, r -> DescriptiveAssertionWithFailureHintFinalStep.create(t, predicate, failureHintFactory, d, r) })
private fun createDescriptiveLikeAssertionDescriptionOption(
predicate: () -> Boolean
): DescriptiveLikeAssertionDescriptionOption<DescriptiveAssertionWithFailureHintFinalStep>
= DescriptiveLikeAssertionDescriptionOption.create(
test,
{ t, d, r -> DescriptiveAssertionWithFailureHintFinalStep.create(t, predicate, failureHintFactory, d, r) }
)
}

View File

@@ -2,31 +2,18 @@ package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.AssertionGroupType
import ch.tutteli.atrium.assertions.builders.AssertionsOption
import ch.tutteli.atrium.assertions.builders.AssertionGroupDescriptionAndRepresentationOption
import ch.tutteli.atrium.domain.builders.assertions.builders.FixedClaimAssertionGroupFinalStep
import ch.tutteli.atrium.domain.builders.assertions.builders.FixedClaimAssertionGroupHoldsOption
import ch.tutteli.atrium.reporting.translating.Translatable
internal class FixedClaimAssertionGroupHoldsOptionImpl<T: AssertionGroupType>(
internal class FixedClaimAssertionGroupHoldsOptionImpl<T : AssertionGroupType>(
override val groupType: T
) : FixedClaimAssertionGroupHoldsOption<T> {
) : FixedClaimLikeAssertionGroupHoldsOptionImpl<T, FixedClaimAssertionGroupFinalStep>(groupType),
FixedClaimAssertionGroupHoldsOption<T> {
override val holding: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, FixedClaimAssertionGroupFinalStep>>
get() = createDescriptionAndRepresentationOption(true)
override val failing: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, FixedClaimAssertionGroupFinalStep>>
get() = createDescriptionAndRepresentationOption(false)
override fun withClaim(holds: Boolean): AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, FixedClaimAssertionGroupFinalStep>>
= createDescriptionAndRepresentationOption(holds)
private fun createDescriptionAndRepresentationOption(holds: Boolean)
= AssertionGroupDescriptionAndRepresentationOption.create(groupType, createAssertionOptionWithHolds(holds))
private fun createAssertionOptionWithHolds(holds: Boolean): (T, Translatable, Any) -> AssertionsOption<T, FixedClaimAssertionGroupFinalStep>
= { t, d, r -> AssertionsOption.create(t, d, r, createFixedClaimAssertionGroupFinalStep(holds)) }
private fun createFixedClaimAssertionGroupFinalStep(holds: Boolean): (T, Translatable, Any, List<Assertion>) -> FixedClaimAssertionGroupFinalStep
= {t, d, r, a -> FixedClaimAssertionGroupFinalStep.create(t, d, r, a, holds)}
override fun createFixedClaimLikeAssertionGroupFinalStep(
holds: Boolean
): (T, Translatable, Any, List<Assertion>) -> FixedClaimAssertionGroupFinalStep = { t, d, r, a ->
FixedClaimAssertionGroupFinalStep.create(t, d, r, a, holds)
}
}

View File

@@ -1,16 +1,11 @@
package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.AssertionGroupType
import ch.tutteli.atrium.assertions.DefaultListAssertionGroupType
import ch.tutteli.atrium.assertions.ListAssertionGroupType
import ch.tutteli.atrium.domain.builders.assertions.builders.FixedClaimAssertionGroupHoldsOption
import ch.tutteli.atrium.domain.builders.assertions.builders.FixedClaimAssertionGroupTypeOption
internal object FixedClaimAssertionGroupTypeOptionImpl : FixedClaimAssertionGroupTypeOption {
override val withListType: FixedClaimAssertionGroupHoldsOption<ListAssertionGroupType>
get() = FixedClaimAssertionGroupHoldsOption.create(DefaultListAssertionGroupType)
override fun <T : AssertionGroupType> withType(type: T): FixedClaimAssertionGroupHoldsOption<T>
= FixedClaimAssertionGroupHoldsOption.create(type)
override fun <T : AssertionGroupType> withType(groupType: T): FixedClaimAssertionGroupHoldsOption<T>
= FixedClaimAssertionGroupHoldsOption.create(groupType)
}

View File

@@ -0,0 +1,31 @@
package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.AssertionGroupType
import ch.tutteli.atrium.assertions.builders.AssertionGroupDescriptionAndRepresentationOption
import ch.tutteli.atrium.assertions.builders.AssertionsOption
import ch.tutteli.atrium.domain.builders.assertions.builders.FixedClaimLikeAssertionGroupHoldsOption
import ch.tutteli.atrium.reporting.translating.Translatable
internal abstract class FixedClaimLikeAssertionGroupHoldsOptionImpl<T : AssertionGroupType, R>(
override val groupType: T
) : FixedClaimLikeAssertionGroupHoldsOption<T, R> {
override val holding: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, R>>
get() = createDescriptionAndRepresentationOption(true)
override val failing: AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, R>>
get() = createDescriptionAndRepresentationOption(false)
override fun withClaim(holds: Boolean): AssertionGroupDescriptionAndRepresentationOption<T, AssertionsOption<T, R>>
= createDescriptionAndRepresentationOption(holds)
private fun createDescriptionAndRepresentationOption(holds: Boolean)
= AssertionGroupDescriptionAndRepresentationOption.create(groupType, createAssertionOptionWithHolds(holds))
private fun createAssertionOptionWithHolds(holds: Boolean): (T, Translatable, Any) -> AssertionsOption<T, R>
= { t, d, r -> AssertionsOption.create(t, d, r, createFixedClaimLikeAssertionGroupFinalStep(holds)) }
protected abstract fun createFixedClaimLikeAssertionGroupFinalStep(
holds: Boolean
): (T, Translatable, Any, List<Assertion>) -> R
}

View File

@@ -0,0 +1,20 @@
package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.*
import ch.tutteli.atrium.reporting.translating.Translatable
internal data class PartiallyFixedClaimAssertionGroup(
override val type: AssertionGroupType,
override val description: Translatable,
override val representation: Any,
override val assertions: List<Assertion>,
private val preTransformationHolds: Boolean
) : AssertionGroup {
/**
* Holds if [preTransformationHolds] and all its [assertions] hold.
*
* @return `true` if [preTransformationHolds] and all [assertions] hold; `false` otherwise.
*/
override fun holds() = preTransformationHolds && super.holds()
}

View File

@@ -0,0 +1,19 @@
package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.AssertionGroup
import ch.tutteli.atrium.assertions.AssertionGroupType
import ch.tutteli.atrium.domain.builders.assertions.builders.PartiallyFixedClaimAssertionGroupFinalStep
import ch.tutteli.atrium.reporting.translating.Translatable
internal class PartiallyFixedClaimAssertionGroupFinalStepImpl(
override val groupType: AssertionGroupType,
override val description: Translatable,
override val representation: Any,
override val assertions: List<Assertion>,
override val preTransformationHolds: Boolean
) : PartiallyFixedClaimAssertionGroupFinalStep {
override fun build(): AssertionGroup
= PartiallyFixedClaimAssertionGroup(groupType, description, representation, assertions, preTransformationHolds)
}

View File

@@ -0,0 +1,19 @@
package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.AssertionGroupType
import ch.tutteli.atrium.domain.builders.assertions.builders.PartiallyFixedClaimAssertionGroupFinalStep
import ch.tutteli.atrium.domain.builders.assertions.builders.PartiallyFixedClaimAssertionGroupHoldsOption
import ch.tutteli.atrium.reporting.translating.Translatable
internal class PartiallyFixedClaimAssertionGroupHoldsOptionImpl<T : AssertionGroupType>(
groupType: T
) : FixedClaimLikeAssertionGroupHoldsOptionImpl<T, PartiallyFixedClaimAssertionGroupFinalStep>(groupType),
PartiallyFixedClaimAssertionGroupHoldsOption<T> {
override fun createFixedClaimLikeAssertionGroupFinalStep(
holds: Boolean
): (T, Translatable, Any, List<Assertion>) -> PartiallyFixedClaimAssertionGroupFinalStep = { t, d, r, a ->
PartiallyFixedClaimAssertionGroupFinalStep.create(t, d, r, a, holds)
}
}

View File

@@ -0,0 +1,11 @@
package ch.tutteli.atrium.domain.builders.assertions.builders.impl
import ch.tutteli.atrium.assertions.AssertionGroupType
import ch.tutteli.atrium.domain.builders.assertions.builders.PartiallyFixedClaimAssertionGroupHoldsOption
import ch.tutteli.atrium.domain.builders.assertions.builders.PartiallyFixedClaimAssertionGroupTypeOption
internal object PartiallyFixedClaimAssertionGroupTypeOptionImpl : PartiallyFixedClaimAssertionGroupTypeOption {
override fun <T : AssertionGroupType> withType(groupType: T): PartiallyFixedClaimAssertionGroupHoldsOption<T>
= PartiallyFixedClaimAssertionGroupHoldsOption.create(groupType)
}

View File

@@ -0,0 +1,53 @@
package ch.tutteli.atrium.domain.builders.assertions.builders
import ch.tutteli.atrium.assertions.*
import ch.tutteli.atrium.assertions.builders.AssertionBuilder
import ch.tutteli.atrium.assertions.builders.BasicAssertionGroupFinalStep
import ch.tutteli.atrium.domain.builders.assertions.builders.impl.PartiallyFixedClaimAssertionGroupFinalStepImpl
import ch.tutteli.atrium.domain.builders.assertions.builders.impl.PartiallyFixedClaimAssertionGroupHoldsOptionImpl
import ch.tutteli.atrium.domain.builders.assertions.builders.impl.PartiallyFixedClaimAssertionGroupTypeOptionImpl
import ch.tutteli.atrium.reporting.translating.Translatable
/**
* Builder to create an [AssertionGroup] whose [AssertionGroup.holds] is a logic AND operation composed by a fixed
* part and its [AssertionGroup.assertions].
*
* The intended use case is if the [AssertionGroup.assertions] depend on a pre-transformation (e.g., extract a feature,
* type transformation etc.) which can either be successful or fail.
* If it fails, then the assertions cannot be carried out (only be used for explanation) and in such a case
* [AssertionGroup.holds] has to be false. Otherwise [AssertionGroup.holds] depend on [AssertionGroup.assertions].
*/
@Suppress("unused")
val AssertionBuilder.partiallyFixedClaimGroup: PartiallyFixedClaimAssertionGroupTypeOption
get() = PartiallyFixedClaimAssertionGroupTypeOptionImpl
interface PartiallyFixedClaimAssertionGroupTypeOption: FixedClaimLikeAssertionGroupTypeOption<PartiallyFixedClaimAssertionGroupFinalStep>
interface PartiallyFixedClaimAssertionGroupHoldsOption<T : AssertionGroupType> : FixedClaimLikeAssertionGroupHoldsOption<T, PartiallyFixedClaimAssertionGroupFinalStep>{
companion object {
fun <T: AssertionGroupType> create(groupType: T): PartiallyFixedClaimAssertionGroupHoldsOption<T>
= PartiallyFixedClaimAssertionGroupHoldsOptionImpl(groupType)
}
}
/**
* Final step which creates an [AssertionGroup] whose [AssertionGroup.holds] is a logic AND operation composed by
* [preTransformationHolds] and its [AssertionGroup.assertions].
*/
interface PartiallyFixedClaimAssertionGroupFinalStep : BasicAssertionGroupFinalStep {
/**
* The previously defined state of the pre-transformation (if it holds or not).
*/
val preTransformationHolds: Boolean
companion object {
fun create(
groupType: AssertionGroupType,
description: Translatable,
representation: Any,
assertions: List<Assertion>,
holds: Boolean
): PartiallyFixedClaimAssertionGroupFinalStep
= PartiallyFixedClaimAssertionGroupFinalStepImpl(groupType, description, representation, assertions, holds)
}
}

View File

@@ -5,6 +5,7 @@ import ch.tutteli.atrium.assertions.AssertionGroup
import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.domain.builders.AssertImpl
import ch.tutteli.atrium.domain.builders.assertions.builders.partiallyFixedClaimGroup
import ch.tutteli.atrium.domain.builders.creating.collectors.collectOrExplain
import ch.tutteli.atrium.reporting.RawString
import ch.tutteli.atrium.reporting.translating.Untranslatable
@@ -29,7 +30,7 @@ private fun <T, A : BaseAssertionPlant<T, A>, C : BaseCollectingAssertionPlant<T
collectingPlantFactory: (() -> T) -> C,
assertionCreator: C.() -> Unit
): Assertion {
//TODO, same as MAP, generalise to extract
//TODO, same as for Map, generalise to a fun like extractFeature
val holds = try {
index < plant.subject.size
} catch (e: PlantHasNoSubjectException) {
@@ -46,14 +47,13 @@ private fun <T, A : BaseAssertionPlant<T, A>, C : BaseCollectingAssertionPlant<T
val methodCallRepresentation = coreFactory.newMethodCallFormatter().format("get", arrayOf(index))
return AssertImpl.builder.feature
return AssertImpl.builder.partiallyFixedClaimGroup
.withFeatureType
.withClaim(holds)
.withDescriptionAndRepresentation(Untranslatable(methodCallRepresentation())) {
if (holds) plant.subject[index] ?: RawString.NULL
else RawString.create(DescriptionListAssertion.INDEX_OUT_OF_BOUNDS)
}
.withAssertions(
AssertImpl.builder.createDescriptive(DescriptionListAssertion.INDEX_WITHIN_BOUND, true) { holds },
assertion
)
.withAssertion(assertion)
.build()
}

View File

@@ -7,6 +7,7 @@ import ch.tutteli.atrium.assertions.AssertionGroup
import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.domain.builders.AssertImpl
import ch.tutteli.atrium.domain.builders.assertions.builders.partiallyFixedClaimGroup
import ch.tutteli.atrium.domain.builders.creating.collectors.collectOrExplain
import ch.tutteli.atrium.reporting.RawString
import ch.tutteli.atrium.reporting.translating.Untranslatable
@@ -81,14 +82,13 @@ private fun <K, V, A : BaseAssertionPlant<V, A>, C : BaseCollectingAssertionPlan
val methodCallRepresentation = coreFactory.newMethodCallFormatter().format("get", arrayOf<Any?>(key))
return AssertImpl.builder.feature
return AssertImpl.builder.partiallyFixedClaimGroup
.withFeatureType
.withClaim(holds)
.withDescriptionAndRepresentation(Untranslatable(methodCallRepresentation())) {
if (holds) plant.subject[key] ?: RawString.NULL
else RawString.create(DescriptionMapAssertion.KEY_DOES_NOT_EXIST)
}
.withAssertions(
AssertImpl.builder.createDescriptive(DescriptionMapAssertion.KEY_EXISTS, true) { holds },
assertion
)
.withAssertion(assertion)
.build()
}

View File

@@ -8,6 +8,5 @@ import ch.tutteli.atrium.reporting.translating.StringBasedTranslatable
*/
enum class DescriptionListAssertion(override val value: String) : StringBasedTranslatable {
CANNOT_EVALUATE_INDEX_OUT_OF_BOUNDS("$COULD_NOT_EVALUATE_DEFINED_ASSERTIONS -- Index ausserhalb der Grenzen (index out of bounds).\n$VISIT_COULD_NOT_EVALUATE_ASSERTIONS"),
INDEX_WITHIN_BOUND("Index innerhalb der Grenzen"),
INDEX_OUT_OF_BOUNDS("❗❗ Index ausserhalb der Grenzen (index out of bounds)")
}

View File

@@ -8,6 +8,5 @@ import ch.tutteli.atrium.reporting.translating.StringBasedTranslatable
*/
enum class DescriptionMapAssertion(override val value: String) : StringBasedTranslatable {
CANNOT_EVALUATE_KEY_DOES_NOT_EXIST("$COULD_NOT_EVALUATE_DEFINED_ASSERTIONS -- der gegebene Key existiert nicht.\n$VISIT_COULD_NOT_EVALUATE_ASSERTIONS"),
KEY_EXISTS("Key existiert"),
KEY_DOES_NOT_EXIST("❗❗ Key existiert nicht")
}

View File

@@ -8,6 +8,5 @@ import ch.tutteli.atrium.reporting.translating.StringBasedTranslatable
*/
enum class DescriptionListAssertion(override val value: String) : StringBasedTranslatable {
CANNOT_EVALUATE_INDEX_OUT_OF_BOUNDS("$COULD_NOT_EVALUATE_DEFINED_ASSERTIONS -- index out of bounds.\n$VISIT_COULD_NOT_EVALUATE_ASSERTIONS"),
INDEX_WITHIN_BOUND("index within bound"),
INDEX_OUT_OF_BOUNDS("❗❗ index out of bounds")
}

View File

@@ -8,6 +8,5 @@ import ch.tutteli.atrium.reporting.translating.StringBasedTranslatable
*/
enum class DescriptionMapAssertion(override val value: String) : StringBasedTranslatable {
CANNOT_EVALUATE_KEY_DOES_NOT_EXIST("$COULD_NOT_EVALUATE_DEFINED_ASSERTIONS -- given key does not exist.\n$VISIT_COULD_NOT_EVALUATE_ASSERTIONS"),
KEY_EXISTS("key exists"),
KEY_DOES_NOT_EXIST("❗❗ key does not exist")
}