From 91fad2624cdbee65ec9873a2592ed873dfa277b7 Mon Sep 17 00:00:00 2001 From: Edward Hou <36526373+wordhou@users.noreply.github.com> Date: Wed, 9 Jun 2021 00:16:57 -0700 Subject: [PATCH] Make notToContain less verbose and show mismatched indices (#931) --- .../creators/impl/ContainsAssertionCreator.kt | 11 +++- .../impl/ContainsObjectsAssertionCreator.kt | 17 +---- .../CharSequenceContainsAssertionCreator.kt | 9 --- .../impl/InAnyOrderEntriesAssertionCreator.kt | 55 +++++++++++----- .../impl/InAnyOrderValuesAssertionCreator.kt | 62 ++++++++++++------ .../impl/DefaultIterableLikeAssertions.kt | 30 +-------- .../atrium/logic/impl/containsHelpers.kt | 35 +++++++++++ ...ableNotToContainEntriesExpectationsSpec.kt | 48 ++++++-------- ...rableNotToContainValuesExpectationsSpec.kt | 63 ++++++++----------- .../IterableToContainEntriesSpecBase.kt | 12 ++-- .../integration/IterableToContainSpecBase.kt | 4 ++ ...leToHaveElementsAndNoneExpectationsSpec.kt | 31 ++++----- settings.gradle.kts | 15 ++++- 13 files changed, 218 insertions(+), 174 deletions(-) diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsAssertionCreator.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsAssertionCreator.kt index 67f08d779..6a510c80a 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsAssertionCreator.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsAssertionCreator.kt @@ -42,12 +42,21 @@ abstract class ContainsAssertionCreator + ): AssertionGroup = inAnyOrderAssertion + /** * Make the underlying subject multiple times consumable. */ diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsObjectsAssertionCreator.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsObjectsAssertionCreator.kt index 3fac2263e..8f7796782 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsObjectsAssertionCreator.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/basic/contains/creators/impl/ContainsObjectsAssertionCreator.kt @@ -2,7 +2,6 @@ package ch.tutteli.atrium.logic.creating.basic.contains.creators.impl import ch.tutteli.atrium.assertions.Assertion import ch.tutteli.atrium.assertions.AssertionGroup -import ch.tutteli.atrium.assertions.AssertionGroupType import ch.tutteli.atrium.assertions.builders.assertionBuilder import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.logic.creating.basic.contains.Contains @@ -32,7 +31,7 @@ abstract class ContainsObjectsAssertionCreator ) : ContainsAssertionCreator(searchBehaviour, checkers) { - final override fun searchAndCreateAssertion( + override fun searchAndCreateAssertion( multiConsumableContainer: AssertionContainer, searchCriterion: SC, featureFactory: (Int, Translatable) -> AssertionGroup @@ -40,9 +39,9 @@ abstract class ContainsObjectsAssertionCreator, searchCriterion: SC): Int - - /** - * Either return the given [featureAssertion] as [List] or add further assertions. - */ - abstract fun decorateAssertion(container: AssertionContainer, featureAssertion: Assertion): List } diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/charsequence/contains/creators/impl/CharSequenceContainsAssertionCreator.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/charsequence/contains/creators/impl/CharSequenceContainsAssertionCreator.kt index 9c03b4716..246d66c61 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/charsequence/contains/creators/impl/CharSequenceContainsAssertionCreator.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/charsequence/contains/creators/impl/CharSequenceContainsAssertionCreator.kt @@ -2,8 +2,6 @@ package ch.tutteli.atrium.logic.creating.charsequence.contains.creators.impl import ch.tutteli.atrium.assertions.Assertion import ch.tutteli.atrium.assertions.AssertionGroup -import ch.tutteli.atrium.assertions.AssertionGroupType -import ch.tutteli.atrium.assertions.DefaultListAssertionGroupType import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.logic.changeSubject import ch.tutteli.atrium.logic.creating.charsequence.contains.CharSequenceContains.* @@ -40,8 +38,6 @@ class CharSequenceContainsAssertionCreator): AssertionContainer = container.changeSubject.unreported { it.toString() }.toAssertionContainer() @@ -49,9 +45,4 @@ class CharSequenceContainsAssertionCreator= 0 multiConsumableContainer.maybeSubject.fold({ -1 }) { searcher.search(it, searchCriterion) } - - override fun decorateAssertion( - container: AssertionContainer, - featureAssertion: Assertion - ): List = listOf(featureAssertion) } diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderEntriesAssertionCreator.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderEntriesAssertionCreator.kt index 4ff491ebd..be4eb47b5 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderEntriesAssertionCreator.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderEntriesAssertionCreator.kt @@ -2,9 +2,8 @@ package ch.tutteli.atrium.logic.creating.iterable.contains.creators.impl import ch.tutteli.atrium.assertions.Assertion import ch.tutteli.atrium.assertions.AssertionGroup -import ch.tutteli.atrium.assertions.DefaultListAssertionGroupType -import ch.tutteli.atrium.assertions.DefaultSummaryAssertionGroupType import ch.tutteli.atrium.assertions.builders.assertionBuilder +import ch.tutteli.atrium.assertions.builders.invisibleGroup import ch.tutteli.atrium.core.None import ch.tutteli.atrium.core.getOrElse import ch.tutteli.atrium.creating.AssertionContainer @@ -17,12 +16,15 @@ import ch.tutteli.atrium.logic.creating.iterable.contains.IterableLikeContains import ch.tutteli.atrium.logic.creating.iterable.contains.searchbehaviours.InAnyOrderSearchBehaviour import ch.tutteli.atrium.logic.creating.iterable.contains.searchbehaviours.NotSearchBehaviour import ch.tutteli.atrium.logic.creating.typeutils.IterableLike +import ch.tutteli.atrium.logic.hasNext import ch.tutteli.atrium.logic.impl.allCreatedAssertionsHold import ch.tutteli.atrium.logic.impl.createExplanatoryAssertionGroup -import ch.tutteli.atrium.logic.impl.createHasElementAssertion +import ch.tutteli.atrium.logic.impl.createExplanatoryGroupForMismatches +import ch.tutteli.atrium.logic.impl.createIndexAssertions import ch.tutteli.atrium.reporting.translating.Translatable import ch.tutteli.atrium.translations.DescriptionIterableAssertion import ch.tutteli.atrium.translations.DescriptionIterableAssertion.AN_ELEMENT_WHICH +import ch.tutteli.kbox.identity /** * Represents a creator of a sophisticated `contains` assertions for [Iterable] where an expected entry can appear @@ -55,6 +57,26 @@ class InAnyOrderEntriesAssertionCreator( override fun makeSubjectMultipleTimesConsumable(container: AssertionContainer): AssertionContainer> = turnSubjectToList(container, converter) + override fun decorateInAnyOrderAssertion( + inAnyOrderAssertion: AssertionGroup, + multiConsumableContainer: AssertionContainer> + ): AssertionGroup { + val hasNext = multiConsumableContainer.hasNext(::identity) + return if (searchBehaviour is NotSearchBehaviour && !hasNext.holds()) { + assertionBuilder.invisibleGroup + .withAssertions( + hasNext, + assertionBuilder.explanatoryGroup + .withDefaultType + .withAssertion(inAnyOrderAssertion) + .build() + ) + .build() + } else { + inAnyOrderAssertion + } + } + override fun searchAndCreateAssertion( multiConsumableContainer: AssertionContainer>, searchCriterion: (Expect.() -> Unit)?, @@ -68,15 +90,18 @@ class InAnyOrderEntriesAssertionCreator( ) val featureAssertion = featureFactory(count, DescriptionIterableAssertion.NUMBER_OF_OCCURRENCES) - val assertions = mutableListOf(explanatoryGroup, featureAssertion) - val groupType = if (searchBehaviour is NotSearchBehaviour) { - assertions.add(createHasElementAssertion(list)) - addEmptyAssertionCreatorLambdaIfNecessary(multiConsumableContainer, assertions, searchCriterion, count) - DefaultSummaryAssertionGroupType + val assertions = mutableListOf(explanatoryGroup) + if (searchBehaviour is NotSearchBehaviour) { + createEmptyAssertionHintIfNecessary(multiConsumableContainer, searchCriterion, count) + ?.let { assertions.add(it) } + val mismatches = createIndexAssertions(list) { (_, element) -> + allCreatedAssertionsHold(multiConsumableContainer, element, searchCriterion) + } + if (mismatches.isNotEmpty()) assertions.add(createExplanatoryGroupForMismatches(mismatches)) } else { - DefaultListAssertionGroupType + assertions.add(featureAssertion) } - return assertionBuilder.customType(groupType) + return assertionBuilder.list .withDescriptionAndEmptyRepresentation(AN_ELEMENT_WHICH) .withAssertions(assertions) .build() @@ -97,21 +122,21 @@ class InAnyOrderEntriesAssertionCreator( // maybe it would be enough to collect @Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */) @UseExperimental(ExperimentalComponentFactoryContainer::class) - private fun addEmptyAssertionCreatorLambdaIfNecessary( + private fun createEmptyAssertionHintIfNecessary( container: AssertionContainer<*>, - assertions: MutableList, searchCriterion: (Expect.() -> Unit)?, count: Int - ) { + ): Assertion? { if (searchCriterion != null && count == 0) { val collectingExpect = CollectingExpect(None, container.components) // not using addAssertionsCreatedBy on purpose so that we don't append a failing assertion collectingExpect.searchCriterion() val collectedAssertions = collectingExpect.getAssertions() if (collectedAssertions.isEmpty()) { - // no assertion created in the lambda, so lets add the failing assertion containing the hint - assertions.add(container.collectBasedOnSubject(None, searchCriterion)) + // no assertion created in the lambda, so return the failing assertion containing the hint + return container.collectBasedOnSubject(None, searchCriterion) } } + return null } } diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderValuesAssertionCreator.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderValuesAssertionCreator.kt index dd15110d6..8699b68f4 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderValuesAssertionCreator.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/creating/iterable/contains/creators/impl/InAnyOrderValuesAssertionCreator.kt @@ -1,6 +1,9 @@ package ch.tutteli.atrium.logic.creating.iterable.contains.creators.impl -import ch.tutteli.atrium.assertions.* +import ch.tutteli.atrium.assertions.Assertion +import ch.tutteli.atrium.assertions.AssertionGroup +import ch.tutteli.atrium.assertions.builders.assertionBuilder +import ch.tutteli.atrium.assertions.builders.invisibleGroup import ch.tutteli.atrium.core.getOrElse import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.logic.creating.basic.contains.creators.impl.ContainsObjectsAssertionCreator @@ -8,9 +11,12 @@ import ch.tutteli.atrium.logic.creating.iterable.contains.IterableLikeContains import ch.tutteli.atrium.logic.creating.iterable.contains.searchbehaviours.InAnyOrderSearchBehaviour import ch.tutteli.atrium.logic.creating.iterable.contains.searchbehaviours.NotSearchBehaviour import ch.tutteli.atrium.logic.creating.typeutils.IterableLike -import ch.tutteli.atrium.logic.impl.createHasElementAssertion +import ch.tutteli.atrium.logic.hasNext +import ch.tutteli.atrium.logic.impl.createExplanatoryGroupForMismatches +import ch.tutteli.atrium.logic.impl.createIndexAssertions import ch.tutteli.atrium.reporting.translating.Translatable import ch.tutteli.atrium.translations.DescriptionIterableAssertion +import ch.tutteli.kbox.identity /** * Represents a creator of a sophisticated `contains` assertions for [Iterable] where an expected entry can appear @@ -38,31 +44,49 @@ class InAnyOrderValuesAssertionCreator( override val descriptionNumberOfOccurrences: Translatable = DescriptionIterableAssertion.NUMBER_OF_OCCURRENCES override val groupDescription: Translatable = DescriptionIterableAssertion.AN_ELEMENT_WHICH_EQUALS - override fun getAssertionGroupType(): AssertionGroupType { - return if (searchBehaviour is NotSearchBehaviour) { - DefaultSummaryAssertionGroupType - } else { - DefaultListAssertionGroupType - } - } - override fun makeSubjectMultipleTimesConsumable(container: AssertionContainer): AssertionContainer> = turnSubjectToList(container, converter) override fun search(multiConsumableContainer: AssertionContainer>, searchCriterion: SC): Int = multiConsumableContainer.maybeSubject.fold({ -1 }) { subject -> subject.filter { it == searchCriterion }.size } - override fun decorateAssertion( - container: AssertionContainer>, - featureAssertion: Assertion - ): List { + override fun searchAndCreateAssertion( + multiConsumableContainer: AssertionContainer>, + searchCriterion: SC, + featureFactory: (Int, Translatable) -> AssertionGroup + ): AssertionGroup { return if (searchBehaviour is NotSearchBehaviour) { - listOf( - featureAssertion, - createHasElementAssertion(container.maybeSubject.getOrElse { emptyList() }) - ) + val list = multiConsumableContainer.maybeSubject.getOrElse { emptyList() } + val mismatches = createIndexAssertions(list) { (_, element) -> element == searchCriterion } + val assertions = mutableListOf() + if (mismatches.isNotEmpty()) assertions.add(createExplanatoryGroupForMismatches(mismatches)) + assertionBuilder.list + .withDescriptionAndRepresentation(groupDescription, searchCriterion) + .withAssertions(assertions) + .build() } else { - listOf(featureAssertion) + super.searchAndCreateAssertion(multiConsumableContainer, searchCriterion, featureFactory) + } + } + + /** + * Override in a subclass if you want to decorate the assertion. + */ + override fun decorateInAnyOrderAssertion( + inAnyOrderAssertion: AssertionGroup, + multiConsumableContainer: AssertionContainer> + ): AssertionGroup { + val hasNext = multiConsumableContainer.hasNext(::identity) + return if (searchBehaviour is NotSearchBehaviour && !hasNext.holds()) { + assertionBuilder.invisibleGroup.withAssertions( + hasNext, + assertionBuilder.explanatoryGroup + .withDefaultType + .withAssertion(inAnyOrderAssertion) + .build() + ).build() + } else { + inAnyOrderAssertion } } } diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultIterableLikeAssertions.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultIterableLikeAssertions.kt index 8b04c74a4..6fadab555 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultIterableLikeAssertions.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/DefaultIterableLikeAssertions.kt @@ -3,7 +3,6 @@ package ch.tutteli.atrium.logic.impl import ch.tutteli.atrium.assertions.Assertion import ch.tutteli.atrium.assertions.builders.* import ch.tutteli.atrium.core.Option -import ch.tutteli.atrium.core.falseProvider import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.logic.IterableLikeAssertions @@ -27,7 +26,6 @@ import ch.tutteli.atrium.reporting.translating.TranslatableWithArgs import ch.tutteli.atrium.translations.DescriptionBasic import ch.tutteli.atrium.translations.DescriptionIterableAssertion import ch.tutteli.atrium.translations.DescriptionIterableAssertion.NEXT_ELEMENT -import ch.tutteli.kbox.WithIndex import ch.tutteli.kbox.mapWithIndex class DefaultIterableLikeAssertions : IterableLikeAssertions { @@ -101,17 +99,7 @@ class DefaultIterableLikeAssertions : IterableLikeAssertions { val mismatches = createIndexAssertions(list) { (_, element) -> !allCreatedAssertionsHold(container, element, assertionCreatorOrNull) } - assertions.add( - assertionBuilder.explanatoryGroup - .withWarningType - .withAssertion( - assertionBuilder.list - .withDescriptionAndEmptyRepresentation(DescriptionIterableAssertion.WARNING_MISMATCHES) - .withAssertions(mismatches) - .build() - ) - .build() - ) + assertions.add(createExplanatoryGroupForMismatches(mismatches)) createHasElementPlusFixedClaimGroup( list, @@ -134,22 +122,6 @@ class DefaultIterableLikeAssertions : IterableLikeAssertions { } } - private fun createIndexAssertions( - list: List, - predicate: (WithIndex) -> Boolean - ) = list - .asSequence() - .mapWithIndex() - .filter { predicate(it) } - .map { (index, element) -> - assertionBuilder.createDescriptive( - TranslatableWithArgs(DescriptionIterableAssertion.INDEX, index), - element, - falseProvider - ) - } - .toList() - private fun createHasElementPlusFixedClaimGroup( list: List, description: Translatable, diff --git a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/containsHelpers.kt b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/containsHelpers.kt index d7755e868..7b03327df 100644 --- a/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/containsHelpers.kt +++ b/logic/atrium-logic-common/src/main/kotlin/ch/tutteli/atrium/logic/impl/containsHelpers.kt @@ -5,14 +5,18 @@ import ch.tutteli.atrium.assertions.AssertionGroup import ch.tutteli.atrium.assertions.builders.assertionBuilder import ch.tutteli.atrium.core.None import ch.tutteli.atrium.core.Some +import ch.tutteli.atrium.core.falseProvider import ch.tutteli.atrium.core.trueProvider import ch.tutteli.atrium.creating.AssertionContainer import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.logic.collectBasedOnSubject import ch.tutteli.atrium.logic.creating.collectors.collectAssertions import ch.tutteli.atrium.reporting.Text +import ch.tutteli.atrium.reporting.translating.TranslatableWithArgs import ch.tutteli.atrium.translations.DescriptionBasic import ch.tutteli.atrium.translations.DescriptionIterableAssertion +import ch.tutteli.kbox.WithIndex +import ch.tutteli.kbox.mapWithIndex internal fun createHasElementAssertion(list: List<*>): Assertion { return assertionBuilder.feature @@ -53,3 +57,34 @@ internal fun createExplanatoryAssertionGroup( } .build() } + +internal fun createIndexAssertions( + list: List, + predicate: (WithIndex) -> Boolean +) = list + .asSequence() + .mapWithIndex() + .filter { predicate(it) } + .map { (index, element) -> + assertionBuilder.createDescriptive( + TranslatableWithArgs(DescriptionIterableAssertion.INDEX, index), + element, + falseProvider + ) + } + .toList() + +internal fun createExplanatoryGroupForMismatches( + mismatches: List +) : AssertionGroup { + return assertionBuilder.explanatoryGroup + .withWarningType + .withAssertion( + assertionBuilder.list + .withDescriptionAndEmptyRepresentation(DescriptionIterableAssertion.WARNING_MISMATCHES) + .withAssertions(mismatches) + .build() + ) + .failing + .build() +} diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainEntriesExpectationsSpec.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainEntriesExpectationsSpec.kt index bf94a635a..8e39bf7cd 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainEntriesExpectationsSpec.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainEntriesExpectationsSpec.kt @@ -45,7 +45,6 @@ abstract class IterableNotToContainEntriesExpectationsSpec( ) = notToContainNullableEntries(this, a, aX) val notToContainDescr = DescriptionIterableAssertion.CONTAINS_NOT.getDefault() - val hasElement = DescriptionIterableAssertion.HAS_ELEMENT.getDefault() nonNullableCases( describePrefix, @@ -67,13 +66,10 @@ abstract class IterableNotToContainEntriesExpectationsSpec( }.toThrow { message { toContainRegex( - "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + - "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + - "$afterExplanatory$toBeDescr: 4.0.*$separator" + - "$featureSuccess$numberOfOccurrences: 0$separator" + - "$isAfterSuccess: 0.*$separator" + - "$featureFailing$hasElement: false$separator" + - "$isAfterFailing: true" + "$hasANextElement$separator" + + "$indentRootBulletPoint\\Q$explanatoryBulletPoint\\E$notToContainDescr: $separator" + + "$indentRootBulletPoint$indentListBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + + "$indentListBulletPoint$afterExplanatory$toBeDescr: 4.0.*" ) } } @@ -104,10 +100,10 @@ abstract class IterableNotToContainEntriesExpectationsSpec( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$toBeLessThanDescr: 4.0.*$separator" + - "$featureFailing$numberOfOccurrences: 3$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(0, "1.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(1, "2.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(5, "3.0")}.*" ) } } @@ -121,16 +117,14 @@ abstract class IterableNotToContainEntriesExpectationsSpec( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$toBeDescr: 1.0.*$separator" + - "$featureFailing$numberOfOccurrences: 1$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true$separator" + + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(0, "1.0")}.*$separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$toBeDescr: 4.0.*$separator" + - "$featureFailing$numberOfOccurrences: 3$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(2, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(3, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(8, "4.0")}.*" ) } } @@ -167,10 +161,9 @@ abstract class IterableNotToContainEntriesExpectationsSpec( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$isDescr: null$separator" + - "$featureFailing$numberOfOccurrences: 2$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(1, "null")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(5, "null")}.*" ) } } @@ -185,10 +178,9 @@ abstract class IterableNotToContainEntriesExpectationsSpec( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$isDescr: null$separator" + - "$featureFailing$numberOfOccurrences: 2$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${index(1)}: null.*$separator" + + "$afterMismatchedWarning${index(5)}: null.*" ) this.notToContain("$notToContainDescr: 1.1") } diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainValuesExpectationsSpec.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainValuesExpectationsSpec.kt index 7b645d4cc..9f3f1cecd 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainValuesExpectationsSpec.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableNotToContainValuesExpectationsSpec.kt @@ -4,7 +4,6 @@ import ch.tutteli.atrium.api.fluent.en_GB.* import ch.tutteli.atrium.api.verbs.internal.expect import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.specs.* -import ch.tutteli.atrium.translations.DescriptionIterableAssertion abstract class IterableNotToContainValuesExpectationsSpec( notToContainValues: Fun2, Double, Array>, @@ -24,9 +23,6 @@ abstract class IterableNotToContainValuesExpectationsSpec( fun Expect>.notToContainNullableFun(a: Double?, vararg aX: Double?) = notToContainNullableValues(this, a, aX) - val notToContainDescr = DescriptionIterableAssertion.CONTAINS_NOT.getDefault() - val hasElement = DescriptionIterableAssertion.HAS_ELEMENT.getDefault() - val anElementWhichIsWithIndent = "$indentRootBulletPoint$listBulletPoint$anElementWhichIs" nonNullableCases( @@ -46,12 +42,9 @@ abstract class IterableNotToContainValuesExpectationsSpec( }.toThrow { message { toContainRegex( - "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + - "$anElementWhichIsWithIndent: 4.0.*$separator" + - "$featureSuccess$numberOfOccurrences: 0$separator" + - "$isAfterSuccess: 0.*$separator" + - "$featureFailing$hasElement: false$separator" + - "$isAfterFailing: true" + "$hasANextElement$separator" + + "$indentRootBulletPoint\\Q$explanatoryBulletPoint\\E$notToContainDescr: $separator" + + "$indentListBulletPoint$anElementWhichIsWithIndent: 4.0.*" ) } } @@ -81,10 +74,10 @@ abstract class IterableNotToContainValuesExpectationsSpec( toContainRegex( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$anElementWhichIsWithIndent: 4.0.*$separator" + - "$featureFailing$numberOfOccurrences: 3$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(2, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(3, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(8, "4.0")}.*" ) } } @@ -97,15 +90,13 @@ abstract class IterableNotToContainValuesExpectationsSpec( toContainRegex( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$anElementWhichIsWithIndent: 1.0.*$separator" + - "$featureFailing$numberOfOccurrences: 1$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true$separator" + + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(0, "1.0")}.*$separator" + "$anElementWhichIsWithIndent: 4.0.*$separator" + - "$featureFailing$numberOfOccurrences: 3$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(2, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(3, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(8, "4.0")}.*" ) } } @@ -118,15 +109,13 @@ abstract class IterableNotToContainValuesExpectationsSpec( toContainRegex( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$anElementWhichIsWithIndent: 4.0.*$separator" + - "$featureFailing$numberOfOccurrences: 3$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true$separator" + + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(2, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(3, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(8, "4.0")}.*$separator" + "$anElementWhichIsWithIndent: 1.0.*$separator" + - "$featureFailing$numberOfOccurrences: 1$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(0, "1.0")}.*" ) } } @@ -152,10 +141,9 @@ abstract class IterableNotToContainValuesExpectationsSpec( toContainRegex( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$anElementWhichIsWithIndent: null$separator" + - "$featureFailing$numberOfOccurrences: 2$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(1, "null")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(5, "null")}.*" ) } } @@ -169,10 +157,9 @@ abstract class IterableNotToContainValuesExpectationsSpec( toContainRegex( "\\Q$rootBulletPoint\\E$notToContainDescr: $separator" + "$anElementWhichIsWithIndent: null$separator" + - "$featureFailing$numberOfOccurrences: 2$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(1, "null")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(5, "null")}.*" ) notToContain("$notToContainDescr: 1.1") } diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainEntriesSpecBase.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainEntriesSpecBase.kt index ac774d242..caea66122 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainEntriesSpecBase.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainEntriesSpecBase.kt @@ -23,14 +23,18 @@ abstract class IterableToContainEntriesSpecBase( val anElementWhich = DescriptionIterableAssertion.AN_ELEMENT_WHICH.getDefault() val toBeLessThanDescr = DescriptionComparableAssertion.IS_LESS_THAN.getDefault() val toBeGreaterThanDescr = DescriptionComparableAssertion.IS_GREATER_THAN.getDefault() + fun mismatchedIndex(index: Int, value: T) : String { + val indexDescr = String.format(DescriptionIterableAssertion.INDEX.getDefault(), index) + return "$indexDescr: ${value.toString()}" + } + fun index(index: Int) = String.format(DescriptionIterableAssertion.INDEX.getDefault(), index) //@formatter:off - val featureSuccess = "$indentRootBulletPoint$indentListBulletPoint\\Q$successfulBulletPoint$featureArrow\\E" - val featureFailing = "$indentRootBulletPoint$indentListBulletPoint\\Q$failingBulletPoint$featureArrow\\E" - val isAfterFailing = "$indentRootBulletPoint$indentListBulletPoint$indentFailingBulletPoint$indentFeatureArrow\\Q$featureBulletPoint\\E$isDescr" - val isAfterSuccess = "$indentRootBulletPoint$indentListBulletPoint$indentSuccessfulBulletPoint$indentFeatureArrow\\Q$featureBulletPoint\\E$isDescr" val afterExplanatory = "$indentRootBulletPoint$indentListBulletPoint$indentSuccessfulBulletPoint\\Q$explanatoryBulletPoint\\E" + val afterExplanatoryIndent = "$indentRootBulletPoint$indentListBulletPoint$indentSuccessfulBulletPoint" + val afterMismatchedWarning = "$afterExplanatoryIndent$indentWarningBulletPoint\\Q$listBulletPoint\\E" + val hasANextElement = "\\Q$rootBulletPoint\\E$hasDescriptionBasic: $nextElement" //@formatter:on } diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainSpecBase.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainSpecBase.kt index 53a8b11d8..3b2c9a8c6 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainSpecBase.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToContainSpecBase.kt @@ -6,6 +6,7 @@ import ch.tutteli.atrium.api.fluent.en_GB.toContain import ch.tutteli.atrium.core.polyfills.format import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.specs.* +import ch.tutteli.atrium.translations.DescriptionBasic import ch.tutteli.atrium.translations.DescriptionCollectionAssertion import ch.tutteli.atrium.translations.DescriptionIterableAssertion import org.spekframework.spek2.Spek @@ -43,6 +44,9 @@ abstract class IterableToContainSpecBase(spec: Root.() -> Unit) : Spek(spec) { val mismatchesAdditionalElements = DescriptionIterableAssertion.WARNING_MISMATCHES_ADDITIONAL_ELEMENTS.getDefault() val sizeExceeded = DescriptionIterableAssertion.SIZE_EXCEEDED.getDefault() val anElementWhichIs = DescriptionIterableAssertion.AN_ELEMENT_WHICH_EQUALS.getDefault() + val hasDescriptionBasic = DescriptionBasic.HAS.getDefault() + val nextElement = DescriptionIterableAssertion.NEXT_ELEMENT.getDefault() + val notToContainDescr = DescriptionIterableAssertion.CONTAINS_NOT.getDefault() val sizeDescr = DescriptionCollectionAssertion.SIZE.getDefault() val atLeastDescr = DescriptionIterableAssertion.AT_LEAST.getDefault() diff --git a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToHaveElementsAndNoneExpectationsSpec.kt b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToHaveElementsAndNoneExpectationsSpec.kt index 546eb6cc5..4a17365d9 100644 --- a/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToHaveElementsAndNoneExpectationsSpec.kt +++ b/misc/specs/atrium-specs-common/src/main/kotlin/ch/tutteli/atrium/specs/integration/IterableToHaveElementsAndNoneExpectationsSpec.kt @@ -29,7 +29,6 @@ abstract class IterableToHaveElementsAndNoneExpectationsSpec( ) {}) val containsNotDescr = DescriptionIterableAssertion.CONTAINS_NOT.getDefault() - val hasElement = DescriptionIterableAssertion.HAS_ELEMENT.getDefault() nonNullableCases( describePrefix, @@ -42,7 +41,14 @@ abstract class IterableToHaveElementsAndNoneExpectationsSpec( expect { expect(fluentEmpty()).toHaveElementsAndNoneFun { toBeLessThan(1.0) } }.toThrow { - messageToContain("$featureArrow$hasElement: false") + message { + toContainRegex( + "$hasANextElement$separator" + + "$indentRootBulletPoint\\Q$explanatoryBulletPoint\\E$containsNotDescr: $separator" + + "$indentRootBulletPoint$indentListBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + + "$indentListBulletPoint$afterExplanatory$toBeLessThanDescr: 1.0.*" + ) + } } } } @@ -66,10 +72,10 @@ abstract class IterableToHaveElementsAndNoneExpectationsSpec( "\\Q$rootBulletPoint\\E$containsNotDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$toBeDescr: 4.0.*$separator" + - "$featureFailing$numberOfOccurrences: 3$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(2, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(3, "4.0")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(8, "4.0")}.*" ) } } @@ -96,10 +102,9 @@ abstract class IterableToHaveElementsAndNoneExpectationsSpec( "\\Q$rootBulletPoint\\E$containsNotDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$isDescr: null$separator" + - "$featureFailing$numberOfOccurrences: 2$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(1, "null")}.*$separator" + + "$afterMismatchedWarning${mismatchedIndex(5, "null")}.*" ) } } @@ -114,10 +119,8 @@ abstract class IterableToHaveElementsAndNoneExpectationsSpec( "\\Q$rootBulletPoint\\E$containsNotDescr: $separator" + "$indentRootBulletPoint\\Q$listBulletPoint\\E$anElementWhich: $separator" + "$afterExplanatory$toBeDescr: 1.0.*$separator" + - "$featureFailing$numberOfOccurrences: 1$separator" + - "$isAfterFailing: 0.*$separator" + - "$featureSuccess$hasElement: true$separator" + - "$isAfterSuccess: true" + "$afterExplanatoryIndent\\Q$warningBulletPoint$mismatches:\\E $separator" + + "$afterMismatchedWarning${mismatchedIndex(0, "1.0")}.*" ) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 846832e20..4033075a6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,7 +32,10 @@ buildscript { "BigDecimalAssertionsSpec.*overload throws PleaseUseReplacementException", // we renamed containsNot to notToContain with 0.17.0 "CharSequenceContains.*Spec.*points to containsNot", - "IterableContains.*Spec.*points to containsNot" + "IterableContains.*Spec.*points to containsNot", + // we improved reporting for notToContain with 0.17.0 + "IterableContainsNot(Entries|Values)AssertionsSpec.*`containsNot( nullable)?`.*throws AssertionError", + "IterableNoneAssertionsSpec.*`(none|containsNot)( nullable)?`.*throws AssertionError" ) + ".*)", // we don't use asci bullet points in reporting since 0.17.0 // but have own tests to assure that changing bullet points work @@ -92,7 +95,10 @@ buildscript { "BigDecimalAssertionsSpec.*overload throws PleaseUseReplacementException.*", // we renamed containsNot to notToContain with 0.17.0 "CharSequenceContains.*Spec.*points to containsNot", - "IterableContains.*Spec.*points to containsNot" + "IterableContains.*Spec.*points to containsNot", + // we improved reporting for notToContain with 0.17.0 + "IterableContainsNot(Entries|Values)AssertionsSpec.*`containsNot.*`.*throws AssertionError", + "IterableNoneAssertionsSpec.*`(none|containsNot).*`.*throws AssertionError" ) + ".*)", // we don't use asci bullet points in reporting since 0.17.0 // but have own tests to assure that changing bullet points work @@ -153,7 +159,10 @@ buildscript { "CharSequenceContains.*Spec.*points to containsNot", "IterableContains.*Spec.*points to containsNot", // we improved reporting for containsNoDuplicates - "IterableExpectationsSpec.*`(containsNoDuplicates|contains noDuplicates)`" + "IterableExpectationsSpec.*`(containsNoDuplicates|contains noDuplicates)`", + // we improved reporting for notToContain with 0.17.0 + "IterableContainsNot(Entries|Values)ExpectationsSpec.*`containsNot.*`.*throws AssertionError", + "IterableNoneExpectationsSpec.*`(none|containsNot).*`.*throws AssertionError" ) + ".*)").let { commonPatterns -> Pair( // bc