Skip to content

Instantly share code, notes, and snippets.

@kimyongin
Created May 26, 2018 15:34
Show Gist options
  • Save kimyongin/f83927528df897e30378d96507edd540 to your computer and use it in GitHub Desktop.
Save kimyongin/f83927528df897e30378d96507edd540 to your computer and use it in GitHub Desktop.
package Chapter8.testing.Ex04
import Chapter8.testing.{Gen, RNG, State, Stream}
import Gen._
import Prop._
case class SimpleRNG(seed: Long) extends RNG {
override def nextInt: (Int, RNG) = {
val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
val nextRNG = SimpleRNG(newSeed)
val n = (newSeed >>> 16).toInt
(n, nextRNG)
}
}
case class Prop(run: (MaxSize, TestCases, RNG) => Result)
object Prop {
type SuccessCount = Int
type TestCases = Int
type MaxSize = Int
type FailedCase = String
sealed trait Result {
def isFalsified: Boolean
}
case object Passed extends Result {
def isFalsified = false
}
case class Falsified(failure: FailedCase, successes: SuccessCount) extends Result {
def isFalsified = true
}
case object Proved extends Result {
def isFalsified = false
}
def randomStream[A](g: Gen[A])(rng: RNG): Stream[A] =
Stream.unfold(rng)(rng => Some(g.sample.run(rng)))
def forAll[A](as: Gen[A])(f: A => Boolean): Prop = Prop {
(n, rng) => {
println(n, rng)
randomStream(as)(rng).zip(Stream.from(0)).take(n).map {
case (a, i) => try {
println("test case : " + a)
if (f(a)) Passed else Falsified(a.toString, i)
} catch {
case e: Exception => Falsified(buildMsg(a, e), i)
}
}.find(_.isFalsified).getOrElse(Passed)
}
}
def buildMsg[A](s: A, e: Exception): String =
s"test case: $s\n" +
s"generated an exception: ${e.getMessage}\n" +
s"stack trace:\n ${e.getStackTrace.mkString("\n")}"
def apply(f: (TestCases, RNG) => Result): Prop =
Prop { (_, n, rng) => f(n, rng) }
def check(p: => Boolean): Prop = Prop { (_, _, _) =>
if (p) Passed else Falsified("()", 0)
}
}
case class Gen[A](sample: State[RNG, A])
object Gen {
def choose(start: Int, stopExclusive: Int): Gen[Int] =
Gen(State(RNG.nonNegativeInt).map(n => start + n % (stopExclusive - start)))
def unit[A](a: => A): Gen[A] =
Gen(State.unit(a))
def boolean: Gen[Boolean] =
Gen(State(RNG.boolean))
def listOfN[A](n: Int, g: Gen[A]): Gen[List[A]] =
Gen(State.sequence(List.fill(n)(g.sample)))
}
object Main {
def main(args: Array[String]): Unit = {
val rng = SimpleRNG(43)
println(choose(5, 30).sample.run(rng))
println(boolean.sample.run(rng))
println(listOfN(10, boolean).sample.run(rng))
println(listOfN(10, choose(5, 30)).sample.run(rng))
val intList = listOfN(10, choose(5, 300))
val passedProp = forAll(intList)(is => is.count(x => x > 5) == 10)
println(passedProp.run(4, 3, rng))
val failedProp = forAll(intList)(is => is.isEmpty)
println(failedProp.run(1, 44, rng))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment