Skip to content

Commit

Permalink
Fix contain exactly verifier draft (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexCue987 committed Apr 19, 2024
1 parent b612160 commit ccaa723
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ fun <T, C : Collection<T>> containExactly(
fun Throwable?.isDisallowedIterableComparisonFailure() =
this?.message?.startsWith(IterableEq.trigger) == true

val failureReason = eq(actual, expected, strictNumberEq = true)
val failureReason = if(verifier == null) {
eq(actual, expected, strictNumberEq = true)
} else {
matchCollectionsWithVerifier(actual, expected, verifier)
}

val missing = expected.filterNot { t ->
actual.any { verifier?.verify(it, t)?.areEqual() ?: (it == t) }
Expand All @@ -91,6 +95,11 @@ fun <T, C : Collection<T>> containExactly(
appendLine()
}

if (failureReason is CollectionMismatchWithCustomVerifier) {
append(failureReason.message)
appendLine()
}

appendMissingAndExtra(missing, extra)
appendLine()
}
Expand Down Expand Up @@ -129,6 +138,37 @@ fun <T, C : Collection<T>> containExactly(
}
}

internal fun<T> matchCollectionsWithVerifier(
actual: Collection<T>,
expected: Collection<T>,
verifier: Equality<T>
): CollectionMismatchWithCustomVerifier? {
val actualIterator = actual.iterator()
val expectedIterator = expected.iterator()
var index = 0
while (actualIterator.hasNext()) {
val actualElement = actualIterator.next()
if (expectedIterator.hasNext()) {
val expectedElement = expectedIterator.next()
val equalityResult = verifier.verify(actualElement, expectedElement)
if(!equalityResult.areEqual()) {
return CollectionMismatchWithCustomVerifier(
"Elements differ at index $index, expected: <${expectedElement.print().value}>, but was <${actualElement.print().value}>, ${equalityResult.details().explain()}"
)
}
} else {
return CollectionMismatchWithCustomVerifier("Actual has an element at index $index, expected is shorter")
}
index++
}
if (expectedIterator.hasNext()) {
return CollectionMismatchWithCustomVerifier("Expected has an element at index $index, actual is shorter")
}
return null
}

internal class CollectionMismatchWithCustomVerifier(message: String): Exception(message)

@JvmName("shouldNotContainExactly_iterable")
infix fun <T> Iterable<T>?.shouldNotContainExactly(expected: Iterable<T>) =
this?.toList() shouldNot containExactly(expected.toList())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.sksamuel.kotest.matchers.collections
import io.kotest.assertions.shouldFailWithMessage
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.WordSpec
import io.kotest.equals.Equality
import io.kotest.equals.EqualityResult
import io.kotest.matchers.collections.CountMismatch
import io.kotest.matchers.collections.containExactly
import io.kotest.matchers.collections.containExactlyInAnyOrder
Expand Down Expand Up @@ -30,6 +32,16 @@ import kotlin.time.Duration.Companion.seconds


class ShouldContainExactlyTest : WordSpec() {
private val caseInsensitiveStringEquality: Equality<String> = object : Equality<String> {
override fun name() = "Case Insensitive String Matcher"

override fun verify(actual: String, expected: String): EqualityResult {
return if (actual.uppercase() == expected.uppercase())
EqualityResult.equal(actual, expected, this)
else
EqualityResult.notEqual(actual, expected, this)
}
}

init {

Expand Down Expand Up @@ -279,6 +291,19 @@ class ShouldContainExactlyTest : WordSpec() {
""".trimMargin()
}

"pass with custom verifier" {
listOf("Apple", "ORANGE", "apple") should containExactly(
listOf("Apple", "orange", "APPLE"),
caseInsensitiveStringEquality
)
}

"fail with custom verifier" {
listOf("Apple", "ORANGE", "orange") shouldNot containExactly(
listOf("Apple", "orange", "APPLE"),
caseInsensitiveStringEquality
)
}
}

"containExactlyInAnyOrder" should {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.kotest.matchers.collections

import io.kotest.core.spec.style.StringSpec
import io.kotest.equals.Equality
import io.kotest.equals.EqualityResult
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldStartWith

class MatchCollectionsWithVerifierTest: StringSpec() {
private val caseInsensitiveStringEquality: Equality<String> = object : Equality<String> {
override fun name() = "Case Insensitive String Matcher"

override fun verify(actual: String, expected: String): EqualityResult {
return if (actual.uppercase() == expected.uppercase())
EqualityResult.equal(actual, expected, this)
else
EqualityResult.notEqual(actual, expected, this)
}
}

init {
"match" {
matchCollectionsWithVerifier(
listOf("Apple", "ORANGE", "apple"),
listOf("Apple", "orange", "APPLE"),
caseInsensitiveStringEquality
) shouldBe null
}
"actual is too long" {
matchCollectionsWithVerifier(
listOf("Apple", "ORANGE", "apple"),
listOf("Apple", "orange"),
caseInsensitiveStringEquality
) shouldBe CollectionMismatchWithCustomVerifier("Actual has an element at index 2, expected is shorter")
}
"expected is too long" {
matchCollectionsWithVerifier(
listOf("Apple", "ORANGE"),
listOf("Apple", "orange", "apple"),
caseInsensitiveStringEquality
) shouldBe CollectionMismatchWithCustomVerifier("Expected has an element at index 2, actual is shorter")
}
"elements mismatch at index 2" {
matchCollectionsWithVerifier(
listOf("Apple", "ORANGE", "orange"),
listOf("Apple", "orange", "apple"),
caseInsensitiveStringEquality
)!!.message shouldStartWith "Elements differ at index 2, expected: <\"apple\">, but was <\"orange\">, \"apple\" is not equal to \"orange\" by Case Insensitive String Matcher"
}
}
}

0 comments on commit ccaa723

Please sign in to comment.