From 6ef5925fe25b2f3bed97ec77621ab865c1071f95 Mon Sep 17 00:00:00 2001 From: ghostbuster91 Date: Thu, 28 Oct 2021 22:07:05 +0200 Subject: [PATCH 1/2] Convert Diff back to invariant type --- .../scala/com/softwaremill/diffx/Diff.scala | 3 +- .../diffx/scalatest/DiffShouldMatcher.scala | 43 +++++++++++++++++++ .../scalatest/DiffShouldMatcherTest.scala | 30 +++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 scalatest-should/src/main/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcher.scala create mode 100644 scalatest-should/src/test/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcherTest.scala diff --git a/core/src/main/scala/com/softwaremill/diffx/Diff.scala b/core/src/main/scala/com/softwaremill/diffx/Diff.scala index 101f44ca..60e32236 100644 --- a/core/src/main/scala/com/softwaremill/diffx/Diff.scala +++ b/core/src/main/scala/com/softwaremill/diffx/Diff.scala @@ -1,9 +1,10 @@ package com.softwaremill.diffx + import com.softwaremill.diffx.ObjectMatcher.{IterableEntry, MapEntry, SetEntry} import com.softwaremill.diffx.generic.{DiffMagnoliaDerivation, MagnoliaDerivedMacro} import com.softwaremill.diffx.instances._ -trait Diff[-T] { outer => +trait Diff[T] { outer => def apply(left: T, right: T): DiffResult = apply(left, right, DiffContext.Empty) def apply(left: T, right: T, context: DiffContext): DiffResult diff --git a/scalatest-should/src/main/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcher.scala b/scalatest-should/src/main/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcher.scala new file mode 100644 index 00000000..4304a13f --- /dev/null +++ b/scalatest-should/src/main/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcher.scala @@ -0,0 +1,43 @@ +package com.softwaremill.diffx.scalatest + +import com.softwaremill.diffx.{ConsoleColorConfig, Diff} +import org.scalactic.{Prettifier, source} +import org.scalatest.Assertion +import org.scalatest.matchers.should.Matchers +import org.scalatest.matchers.{MatchResult, Matcher} + +trait DiffShouldMatcher extends DiffShouldMatcherImp { + + def matchTo[A](right: A): A = right + + implicit def convertToAnyShouldMatcher[T: Diff]( + any: T + )(implicit pos: source.Position, prettifier: Prettifier): AnyShouldWrapper[T] = + new AnyShouldWrapper[T](any, pos, prettifier) +} + +trait DiffShouldMatcherImp { + class AnyShouldWrapper[T: Diff]( + val leftValue: T, + val pos: source.Position, + val prettifier: Prettifier + ) extends Matchers { + + def should(rightValue: T)(implicit c: ConsoleColorConfig): Assertion = { + Matchers.convertToAnyShouldWrapper[T](leftValue)(pos, prettifier).should(matchTo[T](rightValue)) + } + + private def matchTo[A: Diff](right: A)(implicit c: ConsoleColorConfig): Matcher[A] = { left => + val result = Diff[A].apply(left, right) + if (!result.isIdentical) { + val diff = + result.show().split('\n').mkString(Console.RESET, s"${Console.RESET}\n${Console.RESET}", Console.RESET) + MatchResult(matches = false, s"Matching error:\n$diff", "") + } else { + MatchResult(matches = true, "", "") + } + } + } +} + +object DiffShouldMatcher extends DiffShouldMatcher diff --git a/scalatest-should/src/test/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcherTest.scala b/scalatest-should/src/test/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcherTest.scala new file mode 100644 index 00000000..b594b240 --- /dev/null +++ b/scalatest-should/src/test/scala/com/softwaremill/diffx/scalatest/DiffShouldMatcherTest.scala @@ -0,0 +1,30 @@ +package com.softwaremill.diffx.scalatest + +import com.softwaremill.diffx.generic.auto._ +import org.scalatest.OptionValues +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class DiffShouldMatcherTest extends AnyFlatSpec with Matchers with DiffShouldMatcher with OptionValues { + val right: Foo = Foo( + Bar("asdf", 5, Map("a" -> 2)), + List(123, 1234), + Some(Bar("asdf", 5, Map("a" -> 2))) + ) + val left: Foo = Foo( + Bar("asdf", 66, Map("b" -> 3)), + List(1234), + Some(right) + ) + + ignore should "work" in { + left should matchTo(right) + } + + it should "work with option and some" in { + Option("test") should matchTo(Some("test")) + } +} +sealed trait Parent +case class Bar(s: String, i: Int, ss: Map[String, Int]) extends Parent +case class Foo(bar: Bar, b: List[Int], parent: Option[Parent]) extends Parent From 0b93311bf146a27f755cadcc671c02a183687648 Mon Sep 17 00:00:00 2001 From: ghostbuster91 Date: Thu, 28 Oct 2021 22:07:35 +0200 Subject: [PATCH 2/2] Create new modules for scalatest specialized for matchers --- build.sbt | 42 +++++++++++++++++-- .../diffx/scalatest/DiffMustMatcher.scala | 41 ++++++++++++++++++ .../diffx/scalatest/DiffMustMatcherTest.scala | 30 +++++++++++++ .../diffx/scalatest/DiffMatcher.scala | 5 ++- .../diffx/scalatest/DiffMatcherTest.scala | 5 ++- 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 scalatest-must/src/main/scala/com/softwaremill/diffx/scalatest/DiffMustMatcher.scala create mode 100644 scalatest-must/src/test/scala/com/softwaremill/diffx/scalatest/DiffMustMatcherTest.scala diff --git a/build.sbt b/build.sbt index f3e53b68..60b36a85 100644 --- a/build.sbt +++ b/build.sbt @@ -68,7 +68,43 @@ lazy val core = (projectMatrix in file("core")) scalaVersions = List(scala212, scala213) ) -lazy val scalatest = (projectMatrix in file("scalatest")) +lazy val scalatestMust = (projectMatrix in file("scalatest-must")) + .settings(commonSettings) + .settings( + name := "diffx-scalatest-must", + libraryDependencies ++= Seq( + "org.scalatest" %%% "scalatest-mustmatchers" % scalatestVersion, + "org.scalatest" %%% "scalatest-matchers-core" % scalatestVersion, + "org.scalatest" %%% "scalatest-flatspec" % scalatestVersion % Test + ) + ) + .dependsOn(core) + .jvmPlatform( + scalaVersions = List(scala212, scala213) + ) + .jsPlatform( + scalaVersions = List(scala212, scala213) + ) + +lazy val scalatestShould = (projectMatrix in file("scalatest-should")) + .settings(commonSettings) + .settings( + name := "diffx-scalatest-should", + libraryDependencies ++= Seq( + "org.scalatest" %%% "scalatest-shouldmatchers" % scalatestVersion, + "org.scalatest" %%% "scalatest-matchers-core" % scalatestVersion, + "org.scalatest" %%% "scalatest-flatspec" % scalatestVersion % Test + ) + ) + .dependsOn(core) + .jvmPlatform( + scalaVersions = List(scala212, scala213) + ) + .jsPlatform( + scalaVersions = List(scala212, scala213) + ) + +lazy val scalatestLegacy = (projectMatrix in file("scalatest")) .settings(commonSettings) .settings( name := "diffx-scalatest", @@ -209,14 +245,14 @@ lazy val docs = (projectMatrix in file("generated-docs")) // important: it must ), mdocOut := file("generated-docs/out") ) - .dependsOn(core, scalatest, specs2, utest, refined, tagging, cats, munit) + .dependsOn(core, scalatestShould, specs2, utest, refined, tagging, cats, munit) .jvmPlatform(scalaVersions = List(scala213)) val testJVM = taskKey[Unit]("Test JVM projects") val testJS = taskKey[Unit]("Test JS projects") val allAggregates = - core.projectRefs ++ scalatest.projectRefs ++ + core.projectRefs ++ scalatestMust.projectRefs ++ scalatestShould.projectRefs ++ scalatestLegacy.projectRefs ++ specs2.projectRefs ++ utest.projectRefs ++ cats.projectRefs ++ refined.projectRefs ++ tagging.projectRefs ++ docs.projectRefs ++ munit.projectRefs diff --git a/scalatest-must/src/main/scala/com/softwaremill/diffx/scalatest/DiffMustMatcher.scala b/scalatest-must/src/main/scala/com/softwaremill/diffx/scalatest/DiffMustMatcher.scala new file mode 100644 index 00000000..6227b317 --- /dev/null +++ b/scalatest-must/src/main/scala/com/softwaremill/diffx/scalatest/DiffMustMatcher.scala @@ -0,0 +1,41 @@ +package com.softwaremill.diffx.scalatest + +import com.softwaremill.diffx.{ConsoleColorConfig, Diff} +import org.scalactic.{Prettifier, source} +import org.scalatest.Assertion +import org.scalatest.matchers.must.Matchers +import org.scalatest.matchers.{MatchResult, Matcher} + +trait DiffMustMatcher { + + def matchTo[A](right: A): A = right + + implicit def convertToMustWrapper[T: Diff]( + any: T + )(implicit pos: source.Position, prettifier: Prettifier): AnyMustWrapper[T] = + new AnyMustWrapper[T](any, pos, prettifier) + + class AnyMustWrapper[T: Diff]( + val leftValue: T, + val pos: source.Position, + val prettifier: Prettifier + ) extends Matchers { + + def must(rightValue: T)(implicit c: ConsoleColorConfig): Assertion = { + Matchers.convertToAnyMustWrapper(leftValue)(pos, prettifier).must(matchTo(rightValue)) + } + + private def matchTo[A: Diff](right: A)(implicit c: ConsoleColorConfig): Matcher[A] = { left => + val result = Diff[A].apply(left, right) + if (!result.isIdentical) { + val diff = + result.show().split('\n').mkString(Console.RESET, s"${Console.RESET}\n${Console.RESET}", Console.RESET) + MatchResult(matches = false, s"Matching error:\n$diff", "") + } else { + MatchResult(matches = true, "", "") + } + } + } +} + +object DiffMustMatcher extends DiffMustMatcher diff --git a/scalatest-must/src/test/scala/com/softwaremill/diffx/scalatest/DiffMustMatcherTest.scala b/scalatest-must/src/test/scala/com/softwaremill/diffx/scalatest/DiffMustMatcherTest.scala new file mode 100644 index 00000000..7d7541a7 --- /dev/null +++ b/scalatest-must/src/test/scala/com/softwaremill/diffx/scalatest/DiffMustMatcherTest.scala @@ -0,0 +1,30 @@ +package com.softwaremill.diffx.scalatest + +import com.softwaremill.diffx.generic.auto._ +import org.scalatest.OptionValues +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.must.Matchers + +class DiffMatcherTest extends AnyFlatSpec with Matchers with DiffMustMatcher with OptionValues { + val right: Foo = Foo( + Bar("asdf", 5, Map("a" -> 2)), + List(123, 1234), + Some(Bar("asdf", 5, Map("a" -> 2))) + ) + val left: Foo = Foo( + Bar("asdf", 66, Map("b" -> 3)), + List(1234), + Some(right) + ) + + ignore should "work" in { + left must matchTo(right) + } + + it should "work with option and some" in { + Option("test") must matchTo(Some("test")) + } +} +sealed trait Parent +case class Bar(s: String, i: Int, ss: Map[String, Int]) extends Parent +case class Foo(bar: Bar, b: List[Int], parent: Option[Parent]) extends Parent diff --git a/scalatest/src/main/scala/com/softwaremill/diffx/scalatest/DiffMatcher.scala b/scalatest/src/main/scala/com/softwaremill/diffx/scalatest/DiffMatcher.scala index 8ba7bdff..b332b9bc 100644 --- a/scalatest/src/main/scala/com/softwaremill/diffx/scalatest/DiffMatcher.scala +++ b/scalatest/src/main/scala/com/softwaremill/diffx/scalatest/DiffMatcher.scala @@ -3,11 +3,13 @@ package com.softwaremill.diffx.scalatest import com.softwaremill.diffx.{ConsoleColorConfig, Diff} import org.scalatest.matchers.{MatchResult, Matcher} +@deprecated("Use DiffShouldMatcher or DiffMustMatcher instead") trait DiffMatcher { def matchTo[A: Diff](right: A)(implicit c: ConsoleColorConfig): Matcher[A] = { left => val result = Diff[A].apply(left, right) if (!result.isIdentical) { - val diff = result.show().split('\n').mkString(Console.RESET, s"${Console.RESET}\n${Console.RESET}", Console.RESET) + val diff = + result.show().split('\n').mkString(Console.RESET, s"${Console.RESET}\n${Console.RESET}", Console.RESET) MatchResult(matches = false, s"Matching error:\n$diff", "") } else { MatchResult(matches = true, "", "") @@ -15,4 +17,5 @@ trait DiffMatcher { } } +@deprecated("Use DiffShouldMatcher or DiffMustMatcher instead") object DiffMatcher extends DiffMatcher diff --git a/scalatest/src/test/scala/com/softwaremill/diffx/scalatest/DiffMatcherTest.scala b/scalatest/src/test/scala/com/softwaremill/diffx/scalatest/DiffMatcherTest.scala index 3f32ef29..e01caadf 100644 --- a/scalatest/src/test/scala/com/softwaremill/diffx/scalatest/DiffMatcherTest.scala +++ b/scalatest/src/test/scala/com/softwaremill/diffx/scalatest/DiffMatcherTest.scala @@ -1,10 +1,11 @@ package com.softwaremill.diffx.scalatest +import com.softwaremill.diffx.generic.auto._ +import org.scalatest.OptionValues import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -import com.softwaremill.diffx.generic.auto._ -class DiffMatcherTest extends AnyFlatSpec with Matchers with DiffMatcher { +class DiffMatcherTest extends AnyFlatSpec with Matchers with DiffMatcher with OptionValues { val right: Foo = Foo( Bar("asdf", 5, Map("a" -> 2)), List(123, 1234),