
commonMain.io.kotest.matchers.collections.CollectionMatchers.kt Maven / Gradle / Ivy
package io.kotest.matchers.collections
import io.kotest.assertions.show.show
import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.neverNullMatcher
fun haveSizeMatcher(size: Int) = object : Matcher> {
override fun test(value: Collection) =
MatcherResult(
value.size == size,
{ "Collection should have size $size but has size ${value.size}. Values: ${value.show().value}" },
{ "Collection should not have size $size. Values: ${value.show().value}" }
)
}
fun beEmpty(): Matcher> = object : Matcher> {
override fun test(value: Collection): MatcherResult = MatcherResult(
value.isEmpty(),
{ "Collection should be empty but contained ${value.show().value}" },
{ "Collection should not be empty" }
)
}
fun existInOrder(vararg ps: (T) -> Boolean): Matcher?> = existInOrder(ps.asList())
/**
* Assert that a collections contains a subsequence that matches the given subsequence of predicates, possibly with
* values in between.
*/
fun existInOrder(predicates: List<(T) -> Boolean>): Matcher?> = neverNullMatcher { actual ->
require(predicates.isNotEmpty()) { "predicates must not be empty" }
var subsequenceIndex = 0
val actualIterator = actual.iterator()
while (actualIterator.hasNext() && subsequenceIndex < predicates.size) {
if (predicates[subsequenceIndex](actualIterator.next())) subsequenceIndex += 1
}
MatcherResult(
subsequenceIndex == predicates.size,
{ "${actual.show().value} did not match the predicates ${predicates.show().value} in order" },
{ "${actual.show().value} should not match the predicates ${predicates.show().value} in order" }
)
}
fun haveSize(size: Int): Matcher> = haveSizeMatcher(size)
fun singleElement(t: T): Matcher> = object : Matcher> {
override fun test(value: Collection) = MatcherResult(
value.size == 1 && value.first() == t,
{ "Collection should be a single element of $t but has ${value.size} elements: ${value.show().value}" },
{ "Collection should not be a single element of $t" }
)
}
fun singleElement(p: (T) -> Boolean): Matcher> = object : Matcher> {
override fun test(value: Collection): MatcherResult {
val filteredValue: List = value.filter(p)
return MatcherResult(
filteredValue.size == 1,
{ "Collection should have a single element by a given predicate but has ${filteredValue.size} elements: ${value.show().value}" },
{ "Collection should not have a single element by a given predicate" }
)
}
}
fun > beSorted(): Matcher> = sorted()
fun > sorted(): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
val failure = value.withIndex().firstOrNull { (i, it) -> i != value.lastIndex && it > value[i + 1] }
val elementMessage = when (failure) {
null -> ""
else -> ". Element ${failure.value} at index ${failure.index} was greater than element ${value[failure.index + 1]}"
}
return MatcherResult(
failure == null,
{ "List ${value.show().value} should be sorted$elementMessage" },
{ "List ${value.show().value} should not be sorted" }
)
}
}
fun > beMonotonicallyIncreasing(): Matcher> = monotonicallyIncreasing()
fun > monotonicallyIncreasing(): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testMonotonicallyIncreasingWith(value,
Comparator { a, b -> a.compareTo(b) })
}
}
fun beMonotonicallyIncreasingWith(comparator: Comparator): Matcher> =
monotonicallyIncreasingWith(comparator)
fun monotonicallyIncreasingWith(comparator: Comparator): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testMonotonicallyIncreasingWith(value, comparator)
}
}
private fun testMonotonicallyIncreasingWith(value: List, comparator: Comparator): MatcherResult {
val failure = value.zipWithNext().withIndex().find { (_, pair) -> comparator.compare(pair.first, pair.second) > 0 }
val snippet = value.show().value
val elementMessage = when (failure) {
null -> ""
else -> ". Element ${failure.value.second} at index ${failure.index + 1} was not monotonically increased from previous element."
}
return MatcherResult(
failure == null,
{ "List [$snippet] should be monotonically increasing$elementMessage" },
{ "List [$snippet] should not be monotonically increasing" }
)
}
fun > beMonotonicallyDecreasing(): Matcher> = monotonicallyDecreasing()
fun > monotonicallyDecreasing(): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testMonotonicallyDecreasingWith(value,
Comparator { a, b -> a.compareTo(b) })
}
}
fun beMonotonicallyDecreasingWith(comparator: Comparator): Matcher> = monotonicallyDecreasingWith(
comparator)
fun monotonicallyDecreasingWith(comparator: Comparator): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testMonotonicallyDecreasingWith(value, comparator)
}
}
private fun testMonotonicallyDecreasingWith(value: List, comparator: Comparator): MatcherResult {
val failure = value.zipWithNext().withIndex().find { (_, pair) -> comparator.compare(pair.first, pair.second) < 0 }
val snippet = value.show().value
val elementMessage = when (failure) {
null -> ""
else -> ". Element ${failure.value.second} at index ${failure.index + 1} was not monotonically decreased from previous element."
}
return MatcherResult(
failure == null,
{ "List [$snippet] should be monotonically decreasing$elementMessage" },
{ "List [$snippet] should not be monotonically decreasing" }
)
}
fun > beStrictlyIncreasing(): Matcher> = strictlyIncreasing()
fun > strictlyIncreasing(): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testStrictlyIncreasingWith(value, Comparator { a, b -> a.compareTo(b) })
}
}
fun beStrictlyIncreasingWith(comparator: Comparator): Matcher> = strictlyIncreasingWith(
comparator)
fun strictlyIncreasingWith(comparator: Comparator): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testStrictlyIncreasingWith(value, comparator)
}
}
private fun testStrictlyIncreasingWith(value: List, comparator: Comparator): MatcherResult {
val failure = value.zipWithNext().withIndex().find { (_, pair) -> comparator.compare(pair.first, pair.second) >= 0 }
val snippet = value.show().value
val elementMessage = when (failure) {
null -> ""
else -> ". Element ${failure.value.second} at index ${failure.index + 1} was not strictly increased from previous element."
}
return MatcherResult(
failure == null,
{ "List [$snippet] should be strictly increasing$elementMessage" },
{ "List [$snippet] should not be strictly increasing" }
)
}
fun > beStrictlyDecreasing(): Matcher> = strictlyDecreasing()
fun > strictlyDecreasing(): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testStrictlyDecreasingWith(value, Comparator { a, b -> a.compareTo(b) })
}
}
fun beStrictlyDecreasingWith(comparator: Comparator): Matcher> = strictlyDecreasingWith(
comparator)
fun strictlyDecreasingWith(comparator: Comparator): Matcher> = object : Matcher> {
override fun test(value: List): MatcherResult {
return testStrictlyDecreasingWith(value, comparator)
}
}
private fun testStrictlyDecreasingWith(value: List, comparator: Comparator): MatcherResult {
val failure = value.zipWithNext().withIndex().find { (_, pair) -> comparator.compare(pair.first, pair.second) <= 0 }
val snippet = value.show().value
val elementMessage = when (failure) {
null -> ""
else -> ". Element ${failure.value.second} at index ${failure.index + 1} was not strictly decreased from previous element."
}
return MatcherResult(
failure == null,
{ "List [$snippet] should be strictly decreasing$elementMessage" },
{ "List [$snippet] should not be strictly decreasing" }
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy