Created
May 26, 2018 15:34
-
-
Save kimyongin/f83927528df897e30378d96507edd540 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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