2121
2222package fs2
2323
24- import cats .Eq
25- import cats .effect .IO
24+ import cats .{Applicative , Eq , ~> }
25+ import cats .data .{IdT , OptionT }
26+ import cats .effect .{Concurrent , IO , Ref , Resource }
2627import cats .effect .testkit .TestInstances
2728import cats .laws .discipline ._
2829import cats .laws .discipline .arbitrary ._
30+ import cats .mtl .LiftValue
31+ import cats .mtl .laws .discipline .{LiftKindTests , LiftValueTests }
32+ import org .scalacheck .{Arbitrary , Gen }
2933
3034class StreamLawsSuite extends Fs2Suite with TestInstances {
3135 implicit val ticker : Ticker = Ticker ()
3236
33- implicit def eqStream [O : Eq ]: Eq [Stream [IO , O ]] =
34- Eq .instance((x, y) =>
35- Eq [IO [Vector [Either [Throwable , O ]]]]
36- .eqv(x.attempt.compile.toVector, y.attempt.compile.toVector)
37- )
37+ implicit def eqStream [F [_], O ](implicit
38+ F : Concurrent [F ],
39+ eqFVecEitherThrowO : Eq [F [Vector [Either [Throwable , O ]]]]
40+ ): Eq [Stream [F , O ]] =
41+ Eq .by((_ : Stream [F , O ]).attempt.compile.toVector)
42+
43+ private [this ] val counter : IO [Ref [IO , Int ]] = IO .ref(0 )
44+
45+ implicit val arbitraryScope : Arbitrary [IO ~> IO ] =
46+ Arbitrary {
47+ Gen .const {
48+ new (IO ~> IO ) {
49+ def apply [A ](fa : IO [A ]): IO [A ] =
50+ for {
51+ ref <- counter
52+ res <- ref.update(_ + 1 ) >> fa
53+ } yield res
54+ }
55+ }
56+ }
3857
3958 checkAll(
4059 " MonadError[Stream[F, *], Throwable]" ,
@@ -50,4 +69,32 @@ class StreamLawsSuite extends Fs2Suite with TestInstances {
5069 " Align[Stream[F, *]]" ,
5170 AlignTests [Stream [IO , * ]].align[Int , Int , Int , Int ]
5271 )
72+ checkAll(
73+ " LiftKind[IO, Stream[IO, *]" ,
74+ LiftKindTests [IO , Stream [IO , * ]].liftKind[Int , Int ]
75+ )
76+ checkAll(
77+ " LiftKind[IO, Stream[OptionT[IO, *], *]" ,
78+ LiftKindTests [IO , Stream [OptionT [IO , * ], * ]].liftKind[Int , Int ]
79+ )
80+ checkAll(
81+ " LiftValue[Resource[IO, *], Stream[IO, *]" ,
82+ LiftValueTests [Resource [IO , * ], Stream [IO , * ]].liftValue[Int , Int ]
83+ )
84+ locally {
85+ // this is a somewhat silly instance, but we need a
86+ // `LiftValue[X, Resource[IO, *]]` instance where `X` is not `IO` because
87+ // that already has a higher priority implicit instance
88+ implicit val liftIdTResource : LiftValue [IdT [IO , * ], Resource [IO , * ]] =
89+ new LiftValue [IdT [IO , * ], Resource [IO , * ]] {
90+ val applicativeF : Applicative [IdT [IO , * ]] = implicitly
91+ val applicativeG : Applicative [Resource [IO , * ]] = implicitly
92+ def apply [A ](fa : IdT [IO , A ]): Resource [IO , A ] =
93+ Resource .eval(fa.value)
94+ }
95+ checkAll(
96+ " LiftValue[IdT[IO, *], Stream[IO, *]] via Resource[IO, *]" ,
97+ LiftValueTests [IdT [IO , * ], Stream [IO , * ]].liftValue[Int , Int ]
98+ )
99+ }
53100}
0 commit comments