Skip to content

Commit bc5e9b6

Browse files
committed
Move nesting checks in common module
1 parent d4b7979 commit bc5e9b6

File tree

3 files changed

+83
-118
lines changed

3 files changed

+83
-118
lines changed

Diff for: ce2/shared/src/main/scala/munit/CatsEffectSuite.scala

+1-59
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package munit
1818

1919
import cats.effect.{ContextShift, IO, SyncIO, Timer}
20-
import cats.syntax.all._
2120
import scala.concurrent.{Future, ExecutionContext}
21+
import munit.internal.NestingChecks.{checkNestingIO, checkNestingSyncIO}
2222

2323
abstract class CatsEffectSuite
2424
extends FunSuite
@@ -47,64 +47,6 @@ abstract class CatsEffectSuite
4747
"SyncIO",
4848
{ case e: SyncIO[_] => Future(checkNestingSyncIO(e).unsafeRunSync())(munitExecutionContext) }
4949
)
50-
51-
// MUnit works by automatically chaining value transforms of shape `Any => Future[Any]`,
52-
// and we rely on this behavior to chain our `IO ~> Future` transform into the rest of MUnit.
53-
//
54-
// Unfortunately, this has an unforeseen consequence in CatsEffectSuite:
55-
// if you return `IO[IO[A]]` by accident, for example by using `map` instead of `flatMap`,
56-
// MUnit will execute both the inner and outer `IO` by applying our `IO` transform twice.
57-
//
58-
// This breaks the `IO` mental model, and can lead to very surprising behaviour, e.g see:
59-
// https://github.com/typelevel/munit-cats-effect/issues/159.
60-
//
61-
// This method checks for such a case, and fails the test with an actionable message.
62-
private def checkNestingIO(fa: IO[_]): IO[Any] = {
63-
def err(msg: String) = IO.raiseError[Any](new Exception(msg))
64-
65-
fa.flatMap {
66-
case _: IO[_] =>
67-
err(
68-
"your test returns an `IO[IO[_]]`, which means the inner `IO` will not execute." ++
69-
" Call `.flatten` if you want it to execute, or `.void` if you want to discard it"
70-
)
71-
case _: SyncIO[_] =>
72-
err(
73-
"your test returns an `IO[SyncIO[_]]`, which means the inner `SyncIO` will not execute." ++
74-
" Call `.flatMap(_.to[IO]) if you want it to execute, or `.void` if you want to discard it"
75-
)
76-
case _: Future[_] =>
77-
err(
78-
"your test returns an `IO[Future[_]]`, which means the inner `Future` might not execute." ++
79-
" Surround it with `IO.fromFuture` if you want it to execute, or call `.void` if you want to discard it"
80-
)
81-
case v => v.pure[IO]
82-
}
83-
}
84-
85-
// same as above, but for SyncIO
86-
private def checkNestingSyncIO(fa: SyncIO[_]): SyncIO[Any] = {
87-
def err(msg: String) = SyncIO.raiseError[Any](new Exception(msg))
88-
89-
fa.flatMap {
90-
case _: IO[_] =>
91-
err(
92-
"your test returns a `SyncIO[IO[_]]`, which means the inner `IO` will not execute." ++
93-
" Call `.to[IO].flatten` if you want it to execute, or `.void` if you want to discard it"
94-
)
95-
case _: SyncIO[_] =>
96-
err(
97-
"your test returns a `SyncIO[SyncIO[_]]`, which means the inner `SyncIO` will not execute." ++
98-
" Call `.flatten` if you want it to execute, or `.void` if you want to discard it"
99-
)
100-
case _: Future[_] =>
101-
err(
102-
"your test returns a `SyncIO[Future[_]]`, which means the inner `Future` might not execute." ++
103-
" Change it to `IO.fromFuture(yourTest.to[IO])` if you want it to execute, or call `.void` if you want to discard it"
104-
)
105-
case v => v.pure[SyncIO]
106-
}
107-
}
10850
}
10951

11052
object CatsEffectSuite {

Diff for: ce3/shared/src/main/scala/munit/CatsEffectSuite.scala

+1-59
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ package munit
1818

1919
import cats.effect.unsafe.IORuntime
2020
import cats.effect.{IO, SyncIO}
21-
import cats.syntax.all._
2221

2322
import scala.concurrent.{ExecutionContext, Future}
23+
import munit.internal.NestingChecks.{checkNestingIO, checkNestingSyncIO}
2424

2525
abstract class CatsEffectSuite
2626
extends FunSuite
@@ -47,64 +47,6 @@ abstract class CatsEffectSuite
4747
"SyncIO",
4848
{ case e: SyncIO[_] => Future(checkNestingSyncIO(e).unsafeRunSync())(munitExecutionContext) }
4949
)
50-
51-
// MUnit works by automatically chaining value transforms of shape `Any => Future[Any]`,
52-
// and we rely on this behavior to chain our `IO ~> Future` transform into the rest of MUnit.
53-
//
54-
// Unfortunately, this has an unforeseen consequence in CatsEffectSuite:
55-
// if you return `IO[IO[A]]` by accident, for example by using `map` instead of `flatMap`,
56-
// MUnit will execute both the inner and outer `IO` by applying our `IO` transform twice.
57-
//
58-
// This breaks the `IO` mental model, and can lead to very surprising behaviour, e.g see:
59-
// https://github.com/typelevel/munit-cats-effect/issues/159.
60-
//
61-
// This method checks for such a case, and fails the test with an actionable message.
62-
private def checkNestingIO(fa: IO[_]): IO[Any] = {
63-
def err(msg: String) = IO.raiseError[Any](new Exception(msg))
64-
65-
fa.flatMap {
66-
case _: IO[_] =>
67-
err(
68-
"your test returns an `IO[IO[_]]`, which means the inner `IO` will not execute." ++
69-
" Call `.flatten` if you want it to execute, or `.void` if you want to discard it"
70-
)
71-
case _: SyncIO[_] =>
72-
err(
73-
"your test returns an `IO[SyncIO[_]]`, which means the inner `SyncIO` will not execute." ++
74-
" Call `.flatMap(_.to[IO]) if you want it to execute, or `.void` if you want to discard it"
75-
)
76-
case _: Future[_] =>
77-
err(
78-
"your test returns an `IO[Future[_]]`, which means the inner `Future` might not execute." ++
79-
" Surround it with `IO.fromFuture` if you want it to execute, or call `.void` if you want to discard it"
80-
)
81-
case v => v.pure[IO]
82-
}
83-
}
84-
85-
// same as above, but for SyncIO
86-
private def checkNestingSyncIO(fa: SyncIO[_]): SyncIO[Any] = {
87-
def err(msg: String) = SyncIO.raiseError[Any](new Exception(msg))
88-
89-
fa.flatMap {
90-
case _: IO[_] =>
91-
err(
92-
"your test returns a `SyncIO[IO[_]]`, which means the inner `IO` will not execute." ++
93-
" Call `.to[IO].flatten` if you want it to execute, or `.void` if you want to discard it"
94-
)
95-
case _: SyncIO[_] =>
96-
err(
97-
"your test returns a `SyncIO[SyncIO[_]]`, which means the inner `SyncIO` will not execute." ++
98-
" Call `.flatten` if you want it to execute, or `.void` if you want to discard it"
99-
)
100-
case _: Future[_] =>
101-
err(
102-
"your test returns a `SyncIO[Future[_]]`, which means the inner `Future` might not execute." ++
103-
" Change it to `IO.fromFuture(yourTest.to[IO])` if you want it to execute, or call `.void` if you want to discard it"
104-
)
105-
case v => v.pure[SyncIO]
106-
}
107-
}
10850
}
10951

11052
object CatsEffectSuite {

Diff for: common/shared/src/main/scala/munit/internal.scala

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2021 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package munit.internal
18+
19+
import cats.effect.{IO, SyncIO}
20+
import cats.syntax.all._
21+
import scala.concurrent.Future
22+
23+
private[munit] object NestingChecks {
24+
// MUnit works by automatically chaining value transforms of shape `Any => Future[Any]`,
25+
// and we rely on this behavior to chain our `IO ~> Future` transform into the rest of MUnit.
26+
//
27+
// Unfortunately, this has an unforeseen consequence in CatsEffectSuite:
28+
// if you return `IO[IO[A]]` by accident, for example by using `map` instead of `flatMap`,
29+
// MUnit will execute both the inner and outer `IO` by applying our `IO` transform twice.
30+
//
31+
// This breaks the `IO` mental model, and can lead to very surprising behaviour, e.g see:
32+
// https://github.com/typelevel/munit-cats-effect/issues/159.
33+
//
34+
// This method checks for such a case, and fails the test with an actionable message.
35+
def checkNestingIO(fa: IO[_]): IO[Any] = {
36+
def err(msg: String) = IO.raiseError[Any](new Exception(msg))
37+
38+
fa.flatMap {
39+
case _: IO[_] =>
40+
err(
41+
"your test returns an `IO[IO[_]]`, which means the inner `IO` will not execute." ++
42+
" Call `.flatten` if you want it to execute, or `.void` if you want to discard it"
43+
)
44+
case _: SyncIO[_] =>
45+
err(
46+
"your test returns an `IO[SyncIO[_]]`, which means the inner `SyncIO` will not execute." ++
47+
" Call `.flatMap(_.to[IO]) if you want it to execute, or `.void` if you want to discard it"
48+
)
49+
case _: Future[_] =>
50+
err(
51+
"your test returns an `IO[Future[_]]`, which means the inner `Future` might not execute." ++
52+
" Surround it with `IO.fromFuture` if you want it to execute, or call `.void` if you want to discard it"
53+
)
54+
case v => v.pure[IO]
55+
}
56+
}
57+
58+
// same as above, but for SyncIO
59+
def checkNestingSyncIO(fa: SyncIO[_]): SyncIO[Any] = {
60+
def err(msg: String) = SyncIO.raiseError[Any](new Exception(msg))
61+
62+
fa.flatMap {
63+
case _: IO[_] =>
64+
err(
65+
"your test returns a `SyncIO[IO[_]]`, which means the inner `IO` will not execute." ++
66+
" Call `.to[IO].flatten` if you want it to execute, or `.void` if you want to discard it"
67+
)
68+
case _: SyncIO[_] =>
69+
err(
70+
"your test returns a `SyncIO[SyncIO[_]]`, which means the inner `SyncIO` will not execute." ++
71+
" Call `.flatten` if you want it to execute, or `.void` if you want to discard it"
72+
)
73+
case _: Future[_] =>
74+
err(
75+
"your test returns a `SyncIO[Future[_]]`, which means the inner `Future` might not execute." ++
76+
" Change it to `IO.fromFuture(yourTest.to[IO])` if you want it to execute, or call `.void` if you want to discard it"
77+
)
78+
case v => v.pure[SyncIO]
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)