Skip to content

Commit

Permalink
more detailed failure for startWith and endWith (#3886)
Browse files Browse the repository at this point in the history
Co-authored-by: Emil Kantis <emil.kantis@protonmail.com>
  • Loading branch information
AlexCue987 and Kantis committed Feb 26, 2024
1 parent 3ac829a commit d8b5f82
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 10 deletions.
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,
"The following elements failed:\n" +
valueSlice.mapIndexedNotNull { index: Int, t: T ->
if (t != expectedSlice[index]) " [${index + indexOffset}] ${valueSlice[index].print().value} => expected: <${expectedSlice[index].print().value}>, but was: <${valueSlice[index].print().value}>"
else null
}.joinToString("\n"),
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,19 @@ 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]
|The following elements failed:
| [1] 2L => expected: <3L>, but was: <2L>
""".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 +53,20 @@ 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]
|The following elements failed:
| [2] 3L => expected: <1L>, but was: <3L>
| [3] 4L => expected: <3L>, but was: <4L>
""".trimMargin())
}
"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

0 comments on commit d8b5f82

Please sign in to comment.