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

more detailed failure for startWith and endWith #3886

Merged
merged 4 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,54 @@ infix fun <T> List<T>.shouldNotStartWith(slice: Collection<T>) = this shouldNot

fun <T> startWith(expectedSlice: Collection<T>) = object : Matcher<List<T>> {
override fun test(value: List<T>): MatcherResult {
val valueSlice = value.take(expectedSlice.size)
val comparison = SliceComparison.of(expectedSlice.toList(), value, SliceComparison.Companion.SliceType.START)

return MatcherResult(
valueSlice == expectedSlice,
{ "List should start with ${expectedSlice.print().value} but was ${valueSlice.print().value}" },
comparison.match,
{ "List should start with ${expectedSlice.print().value} but was ${comparison.valueSlice.print().value}\n${comparison.mismatchDescription}" },
{ "List should not start with ${expectedSlice.print().value}" }
)
}
}

private data class SliceComparison<T>(
val match: Boolean,
val mismatchDescription: String,
val valueSlice: List<T>
) {
companion object {
fun <T> of(expectedSlice: List<T>, value: List<T>, sliceType: SliceType): SliceComparison<T> {
val valueSlice = when (sliceType) {
SliceType.START -> value.take(expectedSlice.size)
SliceType.END -> value.takeLast(expectedSlice.size)
}
val indexOffset = when (sliceType) {
SliceType.START -> 0
SliceType.END -> value.size - expectedSlice.size
}
return when {
(valueSlice == expectedSlice) -> SliceComparison(true, "", valueSlice)
(valueSlice.size != expectedSlice.size) -> SliceComparison(
false,
"Actual collection is shorter than expected slice",
valueSlice
)
else -> SliceComparison(
false,
"Mismatched elements: " +
valueSlice.mapIndexedNotNull { index: Int, t: T ->
if (t != expectedSlice[index]) "value[${index + indexOffset}] != ${expectedSlice[index].print().value}"
else null
}.joinToString(", "),
valueSlice
)
}
}

internal enum class SliceType { START, END }
}
}

infix fun <T> Iterable<T>.shouldEndWith(element: T) = toList().shouldEndWith(listOf(element))
infix fun <T> Iterable<T>.shouldEndWith(slice: Iterable<T>) = toList().shouldEndWith(slice.toList())
infix fun <T> Iterable<T>.shouldEndWith(slice: Array<T>) = toList().shouldEndWith(slice.asList())
Expand All @@ -65,12 +103,13 @@ infix fun <T> List<T>.shouldNotEndWith(slice: Collection<T>) = this shouldNot en

fun <T> endWith(expectedSlice: Collection<T>) = object : Matcher<List<T>> {
override fun test(value: List<T>): MatcherResult {
val valueSlice = value.takeLast(expectedSlice.size)
val comparison = SliceComparison.of(expectedSlice.toList(), value, SliceComparison.Companion.SliceType.END)

return MatcherResult(
valueSlice == expectedSlice,
{ "List should end with ${expectedSlice.print().value} but was ${valueSlice.print().value}" },
comparison.match,
{ "List should end with ${expectedSlice.print().value} but was ${comparison.valueSlice.print().value}\n${comparison.mismatchDescription}" },
{ "List should not end with ${expectedSlice.print().value}" }
)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,18 @@ class StartWithEndWithTest : WordSpec() {
"print errors unambiguously" {
shouldThrow<AssertionError> {
listOf(1L, 2L) should startWith(listOf(1L, 3L))
}.shouldHaveMessage("List should start with [1L, 3L] but was [1L, 2L]")
}.shouldHaveMessage("""
|List should start with [1L, 3L] but was [1L, 2L]
|Mismatched elements: value[1] != 3L
""".trimMargin())
}
"print errors unambiguously when the actual value is empty" {
shouldThrow<AssertionError> {
emptyList<Long>() should startWith(listOf(1L, 3L))
}.shouldHaveMessage("List should start with [1L, 3L] but was []")
}.shouldHaveMessage("""
|List should start with [1L, 3L] but was []
|Actual collection is shorter than expected slice
""".trimMargin())
}
}

Expand All @@ -46,12 +52,18 @@ class StartWithEndWithTest : WordSpec() {
"print errors unambiguously" {
shouldThrow<AssertionError> {
listOf(1L, 2L, 3L, 4L) should endWith(listOf(1L, 3L))
}.shouldHaveMessage("List should end with [1L, 3L] but was [3L, 4L]")
}.shouldHaveMessage("""
|List should end with [1L, 3L] but was [3L, 4L]
|Mismatched elements: value[2] != 1L, value[3] != 3L
""".trimMargin())
AlexCue987 marked this conversation as resolved.
Show resolved Hide resolved
}
"print errors unambiguously when the actual value is empty" {
shouldThrow<AssertionError> {
emptyList<Long>() should endWith(listOf(1L, 3L))
}.shouldHaveMessage("List should end with [1L, 3L] but was []")
}.shouldHaveMessage("""
|List should end with [1L, 3L] but was []
|Actual collection is shorter than expected slice
""".trimMargin())
}
}
}
Expand Down