Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3b001fa
Try cats-effect-1.0.0 Concurrent ConcurrentEffect
neko-kai Aug 27, 2018
a37d4f9
Almost fix racePair to adhere to cats-effect laws, isolate cases that…
neko-kai Oct 29, 2018
890c0bb
Update to latest changes in ZIO
neko-kai Nov 11, 2018
71695e1
ConcurrentEffect tests
neko-kai Nov 11, 2018
348dec1
Include stub of ConcurrentEffect
neko-kai Nov 11, 2018
7ef4629
Make ioContextShift a global instance wrt RTS
neko-kai Nov 20, 2018
586e8ad
update to latest changes
neko-kai Nov 27, 2018
219eb0c
fmt
neko-kai Nov 27, 2018
7b4571b
remove asyncFRegisterCanBeCancelled override as it was fixed
neko-kai Nov 28, 2018
e2ed60e
Override asyncCancelableReceivesCancelSignal
neko-kai Nov 28, 2018
32918d9
Run Concurrent tests 50 times
neko-kai Nov 28, 2018
e13d242
Override cancelOnBracketReleases with zio version, simplify racePair
neko-kai Nov 28, 2018
ffa8680
cleanup: remove printlns, remove cats laws overrides
neko-kai Nov 29, 2018
6c66774
cleanup: Remove non-working ConcurrentEffect stub
neko-kai Nov 29, 2018
62c39b2
cleanup: depend on cats-effect-1.0.0 outside of laws, fmt
neko-kai Nov 29, 2018
b10d51e
override defaultHandler to reduce stdout noize
neko-kai Nov 30, 2018
ccf36ce
remove erroneous test, remove upTo
neko-kai Nov 30, 2018
3a3c160
Fix #348 Add cats-effecty race. fix tests
neko-kai Nov 30, 2018
d2e59e6
bring back deleted tests
neko-kai Nov 30, 2018
c994871
address review
neko-kai Dec 1, 2018
72af143
Remove raceAttempt
neko-kai Dec 1, 2018
3fe5fae
Merge branch 'master' into feature/cats-effect-1.0.0-concurrent
neko-kai Dec 3, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ lazy val interopCatsLaws = project.module
.settings(
skip in publish := true,
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect-laws" % "1.0.0" % Test,
"org.typelevel" %% "cats-testkit" % "1.3.1" % Test,
"com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.8" % Test
"org.typelevel" %% "cats-effect-laws" % "1.0.0-1182d8c" % Test,
"org.typelevel" %% "cats-testkit" % "1.3.1" % Test,
"com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.8" % Test
),
dependencyOverrides += "org.scalacheck" %% "scalacheck" % "1.13.5" % Test,
scalacOptions in Test ++= Seq("-Yrangepos")
Expand Down
134 changes: 134 additions & 0 deletions core/jvm/src/test/scala/scalaz/zio/RTSSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import org.specs2.concurrent.ExecutionEnv
import scalaz.zio.ExitResult.Cause
import scalaz.zio.ExitResult.Cause.{ Checked, Then, Unchecked }

import scala.util.{ Failure, Success }

class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {

def is = s2"""
Expand Down Expand Up @@ -43,6 +45,7 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
deep uncaught fail $testEvalOfDeepUncaughtFail
catch failing finalizers with fail $testFailOfMultipleFailingFinalizers
catch failing finalizers with terminate $testTerminateOfMultipleFailingFinalizers
run preserves interruption status $testRunInterruptIsInterrupted

RTS finalizers
fail ensuring $testEvalOfFailEnsuring
Expand Down Expand Up @@ -79,8 +82,24 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
RTS concurrency correctness
shallow fork/join identity $testForkJoinIsId
deep fork/join identity $testDeepForkJoinIsId
interrupt of never $testNeverIsInterruptible
asyncPure is interruptible $testAsyncPureIsInterruptible
async is interruptible $testAsyncIsInterruptible
bracket acquire is uninterruptible $testBracketAcquireIsUninterruptible
bracket0 acquire is uninterruptible $testBracket0AcquireIsUninterruptible
bracket use is interruptible $testBracketUseIsInterruptible
bracket0 use is interruptible $testBracket0UseIsInterruptible
bracket release called on interrupt $testBracketReleaseOnInterrupt
bracket0 release called on interrupt $testBracket0ReleaseOnInterrupt
asyncPure creation is interruptible $testAsyncPureCreationIsInterruptible
async0 runs cancel token on interrupt $testAsync0RunsCancelTokenOnInterrupt
redeem + ensuring + interrupt $testRedeemEnsuringInterrupt
supervise fibers $testSupervise
supervise fibers in supervised $testSupervised
supervise fibers in race $testSuperviseRace
supervise fibers in fork $testSuperviseFork
race of fail with success $testRaceChoosesWinner
race of terminate with success $testRaceChoosesWinnerInTerminate
race of fail with fail $testRaceChoosesFailure
race of value & never $testRaceOfValueNever
raceAll of values $testRaceAllOfValues
Expand All @@ -94,6 +113,7 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
reduceAll $testReduceAll
reduceAll Empty List $testReduceAllEmpty
timeout of failure $testTimeoutFailure
timeout of terminate $testTimeoutTerminate

RTS regression tests
deadlock regression 1 $testDeadlockRegression
Expand Down Expand Up @@ -398,6 +418,15 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
test <- r.get
} yield test must_=== true)

def testRunInterruptIsInterrupted =
unsafeRun(for {
p <- Promise.make[Nothing, Unit]
f <- (p.complete(()) *> IO.never).run.fork
_ <- p.get
_ <- f.interrupt
test <- f.observe.map(_.interrupted)
} yield test) must_=== true

def testEvalOfDeepSyncEffect = {
def incLeft(n: Int, ref: Ref[Int]): IO[Throwable, Int] =
if (n <= 0) ref.get
Expand Down Expand Up @@ -660,6 +689,43 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
unsafeRun(io) must_=== 42
}

def testAsyncPureCreationIsInterruptible = {
val io = for {
release <- Promise.make[Nothing, Int]
acquire <- Promise.make[Nothing, Unit]
task = IO.asyncPure[Nothing, Unit] { _ =>
IO.bracket(acquire.complete(()))(_ => release.complete(42).void)(_ => IO.never)
}
fiber <- task.fork
_ <- acquire.get
_ <- fiber.interrupt
a <- release.get
} yield a

unsafeRun(io) must_=== 42
}

def testAsync0RunsCancelTokenOnInterrupt = {
val io = for {
release <- Promise.make[Nothing, Int]
latch = scala.concurrent.Promise[Unit]()
async = IO.async0[Nothing, Nothing] { _ =>
latch.success(()); Async.maybeLater(release.complete(42).void)
}
fiber <- async.fork
_ <- IO.async[Throwable, Unit] { cb =>
latch.future.onComplete {
case Success(a) => cb(ExitResult.succeeded(a))
case Failure(t) => cb(ExitResult.checked(t))
}(scala.concurrent.ExecutionContext.global)
}
_ <- fiber.interrupt
result <- release.get
} yield result

unsafeRun(io) must_=== 42
}

def testBracketUseIsInterruptible = {
val io =
for {
Expand Down Expand Up @@ -689,9 +755,61 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
counter must_=== 2
}

def testSuperviseRace =
unsafeRun(for {
pa <- Promise.make[Nothing, Int]
pb <- Promise.make[Nothing, Int]

p1 <- Promise.make[Nothing, Unit]
p2 <- Promise.make[Nothing, Unit]
f <- (
p1.complete(()).bracket_(pa.complete(1).void)(IO.never) race
p2.complete(()).bracket_(pb.complete(2).void)(IO.never)
).supervised.fork
_ <- p1.get *> p2.get

_ <- f.interrupt
r <- pa.get seq pb.get
} yield r) must_=== (1 -> 2)

def testSuperviseFork =
unsafeRun(for {
pa <- Promise.make[Nothing, Int]
pb <- Promise.make[Nothing, Int]

p1 <- Promise.make[Nothing, Unit]
p2 <- Promise.make[Nothing, Unit]
f <- (
p1.complete(()).bracket_(pa.complete(1).void)(IO.never).fork *>
p2.complete(()).bracket_(pb.complete(2).void)(IO.never).fork *>
IO.never
).supervised.fork
_ <- p1.get *> p2.get

_ <- f.interrupt
r <- pa.get seq pb.get
} yield r) must_=== (1 -> 2)

def testSupervised =
unsafeRun(for {
pa <- Promise.make[Nothing, Int]
pb <- Promise.make[Nothing, Int]
_ <- (for {
p1 <- Promise.make[Nothing, Unit]
p2 <- Promise.make[Nothing, Unit]
_ <- p1.complete(()).bracket_(pa.complete(1).void)(IO.never).fork
_ <- p2.complete(()).bracket_(pb.complete(2).void)(IO.never).fork
_ <- p1.get *> p2.get
} yield ()).supervised
r <- pa.get seq pb.get
} yield r) must_=== (1 -> 2)

def testRaceChoosesWinner =
unsafeRun(IO.fail(42).race(IO.now(24)).attempt) must_=== Right(24)

def testRaceChoosesWinnerInTerminate =
unsafeRun(IO.terminate(new Throwable {}).race(IO.now(24)).attempt) must_=== Right(24)

def testRaceChoosesFailure =
unsafeRun(IO.fail(42).race(IO.fail(42)).attempt) must_=== Left(42)

Expand All @@ -714,6 +832,17 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
24
)

def testRaceBothInterruptsLoser =
unsafeRun(for {
s <- Semaphore(0L)
effect <- Promise.make[Nothing, Int]
winner = s.acquire *> IO.async[Throwable, Unit](_(ExitResult.succeeded(())))
loser = IO.bracket(s.release)(_ => effect.complete(42).void)(_ => IO.never)
race = winner raceBoth loser
_ <- race.attempt
b <- effect.get
} yield b) must_=== 42

def testRepeatedPar = {
def countdown(n: Int): IO[Nothing, Int] =
if (n == 0) IO.now(0)
Expand Down Expand Up @@ -753,6 +882,11 @@ class RTSSpec(implicit ee: ExecutionEnv) extends AbstractRTSSpec {
IO.fail("Uh oh").timeout(1.hour)
) must (throwA[FiberFailure])

def testTimeoutTerminate =
unsafeRunSync(
IO.terminate(ExampleError).timeout(1.hour): IO[Nothing, Option[Int]]
) must_=== ExitResult.unchecked(ExampleError)

def testDeadlockRegression = {

import java.util.concurrent.Executors
Expand Down
15 changes: 8 additions & 7 deletions core/shared/src/main/scala/scalaz/zio/IO.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Copyright (C) 2017-2018 John A. De Goes. All rights reserved.
package scalaz.zio

import scalaz.zio.ExitResult.Cause

import scala.annotation.switch
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scalaz.zio.ExitResult.Cause
import scala.concurrent.duration._

/**
* An `IO[E, A]` ("Eye-Oh of Eeh Aye") is an immutable data structure that
Expand Down Expand Up @@ -152,12 +153,12 @@ sealed abstract class IO[+E, +A] extends Serializable { self =>
(exit, right) =>
exit.redeem[E1, Either[A, B]](
_ => right.join.map(Right(_)),
a => IO.now(Left(a)) <* right.interrupt
a => IO.nowLeft(a) <* right.interrupt
),
(exit, left) =>
exit.redeem[E1, Either[A, B]](
_ => left.join.map(Left(_)),
b => IO.now(Right(b)) <* left.interrupt
b => IO.nowRight(b) <* left.interrupt
)
)

Expand Down Expand Up @@ -520,7 +521,7 @@ sealed abstract class IO[+E, +A] extends Serializable { self =>
e => orElse(e, last.map(_())).map(Left(_)),
a =>
schedule.update(a, state, clock).flatMap { step =>
if (!step.cont) IO.now(Right(step.finish()))
if (!step.cont) IO.nowRight(step.finish())
else IO.now(step.state).delay(step.delay).flatMap(s => loop(Some(step.finish), s))
}
)
Expand Down Expand Up @@ -566,7 +567,7 @@ sealed abstract class IO[+E, +A] extends Serializable { self =>
if (decision.cont) IO.sleep(decision.delay) *> loop(decision.state)
else orElse(err, decision.finish()).map(Left(_))
),
succ => IO.now(Right(succ))
succ => IO.nowRight(succ)
)

policy.initial(clock).flatMap(loop)
Expand Down Expand Up @@ -602,7 +603,7 @@ sealed abstract class IO[+E, +A] extends Serializable { self =>
* }}}
*/
final def timeout0[B](z: B)(f: A => B)(duration: Duration): IO[E, B] =
self.map(f).sandboxWith(io => IO.absolve(io.attempt race IO.now(Right(z)).delay(duration)))
self.map(f).sandboxWith(io => IO.absolve(io.attempt race IO.nowRight(z).delay(duration)))

/**
* Flattens a nested action with a specified duration.
Expand Down
47 changes: 15 additions & 32 deletions interop-cats-laws/src/test/scala/scalaz/zio/interop/catzSpec.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package scalaz.zio
package interop

import java.io.{ ByteArrayOutputStream, PrintStream }

import cats.Eq
import cats.effect.laws.discipline.{ EffectTests, Parameters }
import cats.effect.laws.discipline.arbitrary._
import cats.effect.laws.discipline.{ ConcurrentTests, EffectTests, Parameters }
import cats.effect.laws.util.{ TestContext, TestInstances }
import cats.implicits._
import cats.laws.discipline.{ AlternativeTests, BifunctorTests, MonadErrorTests, ParallelTests, SemigroupKTests }
Expand All @@ -17,8 +15,6 @@ import org.typelevel.discipline.Laws
import org.typelevel.discipline.scalatest.Discipline
import scalaz.zio.interop.catz._

import scala.util.control.NonFatal

class catzSpec
extends FunSuite
with BeforeAndAfterAll
Expand All @@ -29,41 +25,21 @@ class catzSpec
with GenIO
with RTS {

/**
* Silences `System.err`, only printing the output in case exceptions are
* thrown by the executed `thunk`.
*/
def silenceSystemErr[A](thunk: => A): A = synchronized {
// Silencing System.err
val oldErr = System.err
val outStream = new ByteArrayOutputStream()
val fakeErr = new PrintStream(outStream)
System.setErr(fakeErr)
try {
val result = thunk
System.setErr(oldErr)
result
} catch {
case NonFatal(e) =>
System.setErr(oldErr)
// In case of errors, print whatever was caught
fakeErr.close()
val out = outStream.toString("utf-8")
if (out.nonEmpty) oldErr.println(out)
throw e
}
}
override def defaultHandler: ExitResult.Cause[Any] => IO[Nothing, Unit] = _ => IO.unit

def checkAllAsync(name: String, f: TestContext => Laws#RuleSet): Unit = {
val context = TestContext()
val ruleSet = f(context)

for ((id, prop) ← ruleSet.all.properties)
test(name + "." + id) {
silenceSystemErr(check(prop))
check(prop)
}
}

(1 to 50).foreach { s =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to run those 50 times anymore, do we?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully we don't. But, these tests are cheap enough to run and each iteration gives a slightly higher chance of spotting a regression. I know this is a defensive code smell, but IMHO justified by the domain - these are effectively integration tests, that also highly depend on JVM behavior and on upstream cats-effect.

checkAllAsync(s"Concurrent[Task] $s", (_) => ConcurrentTests[Task].concurrent[Int, Int, Int])
}
checkAllAsync("Effect[Task]", implicit e => EffectTests[Task].effect[Int, Int, Int])
checkAllAsync("MonadError[IO[Int, ?]]", (_) => MonadErrorTests[IO[Int, ?], Int].monadError[Int, Int, Int])
checkAllAsync("Alternative[IO[Int, ?]]", (_) => AlternativeTests[IO[Int, ?]].alternative[Int, Int, Int])
Expand All @@ -77,8 +53,15 @@ class catzSpec

implicit def catsEQ[E, A: Eq]: Eq[IO[E, A]] =
new Eq[IO[E, A]] {
def eqv(io1: IO[E, A], io2: IO[E, A]): Boolean =
unsafeRun(io1.attempt) === unsafeRun(io2.attempt)
def eqv(io1: IO[E, A], io2: IO[E, A]): Boolean = {
val v1 = unsafeRunSync(io1)
val v2 = unsafeRunSync(io2)
val res = v1 === v2
if (!res) {
println(s"Mismatch: $v1 != $v2")
}
res
}
}

implicit def catsParEQ[E, A: Eq]: Eq[ParIO[E, A]] =
Expand Down
Loading