Skip to content

Commit 818097a

Browse files
committed
Enable lazy construction of label strings
1 parent c91ec47 commit 818097a

File tree

5 files changed

+70
-17
lines changed

5 files changed

+70
-17
lines changed

core/jvm/src/test/scala/org/scalacheck/GenSpecification.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,8 @@ object GenSpecification extends Properties("Gen") with GenSpecificationVersionSp
583583
}
584584
val avg = sum / n
585585
s"average = $avg" |: avg >= 0.49 && avg <= 0.51
586+
s"average = $avg".ensuring(false, "eager evaluation") =|= avg >= 0.49 && avg <= 0.51
587+
avg >= 0.49 && avg <= 0.51 =|= s"average = $avg"
586588
}
587589

588590
property("uniform long #209") = {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* ScalaCheck
3+
* Copyright (c) 2007-2021 Rickard Nilsson. All rights reserved.
4+
* http://www.scalacheck.org
5+
*
6+
* This software is released under the terms of the Revised BSD License.
7+
* There is NO WARRANTY. See the file LICENSE for the full text.
8+
*/
9+
10+
package org.scalacheck
11+
12+
object `package` {
13+
implicit class `by-name label :|`(private val prop: Prop) extends AnyVal {
14+
/** Label this property.
15+
*
16+
* The label is evaluated lazily.
17+
* The operator name is chosen for its precedence btween
18+
* boolean operators and others.
19+
*/
20+
def =|= (label: => String): Prop = prop.map(_.label(label))
21+
}
22+
// chained implicit for true =|= label
23+
implicit class `by-name label bool :| label`(private val bool: Boolean) extends AnyVal {
24+
def =|= (label: => String): Prop = (bool: Prop).=|=(label)
25+
}
26+
implicit class `by-name label |: prop`(label: => String) {
27+
def =|= (prop: Prop): Prop = prop.=|=(label)
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* ScalaCheck
3+
* Copyright (c) 2007-2021 Rickard Nilsson. All rights reserved.
4+
* http://www.scalacheck.org
5+
*
6+
* This software is released under the terms of the Revised BSD License.
7+
* There is NO WARRANTY. See the file LICENSE for the full text.
8+
*/
9+
10+
package org.scalacheck
11+
12+
object `package` {
13+
14+
/** Label this property. The label is evaluated lazily.
15+
*
16+
* The operator name is chosen for its precedence between boolean operators and others.
17+
*/
18+
extension (prop: Prop) def =|=(label: => String): Prop = prop.map(_.label(label))
19+
20+
extension (label: => String) def =|=(prop: Prop): Prop = prop.map(_.label(label))
21+
}

core/shared/src/main/scala/org/scalacheck/Prop.scala

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,23 @@
99

1010
package org.scalacheck
1111

12-
import scala.annotation.tailrec
13-
1412
import rng.Seed
1513
import util.{Pretty, ConsoleReporter}
1614

1715
/** Helper class to satisfy ScalaJS compilation. Do not use this directly, use `Prop.apply` instead.
1816
*/
1917
sealed class PropFromFun(f: Gen.Parameters => Prop.Result) extends Prop {
18+
19+
/** Evaluate this property by applying the function. */
2020
def apply(prms: Gen.Parameters) = f(prms)
2121
}
2222

2323
@Platform.EnableReflectiveInstantiation
24-
sealed abstract class Prop extends Serializable { self =>
24+
sealed abstract class Prop extends Serializable {
2525

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

28+
/** Evaluate this property. */
2829
def apply(prms: Gen.Parameters): Result
2930

3031
def viewSeed(name: String): Prop =
@@ -36,7 +37,7 @@ sealed abstract class Prop extends Serializable { self =>
3637
val sd = Seed.random()
3738
(prms0.withInitialSeed(sd), sd)
3839
}
39-
val res = self(prms)
40+
val res = apply(prms)
4041
if (res.failure) println(s"failing seed for $name is ${seed.toBase64}")
4142
res
4243
}
@@ -46,7 +47,7 @@ sealed abstract class Prop extends Serializable { self =>
4647
useSeed(seed)
4748

4849
def useSeed(seed: Seed): Prop =
49-
Prop(prms0 => self(prms0.withInitialSeed(seed)))
50+
Prop(prms0 => apply(prms0.withInitialSeed(seed)))
5051

5152
def contramap(f: Gen.Parameters => Gen.Parameters): Prop =
5253
new PropFromFun(params => apply(f(params)))
@@ -197,14 +198,12 @@ object Prop {
197198
labels: Set[String] = Set.empty
198199
) {
199200
def success = status match {
200-
case True => true
201-
case Proof => true
201+
case True | Proof => true
202202
case _ => false
203203
}
204204

205205
def failure = status match {
206-
case False => true
207-
case Exception(_) => true
206+
case False | Exception(_) => true
208207
case _ => false
209208
}
210209

@@ -276,6 +275,10 @@ object Prop {
276275
case (Proof, _) => mergeRes(this, r, r.status)
277276
case (True, _) => mergeRes(this, r, r.status)
278277
}
278+
279+
def flatMap(f: Result => Result): Result = if (success) f(this) else this
280+
281+
def recover(f: Result => Result): Result = if (failure) f(this) else this
279282
}
280283

281284
sealed trait Status
@@ -1274,14 +1277,12 @@ object Prop {
12741277
/** Ensures that the property expression passed in completes within the given space of time.
12751278
*/
12761279
def within(maximumMs: Long)(wrappedProp: => Prop): Prop = {
1277-
@tailrec def attempt(prms: Gen.Parameters, endTime: Long): Result = {
1280+
def attempt(prms: Gen.Parameters, endTime: Long): Result = {
12781281
val result = wrappedProp.apply(prms)
1279-
if (System.currentTimeMillis > endTime) {
1280-
(if (result.failure) result else Result(status = False)).label("Timeout")
1281-
} else {
1282-
if (result.success) result
1283-
else attempt(prms, endTime)
1284-
}
1282+
if (System.currentTimeMillis > endTime)
1283+
result.flatMap(_ => Result(status = False)).label("Timeout")
1284+
else
1285+
result.recover(_ => attempt(prms, endTime))
12851286
}
12861287
Prop.apply(prms => attempt(prms, System.currentTimeMillis + maximumMs))
12871288
}

core/shared/src/test/scala/org/scalacheck/StatsSpecification.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ object StatsSpecification extends Properties("Stats") {
8787

8888
case class Bounds(min: Double, max: Double) {
8989
def contains(x: Double): Prop =
90-
Prop(min <= x && x <= max) :| s"($min <= $x <= $max) was false"
90+
Prop(min <= x && x <= max) =|= s"($min <= $x <= $max) was false"
9191
}
9292

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

0 commit comments

Comments
 (0)