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

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

package io.kotest.matchers

/**
 * A [Matcher] is the main abstraction in the assertions library.
 *
 * Implementations contain a single function, called 'test', which
 * accepts a value of type T and returns an instance of [MatcherResult].
 * This [MatcherResult] return value contains the state of the assertion
 * after it has been evaluted.
 *
 * A matcher will typically be invoked when used with the `should`
 * functions in the assertions DSL. For example, `2 should beLessThan(4)`
 *
 */
interface Matcher {

   fun test(value: T): MatcherResult

   fun  contramap(f: (U) -> T): Matcher = object : Matcher {
      override fun test(value: U): MatcherResult = [email protected](f(value))
   }

   fun invert(): Matcher = object : Matcher {
      override fun test(value: T): MatcherResult {
         val result = [email protected](value)
         return MatcherResult(!result.passed(), result.negatedFailureMessage(), result.failureMessage())
      }
   }

   infix fun  compose(fn: (U) -> T): Matcher = object : Matcher {
      override fun test(value: U): MatcherResult = [email protected](fn(value))
   }

   infix fun and(other: Matcher): Matcher = object : Matcher {
      override fun test(value: T): MatcherResult {
         val r = [email protected](value)
         return if (!r.passed())
            r
         else
            other.test(value)
      }
   }

   infix fun or(other: Matcher): Matcher = object : Matcher {
      override fun test(value: T): MatcherResult {
         val r = [email protected](value)
         return if (r.passed())
            r
         else
            other.test(value)
      }
   }
}

/**
 * A [Matcher] that asserts that the value is not `null` before performing the test.
 *
 * The matcher returned by [invert] will _also_ assert that the value is not `null`. Use this for matchers that
 * should fail on `null` values, whether called with `should` or `shouldNot`.
 */
internal abstract class NeverNullMatcher : Matcher {
   final override fun test(value: T?): MatcherResult {
      return if (value == null) MatcherResult(false, "Expecting actual not to be null", "")
      else testNotNull(value)
   }

   override fun invert(): Matcher = object : NeverNullMatcher() {
      override fun testNotNull(value: T): MatcherResult {
         val result = [email protected](value)
         return MatcherResult(!result.passed(), result.negatedFailureMessage(), result.failureMessage())
      }
   }

   abstract fun testNotNull(value: T): MatcherResult
}

fun  neverNullMatcher(test: (T) -> MatcherResult): Matcher {
   return object : NeverNullMatcher() {
      override fun testNotNull(value: T): MatcherResult {
         return test(value)
      }
   }
}

/**
 * An instance of [MatcherResult] contains the result of an evaluation of a [Matcher].
 */
interface MatcherResult {

   /**
    * Returns true if the matcher indicated this was a valid
    * value and false if the matcher indicated an invalid value.
    */
   fun passed(): Boolean

   /**
    * Returns a message indicating why the evaluation failed
    * for when this matcher is used in the positive sense. For example,
    * if a size matcher was used like `mylist should haveSize(5)` then
    * an appropriate error message would be "list should be size 5".
    */
   fun failureMessage(): String

   /**
    * Returns a message indicating why the evaluation
    * failed for when this matcher is used in the negative sense. For example,
    * if a size matcher was used like `mylist shouldNot haveSize(5)` then
    * an appropriate negated failure would be "List should not have size 5".
    */
   fun negatedFailureMessage(): String

   companion object {

      operator fun invoke(
         passed: Boolean,
         failureMessage: String,
         negatedFailureMessage: String
      ) = object : MatcherResult {
         override fun passed(): Boolean = passed
         override fun failureMessage(): String = failureMessage
         override fun negatedFailureMessage(): String = negatedFailureMessage
      }

      operator fun invoke(
         passed: Boolean,
         failureMessageFn: () -> String,
         negatedFailureMessageFn: () -> String
      ) = object : MatcherResult {
         override fun passed(): Boolean = passed
         override fun failureMessage(): String = failureMessageFn()
         override fun negatedFailureMessage(): String = negatedFailureMessageFn()
      }
   }
}