From e4e4618e0515453fc091e1fd8ca3beacb18091bf Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 1 Dec 2015 21:34:01 -0800 Subject: [PATCH] Add some StateT/State tests --- build.sbt | 2 +- free/src/test/scala/cats/free/FreeTests.scala | 38 ++++++++---- .../cats/laws/discipline/Arbitrary.scala | 2 + .../test/scala/cats/state/StateTTests.scala | 58 +++++++++++++++++-- .../test/scala/cats/tests/FunctionTests.scala | 1 - 5 files changed, 82 insertions(+), 19 deletions(-) diff --git a/build.sbt b/build.sbt index f4f738fbbf..600a3ff05b 100644 --- a/build.sbt +++ b/build.sbt @@ -153,7 +153,7 @@ lazy val freeJVM = free.jvm lazy val freeJS = free.js lazy val state = crossProject.crossType(CrossType.Pure) - .dependsOn(macros, core, free, tests % "test-internal -> test") + .dependsOn(macros, core, free % "compile-internal;test-internal -> test", tests % "test-internal -> test") .settings(moduleName := "cats-state") .settings(catsSettings:_*) .jsSettings(commonJsSettings:_*) diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 01770147e6..28ded453b7 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -4,21 +4,11 @@ package free import cats.arrow.NaturalTransformation import cats.tests.CatsSuite import cats.laws.discipline.{MonadTests, SerializableTests} +import cats.laws.discipline.arbitrary.function0Arbitrary import org.scalacheck.{Arbitrary, Gen} class FreeTests extends CatsSuite { - - implicit def freeArbitrary[F[_], A](implicit F: Arbitrary[F[A]], A: Arbitrary[A]): Arbitrary[Free[F, A]] = - Arbitrary( - Gen.oneOf( - A.arbitrary.map(Free.pure[F, A]), - F.arbitrary.map(Free.liftF[F, A]))) - - implicit def freeEq[S[_]: Monad, A](implicit SA: Eq[S[A]]): Eq[Free[S, A]] = - new Eq[Free[S, A]] { - def eqv(a: Free[S, A], b: Free[S, A]): Boolean = - SA.eqv(a.runM(identity), b.runM(identity)) - } + import FreeTests._ checkAll("Free[Option, ?]", MonadTests[Free[Option, ?]].monad[Int, Int, Int]) checkAll("Monad[Free[Option, ?]]", SerializableTests.serializable(Monad[Free[Option, ?]])) @@ -51,3 +41,27 @@ class FreeTests extends CatsSuite { assert(10000 == a(0).foldMap(runner)) } } + +object FreeTests extends FreeTestsInstances { + import cats.std.function._ + + implicit def trampolineArbitrary[A:Arbitrary]: Arbitrary[Trampoline[A]] = + freeArbitrary[Function0, A] + + implicit def trampolineEq[A:Eq]: Eq[Trampoline[A]] = + freeEq[Function0, A] +} + +sealed trait FreeTestsInstances { + implicit def freeArbitrary[F[_], A](implicit F: Arbitrary[F[A]], A: Arbitrary[A]): Arbitrary[Free[F, A]] = + Arbitrary( + Gen.oneOf( + A.arbitrary.map(Free.pure[F, A]), + F.arbitrary.map(Free.liftF[F, A]))) + + implicit def freeEq[S[_]: Monad, A](implicit SA: Eq[S[A]]): Eq[Free[S, A]] = + new Eq[Free[S, A]] { + def eqv(a: Free[S, A], b: Free[S, A]): Boolean = + SA.eqv(a.runM(identity), b.runM(identity)) + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index ee71f8d6aa..ec5da2481b 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -116,6 +116,8 @@ object arbitrary extends ArbitraryInstances0 { implicit def showArbitrary[A: Arbitrary]: Arbitrary[Show[A]] = Arbitrary(Show.fromToString[A]) + implicit def function0Arbitrary[A: Arbitrary]: Arbitrary[() => A] = + Arbitrary(getArbitrary[A].map(() => _)) } private[discipline] sealed trait ArbitraryInstances0 { diff --git a/state/src/test/scala/cats/state/StateTTests.scala b/state/src/test/scala/cats/state/StateTTests.scala index 6e719f26d5..19ca850f98 100644 --- a/state/src/test/scala/cats/state/StateTTests.scala +++ b/state/src/test/scala/cats/state/StateTTests.scala @@ -2,6 +2,7 @@ package cats package state import cats.tests.CatsSuite +import cats.free.FreeTests._ import cats.laws.discipline.{MonadStateTests, MonoidKTests, SerializableTests} import cats.laws.discipline.eq._ import org.scalacheck.{Arbitrary, Gen} @@ -39,18 +40,65 @@ class StateTTests extends CatsSuite { } } + test("runEmpty, runEmptyS, and runEmptyA consistent"){ + forAll { (f: StateT[List, Long, Int]) => + (f.runEmptyS zip f.runEmptyA) should === (f.runEmpty) + } + } + + test("modify identity is a noop"){ + forAll { (f: StateT[List, Long, Int]) => + f.modify(identity) should === (f) + } + } + + test("modify modifies state"){ + forAll { (f: StateT[List, Long, Int], g: Long => Long, initial: Long) => + f.modify(g).runS(initial) should === (f.runS(initial).map(g)) + } + } + + test("modify doesn't affect A value"){ + forAll { (f: StateT[List, Long, Int], g: Long => Long, initial: Long) => + f.modify(g).runA(initial) should === (f.runA(initial)) + } + } + + test("State.modify equivalent to get then set"){ + forAll { (f: Long => Long) => + val s1 = for { + l <- State.get[Long] + _ <- State.set(f(l)) + } yield () + + val s2 = State.modify(f) + + s1 should === (s2) + } + } + checkAll("StateT[Option, Int, Int]", MonadStateTests[StateT[Option, Int, ?], Int].monadState[Int, Int, Int]) checkAll("MonadState[StateT[Option, ?, ?], Int]", SerializableTests.serializable(MonadState[StateT[Option, Int, ?], Int])) + + checkAll("State[Long, ?]", MonadStateTests[State[Long, ?], Long].monadState[Int, Int, Int]) + checkAll("MonadState[State[Long, ?], Long]", SerializableTests.serializable(MonadState[State[Long, ?], Long])) } -object StateTTests { +object StateTTests extends StateTTestsInstances { + implicit def stateEq[S:Eq:Arbitrary, A:Eq]: Eq[State[S, A]] = + stateTEq[free.Trampoline, S, A] - implicit def stateArbitrary[F[_]: Applicative, S, A](implicit F: Arbitrary[S => F[(S, A)]]): Arbitrary[StateT[F, S, A]] = + implicit def stateArbitrary[S: Arbitrary, A: Arbitrary]: Arbitrary[State[S, A]] = + stateTArbitrary[free.Trampoline, S, A] + + val add1: State[Int, Int] = State(n => (n + 1, n)) +} + +sealed trait StateTTestsInstances { + implicit def stateTArbitrary[F[_]: Applicative, S, A](implicit F: Arbitrary[S => F[(S, A)]]): Arbitrary[StateT[F, S, A]] = Arbitrary(F.arbitrary.map(f => StateT(f))) - implicit def stateEq[F[_], S, A](implicit S: Arbitrary[S], FSA: Eq[F[(S, A)]], F: FlatMap[F]): Eq[StateT[F, S, A]] = + implicit def stateTEq[F[_], S, A](implicit S: Arbitrary[S], FSA: Eq[F[(S, A)]], F: FlatMap[F]): Eq[StateT[F, S, A]] = Eq.by[StateT[F, S, A], S => F[(S, A)]](state => s => state.run(s)) - - val add1: State[Int, Int] = State(n => (n + 1, n)) } diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index 9a4af0c268..867dd3c313 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -11,7 +11,6 @@ import cats.laws.discipline.arbitrary._ import algebra.laws.GroupLaws class FunctionTests extends CatsSuite { - implicit def ev0[A: Arbitrary]: Arbitrary[() => A] = Arbitrary(Arbitrary.arbitrary[A].map { a => () => a }) checkAll("Function0[Int]", BimonadTests[Function0].bimonad[Int, Int, Int]) checkAll("Bimonad[Function0]", SerializableTests.serializable(Bimonad[Function0]))