Skip to content

Commit

Permalink
Enable lazy construction of label strings
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Mar 16, 2024
1 parent c91ec47 commit 818097a
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 17 deletions.
2 changes: 2 additions & 0 deletions core/jvm/src/test/scala/org/scalacheck/GenSpecification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,8 @@ object GenSpecification extends Properties("Gen") with GenSpecificationVersionSp
}
val avg = sum / n
s"average = $avg" |: avg >= 0.49 && avg <= 0.51
s"average = $avg".ensuring(false, "eager evaluation") =|= avg >= 0.49 && avg <= 0.51
avg >= 0.49 && avg <= 0.51 =|= s"average = $avg"
}

property("uniform long #209") = {
Expand Down
29 changes: 29 additions & 0 deletions core/shared/src/main/scala-2/org/scalacheck/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* ScalaCheck
* Copyright (c) 2007-2021 Rickard Nilsson. All rights reserved.
* http://www.scalacheck.org
*
* This software is released under the terms of the Revised BSD License.
* There is NO WARRANTY. See the file LICENSE for the full text.
*/

package org.scalacheck

object `package` {
implicit class `by-name label :|`(private val prop: Prop) extends AnyVal {
/** Label this property.
*
* The label is evaluated lazily.
* The operator name is chosen for its precedence btween
* boolean operators and others.
*/
def =|= (label: => String): Prop = prop.map(_.label(label))
}
// chained implicit for true =|= label
implicit class `by-name label bool :| label`(private val bool: Boolean) extends AnyVal {
def =|= (label: => String): Prop = (bool: Prop).=|=(label)
}
implicit class `by-name label |: prop`(label: => String) {
def =|= (prop: Prop): Prop = prop.=|=(label)
}
}
21 changes: 21 additions & 0 deletions core/shared/src/main/scala-3/org/scalacheck/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* ScalaCheck
* Copyright (c) 2007-2021 Rickard Nilsson. All rights reserved.
* http://www.scalacheck.org
*
* This software is released under the terms of the Revised BSD License.
* There is NO WARRANTY. See the file LICENSE for the full text.
*/

package org.scalacheck

object `package` {

/** Label this property. The label is evaluated lazily.
*
* The operator name is chosen for its precedence between boolean operators and others.
*/
extension (prop: Prop) def =|=(label: => String): Prop = prop.map(_.label(label))

extension (label: => String) def =|=(prop: Prop): Prop = prop.map(_.label(label))
}
33 changes: 17 additions & 16 deletions core/shared/src/main/scala/org/scalacheck/Prop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@

package org.scalacheck

import scala.annotation.tailrec

import rng.Seed
import util.{Pretty, ConsoleReporter}

/** Helper class to satisfy ScalaJS compilation. Do not use this directly, use `Prop.apply` instead.
*/
sealed class PropFromFun(f: Gen.Parameters => Prop.Result) extends Prop {

/** Evaluate this property by applying the function. */
def apply(prms: Gen.Parameters) = f(prms)
}

@Platform.EnableReflectiveInstantiation
sealed abstract class Prop extends Serializable { self =>
sealed abstract class Prop extends Serializable {

import Prop.{Result, True, False, Undecided, provedToTrue, mergeRes}

/** Evaluate this property. */
def apply(prms: Gen.Parameters): Result

def viewSeed(name: String): Prop =
Expand All @@ -36,7 +37,7 @@ sealed abstract class Prop extends Serializable { self =>
val sd = Seed.random()
(prms0.withInitialSeed(sd), sd)
}
val res = self(prms)
val res = apply(prms)
if (res.failure) println(s"failing seed for $name is ${seed.toBase64}")
res
}
Expand All @@ -46,7 +47,7 @@ sealed abstract class Prop extends Serializable { self =>
useSeed(seed)

def useSeed(seed: Seed): Prop =
Prop(prms0 => self(prms0.withInitialSeed(seed)))
Prop(prms0 => apply(prms0.withInitialSeed(seed)))

def contramap(f: Gen.Parameters => Gen.Parameters): Prop =
new PropFromFun(params => apply(f(params)))
Expand Down Expand Up @@ -197,14 +198,12 @@ object Prop {
labels: Set[String] = Set.empty
) {
def success = status match {
case True => true
case Proof => true
case True | Proof => true
case _ => false
}

def failure = status match {
case False => true
case Exception(_) => true
case False | Exception(_) => true
case _ => false
}

Expand Down Expand Up @@ -276,6 +275,10 @@ object Prop {
case (Proof, _) => mergeRes(this, r, r.status)
case (True, _) => mergeRes(this, r, r.status)
}

def flatMap(f: Result => Result): Result = if (success) f(this) else this

def recover(f: Result => Result): Result = if (failure) f(this) else this
}

sealed trait Status
Expand Down Expand Up @@ -1274,14 +1277,12 @@ object Prop {
/** Ensures that the property expression passed in completes within the given space of time.
*/
def within(maximumMs: Long)(wrappedProp: => Prop): Prop = {
@tailrec def attempt(prms: Gen.Parameters, endTime: Long): Result = {
def attempt(prms: Gen.Parameters, endTime: Long): Result = {
val result = wrappedProp.apply(prms)
if (System.currentTimeMillis > endTime) {
(if (result.failure) result else Result(status = False)).label("Timeout")
} else {
if (result.success) result
else attempt(prms, endTime)
}
if (System.currentTimeMillis > endTime)
result.flatMap(_ => Result(status = False)).label("Timeout")
else
result.recover(_ => attempt(prms, endTime))
}
Prop.apply(prms => attempt(prms, System.currentTimeMillis + maximumMs))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ object StatsSpecification extends Properties("Stats") {

case class Bounds(min: Double, max: Double) {
def contains(x: Double): Prop =
Prop(min <= x && x <= max) :| s"($min <= $x <= $max) was false"
Prop(min <= x && x <= max) =|= s"($min <= $x <= $max) was false"
}

implicit class MakeBounds(val n: Double) extends AnyVal {
Expand Down

0 comments on commit 818097a

Please sign in to comment.