Skip to content

Commit 586aa28

Browse files
committed
Create Checkers.Helpers helper functions.
1 parent 799b8e6 commit 586aa28

File tree

2 files changed

+86
-79
lines changed

2 files changed

+86
-79
lines changed

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 [[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) {
@@ -43,9 +45,6 @@ trait Checkers {
4345
B: PropF](
4446
f: (A1, A2, A3) => B)(
4547
implicit loc: SourceLocation): F[Expectations] = {
46-
implicit val tuple3Show: Show[(A1, A2, A3)] = {
47-
case (a1, a2, a3) => s"(${a1.show},${a2.show},${a3.show})"
48-
}
4948
forall(implicitly[Arbitrary[(A1, A2, A3)]].arbitrary)(liftProp(
5049
f.tupled))
5150
}
@@ -58,10 +57,6 @@ trait Checkers {
5857
B: PropF
5958
](f: (A1, A2, A3, A4) => B)(
6059
implicit loc: SourceLocation): F[Expectations] = {
61-
implicit val tuple3Show: Show[(A1, A2, A3, A4)] = {
62-
case (a1, a2, a3, a4) =>
63-
s"(${a1.show},${a2.show},${a3.show},${a4.show})"
64-
}
6560
forall(implicitly[Arbitrary[(A1, A2, A3, A4)]].arbitrary)(
6661
liftProp(f.tupled))
6762
}
@@ -75,10 +70,6 @@ trait Checkers {
7570
B: PropF
7671
](f: (A1, A2, A3, A4, A5) => B)(
7772
implicit loc: SourceLocation): F[Expectations] = {
78-
implicit val tuple3Show: Show[(A1, A2, A3, A4, A5)] = {
79-
case (a1, a2, a3, a4, a5) =>
80-
s"(${a1.show},${a2.show},${a3.show},${a4.show},${a5.show})"
81-
}
8273
forall(implicitly[Arbitrary[(A1, A2, A3, A4, A5)]].arbitrary)(
8374
liftProp(f.tupled))
8475
}
@@ -93,10 +84,6 @@ trait Checkers {
9384
B: PropF
9485
](f: (A1, A2, A3, A4, A5, A6) => B)(
9586
implicit loc: SourceLocation): F[Expectations] = {
96-
implicit val tuple3Show: Show[(A1, A2, A3, A4, A5, A6)] = {
97-
case (a1, a2, a3, a4, a5, a6) =>
98-
s"(${a1.show},${a2.show},${a3.show},${a4.show},${a5.show},${a6.show})"
99-
}
10087
forall(implicitly[Arbitrary[(A1, A2, A3, A4, A5, A6)]].arbitrary)(
10188
liftProp(f.tupled))
10289
}
@@ -106,6 +93,59 @@ trait Checkers {
10693
forall_(gen, liftProp(f))
10794

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

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

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

0 commit comments

Comments
 (0)