From e94148a366d36fe29f048a5257799e38bdd23525 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 10 Sep 2015 09:10:07 -0400 Subject: [PATCH 1/3] Hook Eq up to scalactic/scalatest's equality Fixes #219 This allows us to use matchers like `should ===`. I changed a few random places to do this, but I didn't want to go through the effort of changing tons of places until this PR was reviewed/merged. I think we could clean a lot of things up now by having CatsSuite extend GeneratorDrivenPropertyChecks, which will allow using matchers like `should ===` inside of `forAll` properties. --- .../test/scala/cats/state/StateTTests.scala | 10 +++---- .../test/scala/cats/tests/CatsEquality.scala | 29 +++++++++++++++++++ .../src/test/scala/cats/tests/CatsSuite.scala | 10 ++++--- .../src/test/scala/cats/tests/EvalTests.scala | 2 +- .../scala/cats/tests/StreamingTTests.scala | 2 +- .../test/scala/cats/tests/SyntaxTests.scala | 4 ++- 6 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 tests/shared/src/test/scala/cats/tests/CatsEquality.scala diff --git a/state/src/test/scala/cats/state/StateTTests.scala b/state/src/test/scala/cats/state/StateTTests.scala index 96a0ac79bb..73c97c0a2a 100644 --- a/state/src/test/scala/cats/state/StateTTests.scala +++ b/state/src/test/scala/cats/state/StateTTests.scala @@ -10,31 +10,31 @@ class StateTTests extends CatsSuite { import StateTTests._ test("basic state usage"){ - assert(add1.run(1).run == (2 -> 1)) + add1.run(1).run should === (2 -> 1) } test("traversing state is stack-safe"){ val ns = (0 to 100000).toList val x = ns.traverseU(_ => add1) - assert(x.runS(0).run == 100001) + x.runS(0).run should === (100001) } test("State.pure and StateT.pure are consistent")(check { forAll { (s: String, i: Int) => val state: State[String, Int] = State.pure(i) val stateT: State[String, Int] = StateT.pure(i) - state.run(s).run == stateT.run(s).run + state.run(s).run === stateT.run(s).run } }) test("Apply syntax is usable on State") { val x = add1 *> add1 - assert(x.runS(0).run == 2) + x.runS(0).run should === (2) } test("Singleton and instance inspect are consistent")(check { forAll { (s: String, i: Int) => - State.inspect[Int, String](_.toString).run(i).run == + State.inspect[Int, String](_.toString).run(i).run === State.pure[Int, Unit](()).inspect(_.toString).run(i).run } }) diff --git a/tests/shared/src/test/scala/cats/tests/CatsEquality.scala b/tests/shared/src/test/scala/cats/tests/CatsEquality.scala new file mode 100644 index 0000000000..a45dc9a4e6 --- /dev/null +++ b/tests/shared/src/test/scala/cats/tests/CatsEquality.scala @@ -0,0 +1,29 @@ +package cats +package tests + +import org.scalactic._ +import TripleEqualsSupport.AToBEquivalenceConstraint +import TripleEqualsSupport.BToAEquivalenceConstraint + +// The code in this file was taken and only slightly modified from +// https://github.com/bvenners/equality-integration-demo +// Thanks for the great examples, Bill! + +final class CatsEquivalence[T](T: Eq[T]) extends Equivalence[T] { + def areEquivalent(a: T, b: T): Boolean = T.eqv(a, b) +} + +trait LowPriorityStrictCatsConstraints extends TripleEquals { + implicit def lowPriorityCatsCanEqual[A, B](implicit B: Eq[B], ev: A <:< B): CanEqual[A, B] = + new AToBEquivalenceConstraint[A, B](new CatsEquivalence(B), ev) +} + +trait StrictCatsEquality extends LowPriorityStrictCatsConstraints { + override def convertToEqualizer[T](left: T): Equalizer[T] = super.convertToEqualizer[T](left) + implicit override def convertToCheckingEqualizer[T](left: T): CheckingEqualizer[T] = new CheckingEqualizer(left) + override def unconstrainedEquality[A, B](implicit equalityOfA: Equality[A]): CanEqual[A, B] = super.unconstrainedEquality[A, B] + implicit def catsCanEqual[A, B](implicit A: Eq[A], ev: B <:< A): CanEqual[A, B] = + new BToAEquivalenceConstraint[A, B](new CatsEquivalence(A), ev) +} + +object StrictCatsEquality extends StrictCatsEquality diff --git a/tests/shared/src/test/scala/cats/tests/CatsSuite.scala b/tests/shared/src/test/scala/cats/tests/CatsSuite.scala index dffeac5b1a..80b7bb5a19 100644 --- a/tests/shared/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/CatsSuite.scala @@ -2,7 +2,7 @@ package cats package tests import cats.std.AllInstances -import cats.syntax.AllSyntax +import cats.syntax.{AllSyntax, EqOps} import org.scalatest.{ FunSuite, PropSpec, Matchers } import org.scalatest.prop.PropertyChecks import org.typelevel.discipline.scalatest.Discipline @@ -16,13 +16,15 @@ import scala.util.{Failure, Success, Try} * An opinionated stack of traits to improve consistency and reduce * boilerplate in Cats tests. */ -trait CatsSuite extends FunSuite with Matchers with Discipline with AllInstances with AllSyntax with TestInstances { +trait CatsSuite extends FunSuite with Matchers with Discipline with AllInstances with AllSyntax with TestInstances with StrictCatsEquality { implicit override val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration( minSuccessful = Platform.minSuccessful, maxDiscardedFactor = Platform.maxDiscardedFactor) - // disable scalatest's === - override def convertToEqualizer[T](left: T): Equalizer[T] = ??? + + // disable Eq syntax (by making `eqSyntax` not implicit), since it collides + // with scalactic's equality + override def eqSyntax[A: Eq](a: A): EqOps[A] = new EqOps[A](a) } trait CatsProps extends PropSpec with Matchers with PropertyChecks with TestInstances { diff --git a/tests/shared/src/test/scala/cats/tests/EvalTests.scala b/tests/shared/src/test/scala/cats/tests/EvalTests.scala index 9b39cebcee..1756e6ee24 100644 --- a/tests/shared/src/test/scala/cats/tests/EvalTests.scala +++ b/tests/shared/src/test/scala/cats/tests/EvalTests.scala @@ -37,7 +37,7 @@ class EvalTests extends CatsSuite { val (spooky, lz) = init(value) (0 until n).foreach { _ => val result = lz.value - assert(result === value) + result should === (value) spin ^= result.## } assert(spooky.counter == numEvals) diff --git a/tests/shared/src/test/scala/cats/tests/StreamingTTests.scala b/tests/shared/src/test/scala/cats/tests/StreamingTTests.scala index f238e9bcf4..17cea0c2b3 100644 --- a/tests/shared/src/test/scala/cats/tests/StreamingTTests.scala +++ b/tests/shared/src/test/scala/cats/tests/StreamingTTests.scala @@ -50,6 +50,6 @@ class SpecificStreamingTTests extends CatsSuite { val x = fa.flatMap(f).flatMap(g) val y = fa.flatMap(a => f(a).flatMap(g)) - assert(x === y) + x should === (y) } } diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxTests.scala b/tests/shared/src/test/scala/cats/tests/SyntaxTests.scala index 71ff00640e..098ed3e359 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxTests.scala @@ -1,6 +1,8 @@ package cats package tests +import cats.std.AllInstances +import cats.syntax.AllSyntax import algebra.laws.GroupLaws import cats.functor.{Invariant, Contravariant} import cats.laws.discipline.SerializableTests @@ -27,7 +29,7 @@ import scala.reflect.runtime.universe.TypeTag * * None of these tests should ever run, or do any runtime checks. */ -class SyntaxTests extends CatsSuite with PropertyChecks { +class SyntaxTests extends AllInstances with AllSyntax { // pretend we have a value of type A def mock[A]: A = ??? From 3329070855446cc97b40bb89c72a9be4e4adbc93 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 15 Sep 2015 07:26:07 -0400 Subject: [PATCH 2/3] Ignore missing-interpolator warnings For some reason we are getting the following error in the `free` project: ``` possible missing interpolator: detected interpolated identifier `$conforms` ``` I think one of our macros/plugins is doing this. It would be nice to figure out where it's coming from, but this should work around it for now. --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bff9d42636..eb38f9b9b3 100644 --- a/build.sbt +++ b/build.sbt @@ -274,7 +274,7 @@ lazy val commonScalacOptions = Seq( "-language:experimental.macros", "-unchecked", "-Xfatal-warnings", - "-Xlint", + "-Xlint:-missing-interpolator,_", "-Yinline-warnings", "-Yno-adapted-args", "-Ywarn-dead-code", From d8e565f4451512dfe0a882785665f7f64dfef59f Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 15 Sep 2015 08:45:53 -0400 Subject: [PATCH 3/3] Fix test compile error --- build.sbt | 2 +- free/src/test/scala/cats/free/CoyonedaTests.scala | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index eb38f9b9b3..bff9d42636 100644 --- a/build.sbt +++ b/build.sbt @@ -274,7 +274,7 @@ lazy val commonScalacOptions = Seq( "-language:experimental.macros", "-unchecked", "-Xfatal-warnings", - "-Xlint:-missing-interpolator,_", + "-Xlint", "-Yinline-warnings", "-Yno-adapted-args", "-Ywarn-dead-code", diff --git a/free/src/test/scala/cats/free/CoyonedaTests.scala b/free/src/test/scala/cats/free/CoyonedaTests.scala index 01df39adc1..3193bc12c9 100644 --- a/free/src/test/scala/cats/free/CoyonedaTests.scala +++ b/free/src/test/scala/cats/free/CoyonedaTests.scala @@ -32,14 +32,12 @@ class CoyonedaTests extends CatsSuite { }) test("transform and run is same as applying natural trans") { - assert { val nt = new NaturalTransformation[Option, List] { def apply[A](fa: Option[A]): List[A] = fa.toList } val o = Option("hello") val c = Coyoneda.lift(o) - c.transform(nt).run === nt(o) - } + c.transform(nt).run should === (nt(o)) } }