Skip to content

Commit 05e235d

Browse files
committed
Create Checkers.Helpers helper functions.
1 parent 9d71404 commit 05e235d

2 files changed

Lines changed: 86 additions & 79 deletions

File tree

modules/scalacheck/shared/src/main/scala/weaver/scalacheck/CheckConfig.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,21 @@ case class CheckConfig private (
2828
/**
2929
* The proportion of values discarded by the generator allowed before the test
3030
* is considered failed.
31+
*
32+
* A value is considered discarded if the generator outputs `None`. The
33+
* generator must not discard more values than the number of successful runs,
34+
* so this ratio is a proportion of [[minimumSuccessful]].
3135
*/
3236
def withMaximumDiscardRatio(maximumDiscardRatio: Int) = copy(
3337
maximumDiscardRatio = maximumDiscardRatio
3438
)
3539

40+
/** The [[org.scalacheck.Gen.Parameters.size]] of the generator. */
3641
def withMaximumGeneratorSize(maximumGeneratorSize: Int) = copy(
3742
maximumGeneratorSize = maximumGeneratorSize
3843
)
3944

45+
/** The number of concurrent runs. */
4046
def withPerPropertyParallelism(perPropertyParallelism: Int) = copy(
4147
perPropertyParallelism = perPropertyParallelism
4248
)

modules/scalacheck/shared/src/main/scala/weaver/scalacheck/Checkers.scala

Lines changed: 80 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import scala.util.control.NoStackTrace
77
import org.scalacheck.rng.Seed
88
import org.scalacheck.{ Arbitrary, Gen }
99
import cats.data.Validated
10+
import cats.effect.Concurrent
11+
import cats.MonadThrow
1012

1113
trait Checkers {
1214
self: EffectSuiteAux =>
@@ -20,7 +22,7 @@ trait Checkers {
2022
f andThen (b => Prop[F, B].lift(b))
2123
}
2224

23-
// Configuration for property-based tests
25+
/** Configuration for all property-based tests in this suite. */
2426
def checkConfig: CheckConfig = CheckConfig.default
2527

2628
class PartiallyAppliedForall(config: CheckConfig) {
@@ -42,9 +44,6 @@ trait Checkers {
4244
B: PropF](
4345
f: (A1, A2, A3) => B)(
4446
implicit loc: SourceLocation): F[Expectations] = {
45-
implicit val tuple3Show: Show[(A1, A2, A3)] = {
46-
case (a1, a2, a3) => s"(${a1.show},${a2.show},${a3.show})"
47-
}
4847
forall_(implicitly[Arbitrary[(A1, A2, A3)]].arbitrary, liftProp(f.tupled))
4948
}
5049

@@ -56,10 +55,6 @@ trait Checkers {
5655
B: PropF
5756
](f: (A1, A2, A3, A4) => B)(
5857
implicit loc: SourceLocation): F[Expectations] = {
59-
implicit val tuple3Show: Show[(A1, A2, A3, A4)] = {
60-
case (a1, a2, a3, a4) =>
61-
s"(${a1.show},${a2.show},${a3.show},${a4.show})"
62-
}
6358
forall_(implicitly[Arbitrary[(A1, A2, A3, A4)]].arbitrary,
6459
liftProp(f.tupled))
6560
}
@@ -73,10 +68,6 @@ trait Checkers {
7368
B: PropF
7469
](f: (A1, A2, A3, A4, A5) => B)(
7570
implicit loc: SourceLocation): F[Expectations] = {
76-
implicit val tuple3Show: Show[(A1, A2, A3, A4, A5)] = {
77-
case (a1, a2, a3, a4, a5) =>
78-
s"(${a1.show},${a2.show},${a3.show},${a4.show},${a5.show})"
79-
}
8071
forall_(implicitly[Arbitrary[(A1, A2, A3, A4, A5)]].arbitrary,
8172
liftProp(f.tupled))
8273
}
@@ -91,10 +82,6 @@ trait Checkers {
9182
B: PropF
9283
](f: (A1, A2, A3, A4, A5, A6) => B)(
9384
implicit loc: SourceLocation): F[Expectations] = {
94-
implicit val tuple3Show: Show[(A1, A2, A3, A4, A5, A6)] = {
95-
case (a1, a2, a3, a4, a5, a6) =>
96-
s"(${a1.show},${a2.show},${a3.show},${a4.show},${a5.show},${a6.show})"
97-
}
9885
forall_(implicitly[Arbitrary[(A1, A2, A3, A4, A5, A6)]].arbitrary,
9986
liftProp(f.tupled))
10087
}
@@ -104,6 +91,59 @@ trait Checkers {
10491
forall_(gen, liftProp(f))
10592

10693
private def forall_[A: Show](gen: Gen[A], f: A => F[Expectations])(
94+
implicit loc: SourceLocation): F[Expectations] =
95+
Helpers.forall(config, gen, f)
96+
}
97+
98+
object forall extends PartiallyAppliedForall(checkConfig) {
99+
100+
/** Configuration for this specific property assertion. */
101+
def withConfig(config: CheckConfig) = new PartiallyAppliedForall(config)
102+
}
103+
}
104+
105+
object Checkers {
106+
// These allow us to define functions that go from F[Expectations]
107+
trait Prop[F[_], A] {
108+
def lift(a: A): F[Expectations]
109+
}
110+
111+
object Prop {
112+
def apply[F[_], B](implicit ev: Prop[F, B]): Prop[F, B] = ev
113+
114+
implicit def wrap[F[_]: Applicative]: Prop[F, Expectations] =
115+
new Prop[F, Expectations] {
116+
def lift(a: Expectations): F[Expectations] = Applicative[F].pure(a)
117+
}
118+
119+
implicit def unwrapped[F[_], FE](
120+
implicit ev: FE <:< F[Expectations]): Prop[F, FE] =
121+
new Prop[F, FE] {
122+
def lift(a: FE): F[Expectations] = ev(a)
123+
}
124+
}
125+
private def failureMessage(ith: Int, seed: Seed, input: String): String =
126+
s"""Property test failed on try $ith with seed ${seed} and input $input.
127+
|You can reproduce this by adding the following configuration to your test:
128+
|
129+
|forall.withConfig(checkConfig.withInitialSeed(org.scalacheck.rng.$seed.toOption))""".stripMargin
130+
131+
132+
private[scalacheck] class PropertyTestError(
133+
ith: Int,
134+
seed: Seed,
135+
input: String,
136+
cause: Throwable)
137+
extends RuntimeException(failureMessage(ith, seed, input), cause)
138+
with NoStackTrace
139+
140+
object Helpers {
141+
142+
/** Runs assertions in a property test concurrently. */
143+
def forall[F[_]: Defer: Concurrent, A: Show](
144+
config: CheckConfig,
145+
gen: Gen[A],
146+
f: A => F[Expectations])(
107147
implicit loc: SourceLocation): F[Expectations] = {
108148
val params = Gen.Parameters.default.withNoInitialSeed.withSize(
109149
config.maximumGeneratorSize)
@@ -130,37 +170,33 @@ trait Checkers {
130170
.map { status => status.endResult(config) }
131171
}
132172

133-
private def seedStream(initial: Seed): fs2.Stream[F, Seed] =
134-
fs2.Stream.iterate[F, Seed](initial)(_.slide)
135-
}
136-
137-
object forall extends PartiallyAppliedForall(checkConfig) {
138-
def withConfig(config: CheckConfig) = new PartiallyAppliedForall(config)
139-
}
140-
141-
private def testOne[T: Show](
142-
gen: Gen[T],
143-
f: T => F[Expectations])(
144-
params: Gen.Parameters,
145-
seed: Seed): F[TestResult] = {
146-
Defer[F](self.effect).defer {
147-
gen(params, seed)
148-
.traverse(x => f(x).attempt.map(x -> _))
149-
.map { (x: Option[(T, Either[Throwable, Expectations])]) =>
150-
x match {
151-
case Some((_, Right(ex))) if ex.run.isValid => TestResult.Success
152-
case Some((t, Right(ex))) => TestResult.Failure(t.show, ex)
153-
case Some((t, Left(exception: ExpectationFailed))) =>
154-
TestResult.Failure(t.show,
155-
Expectations(Validated.invalidNel(exception)))
156-
case Some((t, Left(other))) => TestResult.Exception(t.show, other)
157-
case None => TestResult.Discard
173+
private def testOne[F[_]: Defer: MonadThrow, T: Show](
174+
gen: Gen[T],
175+
f: T => F[Expectations])(
176+
params: Gen.Parameters,
177+
seed: Seed): F[TestResult] = {
178+
Defer[F].defer {
179+
gen(params, seed)
180+
.traverse(x => f(x).attempt.map(x -> _))
181+
.map { (x: Option[(T, Either[Throwable, Expectations])]) =>
182+
x match {
183+
case Some((_, Right(ex))) if ex.run.isValid => TestResult.Success
184+
case Some((t, Right(ex))) => TestResult.Failure(t.show, ex)
185+
case Some((t, Left(exception: ExpectationFailed))) =>
186+
TestResult.Failure(
187+
t.show,
188+
Expectations(Validated.invalidNel(exception)))
189+
case Some((t, Left(other))) => TestResult.Exception(t.show, other)
190+
case None => TestResult.Discard
191+
}
158192
}
159-
}
193+
}
160194
}
161-
}
162195

163-
private[scalacheck] case class Status[T](
196+
private def seedStream[F[_]](initial: Seed): fs2.Stream[F, Seed] =
197+
fs2.Stream.iterate[F, Seed](initial)(_.slide)
198+
}
199+
private case class Status[T](
164200
succeeded: Int,
165201
discarded: Int,
166202
failure: Option[Expectations]
@@ -199,28 +235,6 @@ trait Checkers {
199235
def start[T] = Status[T](0, 0, None)
200236
}
201237

202-
}
203-
204-
object Checkers {
205-
trait Prop[F[_], A] {
206-
def lift(a: A): F[Expectations]
207-
}
208-
209-
object Prop {
210-
def apply[F[_], B](implicit ev: Prop[F, B]): Prop[F, B] = ev
211-
212-
implicit def wrap[F[_]: Applicative]: Prop[F, Expectations] =
213-
new Prop[F, Expectations] {
214-
def lift(a: Expectations): F[Expectations] = Applicative[F].pure(a)
215-
}
216-
217-
implicit def unwrapped[F[_], FE](
218-
implicit ev: FE <:< F[Expectations]): Prop[F, FE] =
219-
new Prop[F, FE] {
220-
def lift(a: FE): F[Expectations] = ev(a)
221-
}
222-
}
223-
224238
private sealed trait TestResult
225239
private object TestResult {
226240
case object Success extends TestResult
@@ -230,17 +244,4 @@ object Checkers {
230244
case class Exception(input: String, error: Throwable) extends TestResult
231245
}
232246

233-
private def failureMessage(ith: Int, seed: Seed, input: String): String =
234-
s"""Property test failed on try $ith with seed ${seed} and input $input.
235-
|You can reproduce this by adding the following configuration to your test:
236-
|
237-
|forall.withConfig(checkConfig.withInitialSeed(org.scalacheck.rng.$seed.toOption))""".stripMargin
238-
239-
private class PropertyTestError(
240-
ith: Int,
241-
seed: Seed,
242-
input: String,
243-
cause: Throwable)
244-
extends RuntimeException(failureMessage(ith, seed, input), cause)
245-
with NoStackTrace
246247
}

0 commit comments

Comments
 (0)