All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.kotest.matchers.sequences.matchers.kt Maven / Gradle / Ivy

package io.kotest.matchers.sequences

import io.kotest.matchers.*

private fun  Sequence.toString(limit: Int = 10) = this.joinToString(", ", limit = limit)

/*
How should infinite sequences be detected, and how should they be dealt with?

Sequence.count() may run through the whole sequence (sequences from `generateSequence()` do so), so isn't always a safe way to detect infinite sequences.

For now, the documentation should mention that infinite sequences will cause these matchers never to halt.
*/

fun  Sequence.shouldContainOnlyNulls() = this should containOnlyNulls()
fun  Sequence.shouldNotContainOnlyNulls() = this shouldNot containOnlyNulls()
fun  containOnlyNulls() = object : Matcher> {
   override fun test(value: Sequence) =
      MatcherResult(
         value.all { it == null },
         "Sequence should contain only nulls",
         "Sequence should not contain only nulls"
      )
}

fun  Sequence.shouldContainNull() = this should containNull()
fun  Sequence.shouldNotContainNull() = this shouldNot containNull()
fun  containNull() = object : Matcher> {
   override fun test(value: Sequence) =
      MatcherResult(
         value.any { it == null },
         "Sequence should contain at least one null",
         "Sequence should not contain any nulls"
      )
}

fun  Sequence.shouldHaveElementAt(index: Int, element: T) = this should haveElementAt(index, element)
fun  Sequence.shouldNotHaveElementAt(index: Int, element: T) = this shouldNot haveElementAt(index, element)

fun > haveElementAt(index: Int, element: T) = object : Matcher {
   override fun test(value: S) =
      MatcherResult(
         value.elementAt(index) == element,
         { "Sequence should contain $element at index $index" },
         { "Sequence should not contain $element at index $index" }
      )
}

fun  Sequence.shouldContainNoNulls() = this should containNoNulls()
fun  Sequence.shouldNotContainNoNulls() = this shouldNot containNoNulls()
fun  containNoNulls() = object : Matcher> {
   override fun test(value: Sequence) =
      MatcherResult(
         value.all { it != null },
         { "Sequence should not contain nulls" },
         { "Sequence should have at least one null" }
      )
}

infix fun > C.shouldContain(t: T) = this should contain(t)
infix fun > C.shouldNotContain(t: T) = this shouldNot contain(t)
fun > contain(t: T) = object : Matcher {
   override fun test(value: C) = MatcherResult(
      value.contains(t),
      { "Sequence should contain element $t" },
      { "Sequence should not contain element $t" }
   )
}

infix fun > C?.shouldNotContainExactly(expected: C) = this shouldNot containExactly(expected)
fun > C?.shouldNotContainExactly(vararg expected: T) = this shouldNot containExactly(*expected)

infix fun > C?.shouldContainExactly(expected: C) = this should containExactly(expected)
fun > C?.shouldContainExactly(vararg expected: T) = this should containExactly(*expected)

fun  containExactly(vararg expected: T): Matcher?> = containExactly(expected.asSequence())

/** Assert that a sequence contains exactly the given values and nothing else, in order. */
fun > containExactly(expected: C): Matcher = neverNullMatcher { value ->
   var passed: Boolean = value.count() == expected.count()
   var failMessage = "Sequence should contain exactly $expected but was $value"
   if (passed) {
      val diff = value.zip(expected) { a, b -> Triple(a, b, a == b) }.withIndex().find { !it.value.third }
      if (diff != null) {
         passed = false
         failMessage += " (expected ${diff.value.second} at ${diff.index} but found ${diff.value.first})"
      }
   } else {
      failMessage += " (expected ${expected.count()} elements but found ${value.count()})"
   }
   MatcherResult(
      passed,
      failMessage,
      "Sequence should not be exactly $expected"
   )
}

@Deprecated("use shouldNotContainAllInAnyOrder", ReplaceWith("shouldNotContainAllInAnyOrder"))
infix fun > C?.shouldNotContainExactlyInAnyOrder(expected: C) =
   this shouldNot containAllInAnyOrder(expected)

@Deprecated("use shouldNotContainAllInAnyOrder", ReplaceWith("shouldNotContainAllInAnyOrder"))
fun > C?.shouldNotContainExactlyInAnyOrder(vararg expected: T) =
   this shouldNot containAllInAnyOrder(*expected)

@Deprecated("use shouldContainAllInAnyOrder", ReplaceWith("shouldContainAllInAnyOrder"))
infix fun > C?.shouldContainExactlyInAnyOrder(expected: C) =
   this should containAllInAnyOrder(expected)

@Deprecated("use shouldContainAllInAnyOrder", ReplaceWith("shouldContainAllInAnyOrder"))
fun > C?.shouldContainExactlyInAnyOrder(vararg expected: T) =
   this should containAllInAnyOrder(*expected)

@Deprecated("use containAllInAnyOrder", ReplaceWith("containAllInAnyOrder"))
fun  containExactlyInAnyOrder(vararg expected: T): Matcher?> =
   containAllInAnyOrder(expected.asSequence())

@Deprecated("use containAllInAnyOrder", ReplaceWith("containAllInAnyOrder"))
/** Assert that a sequence contains the given values and nothing else, in any order. */
fun > containExactlyInAnyOrder(expected: C): Matcher = containAllInAnyOrder(expected)

infix fun > C?.shouldNotContainAllInAnyOrder(expected: C) =
   this shouldNot containAllInAnyOrder(expected)

fun > C?.shouldNotContainAllInAnyOrder(vararg expected: T) =
   this shouldNot containAllInAnyOrder(*expected)

infix fun > C?.shouldContainAllInAnyOrder(expected: C) =
   this should containAllInAnyOrder(expected)

fun > C?.shouldContainAllInAnyOrder(vararg expected: T) =
   this should containAllInAnyOrder(*expected)

fun  containAllInAnyOrder(vararg expected: T): Matcher?> =
   containAllInAnyOrder(expected.asSequence())

/** Assert that a sequence contains all the given values and nothing else, in any order. */
fun > containAllInAnyOrder(expected: C): Matcher = neverNullMatcher { value ->
   val passed = value.count() == expected.count() && expected.all { value.contains(it) }
   MatcherResult(
      passed,
      { "Sequence should contain the values of $expected in any order, but was $value" },
      { "Sequence should not contain the values of $expected in any order" }
   )
}

infix fun , C : Sequence> C.shouldHaveUpperBound(t: T) = this should haveUpperBound(t)

fun , C : Sequence> haveUpperBound(t: T) = object : Matcher {
   override fun test(value: C) = MatcherResult(
      (value.max() ?: t) <= t,
      { "Sequence should have upper bound $t" },
      { "Sequence should not have upper bound $t" }
   )
}

infix fun , C : Sequence> C.shouldHaveLowerBound(t: T) = this should haveLowerBound(t)

fun , C : Sequence> haveLowerBound(t: T) = object : Matcher {
   override fun test(value: C) = MatcherResult(
      (value.min() ?: t) >= t,
      { "Sequence should have lower bound $t" },
      { "Sequence should not have lower bound $t" }
   )
}

fun  Sequence.shouldBeUnique() = this should beUnique()
fun  Sequence.shouldNotBeUnique() = this shouldNot beUnique()
fun  beUnique() = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.toSet().size == value.count(),
      { "Sequence should be Unique" },
      { "Sequence should contain at least one duplicate element" }
   )
}

fun  Sequence.shouldContainDuplicates() = this should containDuplicates()
fun  Sequence.shouldNotContainDuplicates() = this shouldNot containDuplicates()
fun  containDuplicates() = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.toSet().size < value.count(),
      { "Sequence should contain duplicates" },
      { "Sequence should not contain duplicates" }
   )
}

fun > Sequence.shouldBeSorted() = this should beSorted()
fun > Sequence.shouldNotBeSorted() = this shouldNot beSorted()

fun > beSorted(): Matcher> = sorted()
fun > sorted(): Matcher> = object : Matcher> {
   override fun test(value: Sequence): MatcherResult {
      @Suppress("UNUSED_DESTRUCTURED_PARAMETER_ENTRY")
      val failure = value.zipWithNext().withIndex().firstOrNull { (i, it) -> it.first > it.second }
      val snippet = value.joinToString(",", limit = 10)
      val elementMessage = when (failure) {
         null -> ""
         else -> ". Element ${failure.value.first} at index ${failure.index} was greater than element ${failure.value.second}"
      }
      return MatcherResult(
         failure == null,
         { "Sequence $snippet should be sorted$elementMessage" },
         { "Sequence $snippet should not be sorted" }
      )
   }
}

infix fun  Sequence.shouldBeSortedWith(comparator: Comparator) = this should beSortedWith(comparator)

infix fun  Sequence.shouldBeSortedWith(cmp: (T, T) -> Int) = this should beSortedWith(cmp)

fun  beSortedWith(comparator: Comparator): Matcher> = sortedWith(comparator)

fun  beSortedWith(cmp: (T, T) -> Int): Matcher> = sortedWith(cmp)
fun  sortedWith(comparator: Comparator): Matcher> = sortedWith { a, b ->
   comparator.compare(a, b)
}

fun  sortedWith(cmp: (T, T) -> Int): Matcher> = object : Matcher> {
   override fun test(value: Sequence): MatcherResult {
      @Suppress("UNUSED_DESTRUCTURED_PARAMETER_ENTRY")
      val failure = value.zipWithNext().withIndex().firstOrNull { (i, it) -> cmp(it.first, it.second) > 0 }
      val snippet = value.joinToString(",", limit = 10)
      val elementMessage = when (failure) {
         null -> ""
         else -> ". Element ${failure.value.first} at index ${failure.index} shouldn't precede element ${failure.value.second}"
      }
      return MatcherResult(
         failure == null,
         { "Sequence $snippet should be sorted$elementMessage" },
         { "Sequence $snippet should not be sorted" }
      )
   }
}

infix fun  Sequence.shouldNotBeSortedWith(comparator: Comparator) = this shouldNot beSortedWith(comparator)
infix fun  Sequence.shouldNotBeSortedWith(cmp: (T, T) -> Int) = this shouldNot beSortedWith(cmp)

infix fun  Sequence.shouldHaveSingleElement(t: T) = this should singleElement(t)
infix fun  Sequence.shouldNotHaveSingleElement(t: T) = this shouldNot singleElement(t)

fun  singleElement(t: T) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.count() == 1 && value.first() == t,
      { "Sequence should be a single element of $t but has ${value.count()} elements" },
      { "Sequence should not be a single element of $t" }
   )
}


infix fun  Sequence.shouldHaveCount(count: Int) = this should haveCount(count)
infix fun  Sequence.shouldNotHaveCount(count: Int) = this shouldNot haveCount(
   count)

infix fun  Sequence.shouldHaveSize(size: Int) = this should haveCount(size)
infix fun  Sequence.shouldNotHaveSize(size: Int) = this shouldNot haveCount(size)
//fun  haveSize(size: Int) = haveCount(size)
fun  haveSize(size: Int): Matcher> = haveCount(size)

fun  haveCount(count: Int): Matcher> = object : Matcher> {
   override fun test(value: Sequence) =
      MatcherResult(
         value.count() == count,
         { "Sequence should have count $count but has count ${value.count()}" },
         { "Sequence should not have count $count" }
      )
}


infix fun  Sequence.shouldBeLargerThan(other: Sequence) = this should beLargerThan(other)

fun  beLargerThan(other: Sequence) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.count() > other.count(),
      { "Sequence of count ${value.count()} should be larger than sequence of count ${other.count()}" },
      { "Sequence of count ${value.count()} should not be larger than sequence of count ${other.count()}" }
   )
}

infix fun  Sequence.shouldBeSmallerThan(other: Sequence) = this should beSmallerThan(other)

fun  beSmallerThan(other: Sequence) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.count() < other.count(),
      { "Sequence of count ${value.count()} should be smaller than sequence of count ${other.count()}" },
      { "Sequence of count ${value.count()} should not be smaller than sequence of count ${other.count()}" }
   )
}

infix fun  Sequence.shouldBeSameCountAs(other: Sequence) = this should beSameCountAs(
   other)

fun  beSameCountAs(other: Sequence) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.count() == other.count(),
      { "Sequence of count ${value.count()} should be the same count as sequence of count ${other.count()}" },
      { "Sequence of count ${value.count()} should not be the same count as sequence of count ${other.count()}" }
   )
}

infix fun  Sequence.shouldBeSameSizeAs(other: Sequence) = this.shouldBeSameCountAs(other)

infix fun  Sequence.shouldHaveAtLeastCount(n: Int) = this shouldHave atLeastCount(n)

fun  atLeastCount(n: Int) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.count() >= n,
      { "Sequence should contain at least $n elements" },
      { "Sequence should contain less than $n elements" }
   )
}

infix fun  Sequence.shouldHaveAtLeastSize(n: Int) = this.shouldHaveAtLeastCount(n)

infix fun  Sequence.shouldHaveAtMostCount(n: Int) = this shouldHave atMostCount(n)
fun  atMostCount(n: Int) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.count() <= n,
      { "Sequence should contain at most $n elements" },
      { "Sequence should contain more than $n elements" }
   )
}

infix fun  Sequence.shouldHaveAtMostSize(n: Int) = this shouldHave atMostCount(n)


infix fun  Sequence.shouldExist(p: (T) -> Boolean) = this should exist(p)
fun  exist(p: (T) -> Boolean) = object : Matcher> {
   override fun test(value: Sequence) = MatcherResult(
      value.any { p(it) },
      { "Sequence should contain an element that matches the predicate $p" },
      { "Sequence should not contain an element that matches the predicate $p" }
   )
}

fun > Sequence.shouldContainInOrder(vararg ts: T) =
   this should containsInOrder(ts.asSequence())

infix fun > Sequence.shouldContainInOrder(expected: Sequence) =
   this should containsInOrder(expected)

fun > Sequence.shouldNotContainInOrder(expected: Sequence) =
   this shouldNot containsInOrder(expected)

/** Assert that a sequence contains a given subsequence, possibly with values in between. */
fun  containsInOrder(subsequence: Sequence): Matcher?> = neverNullMatcher { actual ->
   val subsequenceCount = subsequence.count()
   require(subsequenceCount > 0) { "expected values must not be empty" }

   var subsequenceIndex = 0
   val actualIterator = actual.iterator()

   while (actualIterator.hasNext() && subsequenceIndex < subsequenceCount) {
      if (actualIterator.next() == subsequence.elementAt(subsequenceIndex)) subsequenceIndex += 1
   }

   MatcherResult(
      subsequenceIndex == subsequence.count(),
      { "[$actual] did not contain the elements [$subsequence] in order" },
      { "[$actual] should not contain the elements [$subsequence] in order" }
   )
}

fun  Sequence.shouldBeEmpty() = this should beEmpty()
fun  Sequence.shouldNotBeEmpty() = this shouldNot beEmpty()
fun  beEmpty(): Matcher> = object : Matcher> {
   override fun test(value: Sequence): MatcherResult = MatcherResult(
      value.count() == 0,
      { "Sequence should be empty" },
      { "Sequence should not be empty" }
   )
}


fun  Sequence.shouldContainAll(vararg ts: T) = this should containAll(ts.asSequence())
infix fun  Sequence.shouldContainAll(ts: Collection) = this should containAll(ts.asSequence())

infix fun  Sequence.shouldContainAll(ts: Sequence) = this should containAll(ts)
fun  Sequence.shouldNotContainAll(vararg ts: T) = this shouldNot containAll(ts.asSequence())
infix fun  Sequence.shouldNotContainAll(ts: Collection) = this shouldNot containAll(ts.asSequence())

infix fun  Sequence.shouldNotContainAll(ts: Sequence) = this shouldNot containAll(ts)

fun > containAll(ts: C): Matcher> = object : Matcher> {
   override fun test(value: Sequence): MatcherResult = MatcherResult(
      ts.all { value.contains(it) },
      { "Sequence should contain all of ${value.joinToString(",", limit = 10)}" },
      { "Sequence should not contain all of ${value.joinToString(",", limit = 10)}" }
   )
}