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

nyaya.test.PropTest.scala Maven / Gradle / Ivy

package nyaya.test

import nyaya.gen._
import nyaya.prop._
import nyaya.util.Util
import scala.Console._

object PropTest extends PropTestOps {
  implicit def defaultPropSettings: Settings =
    DefaultSettings.propSettings
}

object PropTestOps extends PropTestOps

trait PropTestOps {

  def testProp[A](p: Prop[A], g: Gen[A])(implicit S: Settings): Unit =
    assertSuccessful(p, PTest.test(p, g, S))

  def proveProp[A](p: Prop[A], d: Domain[A])(implicit S: Settings): Unit = {
    val rs = PTest.prove(p, d, S)
    assertSuccessful(p, rs)
    rs match {
      case RunState(n, Result.Satisfied) =>
        println(s"${YELLOW}Proposition $RESET[$p]$YELLOW was satisfied but not proved after $RED$n runs$YELLOW of an expected ${d.size}.")
        fail(s"Proposition [$p] was satisfied but not proved after $n runs of an expected ${d.size}.")
      case _ => ()
    }
  }

  private[this] def assertSuccessful[A](p: Prop[A], rs: RunState[A])(implicit S: Settings): Unit =
    rs match {
      case RunState(_, _: Result.Success) =>
        ()
      case RunState(runs, f: Result.Failure[A]) =>
        printPropFailureInfo(p, runs, f)
        throwPropFailure(p, f)
    }

  private[this] def printFailureInfo[A](a: A, header: String => String, printFooter: => Unit)(implicit S: Settings): Unit = {
    if (!S.debug) {
      val v = a.toString
      val w = Util.escapeString(v)
      println(header(v))
      if (w != v) println(s"$w\n")
    }
    printFooter
    println()
  }

  def printPropFailureInfo[A](p: Prop[A], runs: Int, f: Result.Failure[Any])(implicit S: Settings): Unit =
    f match {
      case e: Result.Falsified[Any] =>
        val name = e.eval.name.value
        printFailureInfo(e.a, v => s"\n${RED}Falsified $WHITE_B[$name]$RESET$RED after $runs runs with:$RESET\n$v\n",
          println(e.eval.report))

      case e: Result.Error[Any] =>
        printFailureInfo(e.a, v => s"\n${RED_B}Crashed $WHITE_B$RED[$p]$RESET$RED_B after $runs runs with:$RESET\n$v\n",
          e.error.printStackTrace())
    }

  def throwPropFailure[A](p: Prop[A], f: Result.Failure[Any]): Nothing =
    f match {
      case e: Result.Falsified[Any] => fail("Failed: " + e.eval.name.value)
      case e: Result.Error    [Any] => fail("Failed: " + p, e.error)
    }

  private[test] def fail(s: String, e: Throwable = null): Nothing =
    throw new java.lang.AssertionError(s, e)

  @inline implicit def autoToOpsPropExt  [A](p: Prop[A])  : PropExt  [A] = new PropExt(p)
  @inline implicit def autoToOpsGenExt   [A](g: Gen[A])   : GenExt   [A] = new GenExt(g.run)
  @inline implicit def autoToOpsDomainExt[A](d: Domain[A]): DomainExt[A] = new DomainExt(d)
}

import nyaya.test.PropTestOps._

class PropExt[A](private val p: Prop[A]) extends AnyVal {
  def mustBeSatisfiedBy         (g: Gen[A])(implicit S: Settings) = testProp(p, g)
  def mustBeSatisfiedBy_[B <: A](g: Gen[B])(implicit S: Settings) = testProp(p, g)

  def mustBeProvedBy         (d: Domain[A])(implicit S: Settings) = proveProp(p, d)
  def mustBeProvedBy_[B <: A](d: Domain[B])(implicit S: Settings) = proveProp(p, d.subst[A])
}

class GenExt[A](private val g: Gen.Run[A]) extends AnyVal {
  def mustSatisfy         (p: Prop[A])   (implicit S: Settings) = testProp(p, Gen(g))
  def mustSatisfyE        (f: A => EvalL)(implicit S: Settings) = testProp(Prop eval f, Gen(g))
  def _mustSatisfy[B >: A](p: Prop[B])   (implicit S: Settings) = testProp(p, Gen(g))

  /**
   * Find a seed that deterministically produces failure-inducing random data.
   */
  def bugHunt(seedStart     : Long    = 0L,
              seeds         : Int     = 10000,
              samplesPerSeed: Int     = 1,
              startMsg      : Boolean = true,
              printFailure  : Boolean = true,
              filterFailure : Result.Failure[A] => Boolean = _ => true)
             (p: Prop[A])(implicit S: Settings): Unit = {
    if (startMsg)
      println(s"Starting bugHunt: $seeds x $samplesPerSeed")
    val s2 = S.setSampleSize(samplesPerSeed)
    for (n <- 0 until seeds) {
      val seed = seedStart + n
      val g2 = Gen { c =>
        c.setSeed(seed)
        g(c)
      }
      PTest.test(p, g2, s2) match {
        case RunState(_, _: Result.Success)    => ()
        case RunState(_, f: Result.Failure[A]) =>
          if (filterFailure(f)) {
            if (printFailure)
              printPropFailureInfo(p, n, f)
            println(s"$YELLOW_B${BLACK}Found issue with seed:$RESET $BOLD$YELLOW$BLACK_B$seed$RESET\n")
            throwPropFailure(p, f)
          }
      }
    }
    println(s"$YELLOW_B${BLACK}Bug hunt failed.$RESET\n")
  }
}

class DomainExt[A](private val d: Domain[A]) extends AnyVal {
  def mustProve         (p: Prop[A])   (implicit S: Settings) = proveProp(p, d)
  def mustProveE        (f: A => EvalL)(implicit S: Settings) = proveProp(Prop eval f, d)
  def _mustProve[B >: A](p: Prop[B])   (implicit S: Settings) = proveProp(p, d.subst[B])
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy