From e46b2e1b533a1fdc7717b703cc5eb32b50f8bddc Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sun, 15 Nov 2015 11:30:15 -0500 Subject: [PATCH] Make Arbitrary StreamingT more arbitrary This still isn't a perfect Arbitrary instance for StreamingT, but I think it will help us hit more test cases than the current implementation. For example, I don't think the current instance ever results in `Wait` instances. --- .../cats/laws/discipline/Arbitrary.scala | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index 7169c22a51..f9327f068b 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -56,25 +56,35 @@ object arbitrary { implicit def streamingArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Streaming[A]] = Arbitrary(Gen.listOf(A.arbitrary).map(Streaming.fromList(_))) - // TODO: it would be better to do this recursively, i.e. more like: - // - // Gen.oneOf( - // for { a <- arbitrary[A]; s <- arbitrary[F[StreamingT[F, A]]] } yield cons(a, s), - // for { s <- arbitrary[F[StreamingT[F, A]]] } yield wait(s), - // const(StreamingT.empty[F, A])) - // - // However, getting this right with Scalacheck (and avoiding SOEs) is - // somewhat fiddly, so this will have to do for now. - // + def emptyStreamingTGen[F[_], A]: Gen[StreamingT[F, A]] = + Gen.const(StreamingT.empty[F, A]) + + def streamingTGen[F[_], A](maxDepth: Int)(implicit F: Monad[F], A: Arbitrary[A]): Gen[StreamingT[F, A]] = { + if (maxDepth <= 1) + emptyStreamingTGen[F, A] + else { + Gen.oneOf( + // Empty + emptyStreamingTGen[F, A], + // Wait + streamingTGen[F, A](maxDepth - 1).map(s => + StreamingT.wait(F.pure(s))), + // Cons + for { + a <- A.arbitrary + s <- streamingTGen[F, A](maxDepth - 1) + } yield StreamingT.cons(a, F.pure(s)) + ) + } + } + // The max possible size of a StreamingT instance (n) will result in // instances of up to n^3 in length when testing flatMap // composition. The current value (8) could result in streams of up // to 512 elements in length. Thus, since F may not be stack-safe, // we want to keep n relatively small. implicit def streamingTArbitrary[F[_], A](implicit F: Monad[F], A: Arbitrary[A]): Arbitrary[StreamingT[F, A]] = - Arbitrary(for { - as <- Gen.listOf(A.arbitrary).map(_.take(8)) - } yield StreamingT.fromList(as)) + Arbitrary(streamingTGen[F, A](8)) implicit def writerTArbitrary[F[_], L, V](implicit F: Arbitrary[F[(L, V)]]): Arbitrary[WriterT[F, L, V]] = Arbitrary(F.arbitrary.map(WriterT(_)))