21
21
22
22
package fs2
23
23
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 }
26
27
import cats .effect .testkit .TestInstances
27
28
import cats .laws .discipline ._
28
29
import cats .laws .discipline .arbitrary ._
30
+ import cats .mtl .LiftValue
31
+ import cats .mtl .laws .discipline .{LiftKindTests , LiftValueTests }
32
+ import org .scalacheck .{Arbitrary , Gen }
29
33
30
34
class StreamLawsSuite extends Fs2Suite with TestInstances {
31
35
implicit val ticker : Ticker = Ticker ()
32
36
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
+ }
38
57
39
58
checkAll(
40
59
" MonadError[Stream[F, *], Throwable]" ,
@@ -50,4 +69,32 @@ class StreamLawsSuite extends Fs2Suite with TestInstances {
50
69
" Align[Stream[F, *]]" ,
51
70
AlignTests [Stream [IO , * ]].align[Int , Int , Int , Int ]
52
71
)
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
+ }
53
100
}
0 commit comments