Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simplified overloads to shouldMatchEach extensions (#3911) #3929

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -164,12 +164,23 @@ fun <T> matchInOrder(assertions: List<(T) -> Unit>, allowGaps: Boolean): Matcher
}
}

/**
* Asserts that each element of the collection matches with the element of [expected].
* Elements will be compared sequentially by passing the actual / expected pairs to the
* [asserter] in the order given by the iterators of the collections.
*/
fun <T> matchEach(expected: List<T>, asserter: (T, T) -> Unit): Matcher<Collection<T>?> =
matchEach(expected.map { expectedElement ->
{ actualElement: T ->
asserter(actualElement, expectedElement)
}
})

/**
* Asserts that each element in the collection matches its corresponding matcher in [assertions].
* Elements will be compared sequentially in the order given by the iterators of the collections.
*/
fun <T> matchEach(assertions: List<(T) -> Unit>): Matcher<Collection<T>?> = neverNullMatcher { actual ->
data class ElementPass(val atIndex: Int)
data class MatchEachProblem(val atIndex: Int, val problem: String?)

val problems = errorCollector.runWithMode(ErrorCollectionMode.Hard) {
Expand All @@ -187,7 +198,7 @@ fun <T> matchEach(assertions: List<(T) -> Unit>): Matcher<Collection<T>?> = neve
}
}
}
} + (actual.size..assertions.size - 1).map {
} + (actual.size until assertions.size).map {
MatchEachProblem(
it,
"No actual element for assertion at index $it"
Expand All @@ -198,7 +209,7 @@ fun <T> matchEach(assertions: List<(T) -> Unit>): Matcher<Collection<T>?> = neve
problems.isEmpty(),
{
"Expected each element to pass its assertion, but found issues at indexes: [${problems.joinToString { it.atIndex.toString() }}]\n\n" +
"${problems.joinToString(separator = "\n") { "${it.atIndex} => ${it.problem}" }}"
problems.joinToString(separator = "\n") { "${it.atIndex} => ${it.problem}" }
},
{ "Expected some element to fail its assertion, but all passed." },
)
Expand Down
Expand Up @@ -64,8 +64,10 @@ fun <T> Iterable<T>.shouldMatchEach(vararg assertions: (T) -> Unit) = toList().s
fun <T> Array<T>.shouldMatchEach(vararg assertions: (T) -> Unit) = asList().shouldMatchEach(assertions.toList())
fun <T> List<T>.shouldMatchEach(vararg assertions: (T) -> Unit) = this.shouldMatchEach(assertions.toList())
infix fun <T> Iterable<T>.shouldMatchEach(assertions: List<(T) -> Unit>) = toList().shouldMatchEach(assertions)
fun <T> Iterable<T>.shouldMatchEach(expected: Iterable<T>, asserter: (T, T) -> Unit) = toList().shouldMatchEach(expected.toList(), asserter)
infix fun <T> Array<T>.shouldMatchEach(assertions: List<(T) -> Unit>) = asList().shouldMatchEach(assertions)
infix fun <T> List<T>.shouldMatchEach(assertions: List<(T) -> Unit>) = this should matchEach(assertions.toList())
fun <T> List<T>.shouldMatchEach(expected: List<T>, asserter: (T, T) -> Unit) = this should matchEach(expected, asserter)
fun <T> Iterable<T>.shouldNotMatchEach(vararg assertions: (T) -> Unit) = toList().shouldNotMatchEach(assertions.toList())
fun <T> Array<T>.shouldNotMatchEach(vararg assertions: (T) -> Unit) = asList().shouldNotMatchEach(assertions.toList())
fun <T> List<T>.shouldNotMatchEach(vararg assertions: (T) -> Unit) = this.shouldNotMatchEach(assertions.toList())
Expand Down
Expand Up @@ -4,8 +4,14 @@ package io.kotest.matchers.sequences

import io.kotest.assertions.eq.eq
import io.kotest.assertions.print.print
import io.kotest.matchers.*
import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.collections.duplicates
import io.kotest.matchers.collections.shouldMatchEach
import io.kotest.matchers.neverNullMatcher
import io.kotest.matchers.should
import io.kotest.matchers.shouldHave
import io.kotest.matchers.shouldNot

/*
How should infinite sequences be detected, and how should they be dealt with?
Expand Down Expand Up @@ -489,3 +495,7 @@ fun <T> containAll(ts: List<T>): Matcher<Sequence<T>> = object : Matcher<Sequenc
return MatcherResult(remaining.isEmpty(), failure, negFailure)
}
}

fun <T> Sequence<T>.shouldMatchEach(vararg assertions: (T) -> Unit) = toList().shouldMatchEach(assertions.toList())
infix fun <T> Sequence<T>.shouldMatchEach(assertions: List<(T) -> Unit>) = toList().shouldMatchEach(assertions)
fun <T> Sequence<T>.shouldMatchEach(expected: Sequence<T>, asserter: (T, T) -> Unit) = toList().shouldMatchEach(expected.toList(), asserter)
Expand Up @@ -861,6 +861,53 @@ class CollectionMatchersTest : WordSpec() {
}
}

"matchEach with actual / expected pairs" should {
"create proper matchers for collections of the same size" {
shouldThrow<AssertionError> {
listOf(4, 3, 2, 1) should matchEach(listOf(1, 2, 3, 4)) { actual, expected ->
actual shouldBe expected
}
}.message shouldBe """
Expected each element to pass its assertion, but found issues at indexes: [0, 1, 2, 3]

0 => expected:<1> but was:<4>
1 => expected:<2> but was:<3>
2 => expected:<3> but was:<2>
3 => expected:<4> but was:<1>
""".trimIndent()
}

"element missing on expected list" {
shouldThrow<AssertionError> {
listOf(4, 3, 2, 1) should matchEach(listOf(1, 2, 3)) { actual, expected ->
actual shouldBe expected
}
}.message shouldBe """
Expected each element to pass its assertion, but found issues at indexes: [0, 1, 2, 3]

0 => expected:<1> but was:<4>
1 => expected:<2> but was:<3>
2 => expected:<3> but was:<2>
3 => Element has no corresponding assertion. Only 3 assertions provided
""".trimIndent()
}

"element missing on actual list" {
shouldThrow<AssertionError> {
listOf(4, 3, 2) should matchEach(listOf(1, 2, 3, 4)) { actual, expected ->
actual shouldBe expected
}
}.message shouldBe """
Expected each element to pass its assertion, but found issues at indexes: [0, 1, 2, 3]

0 => expected:<1> but was:<4>
1 => expected:<2> but was:<3>
2 => expected:<3> but was:<2>
3 => No actual element for assertion at index 3
""".trimIndent()
}
}

"existInOrder" should {
"test that a collection matches the predicates in the given order, duplicates permitted" {
val col = listOf(1, 1, 2, 2, 3, 3)
Expand Down