Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some StateT/State tests #707

Merged
merged 1 commit into from
Dec 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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:_*)
Expand Down
38 changes: 26 additions & 12 deletions free/src/test/scala/cats/free/FreeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, ?]]))
Expand Down Expand Up @@ -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))
}
}
2 changes: 2 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
58 changes: 53 additions & 5 deletions state/src/test/scala/cats/state/StateTTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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))
}
1 change: 0 additions & 1 deletion tests/src/test/scala/cats/tests/FunctionTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]))

Expand Down