@@ -7,6 +7,8 @@ import scala.util.control.NoStackTrace
77import org .scalacheck .rng .Seed
88import org .scalacheck .{ Arbitrary , Gen }
99import cats .data .Validated
10+ import cats .effect .Concurrent
11+ import cats .MonadThrow
1012
1113trait 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