From 48026cbb411edf05a08da19fbedcf7364d59be44 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 28 Mar 2020 11:19:32 -0400 Subject: [PATCH 1/7] Add support for Hedgehog property testing --- build.sbt | 25 +++- .../scala/munit/HedgehogFailException.scala | 38 ++++++ .../src/main/scala/munit/HedgehogSuite.scala | 113 ++++++++++++++++++ .../scala/munit/HedgehogFrameworkSuite.scala | 72 +++++++++++ .../munit/ScalaCheckFrameworkSuite.scala | 2 +- .../src/test/scala/munit/FrameworkSuite.scala | 3 +- 6 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala create mode 100644 munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala create mode 100644 tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala diff --git a/build.sbt b/build.sbt index 47465615..625bfa94 100644 --- a/build.sbt +++ b/build.sbt @@ -223,8 +223,31 @@ lazy val munitScalacheckJVM = munitScalacheck.jvm lazy val munitScalacheckJS = munitScalacheck.js lazy val munitScalacheckNative = munitScalacheck.native +lazy val munitHedgehog = crossProject(JSPlatform, JVMPlatform, NativePlatform) + .in(file("munit-hedgehog")) + .dependsOn(munit) + .settings( + moduleName := "munit-hedgehog", + sharedSettings, + crossScalaVersions := List(scala213, scala212, scala211, dotty), + resolvers += "bintray-scala-hedgehog".at("https://dl.bintray.com/hedgehogqa/scala-hedgehog"), + libraryDependencies += ("qa.hedgehog" %% "hedgehog-runner" % "97854199ef795a5dfba15478fd9abe66035ddea2").withDottyCompat(scalaVersion.value) + ) + .jvmSettings( + skip in publish := customScalaJSVersion.isDefined + ) + .nativeConfigure(sharedNativeConfigure) + .nativeSettings( + sharedNativeSettings, + skip in publish := customScalaJSVersion.isDefined + ) + .jsSettings(sharedJSSettings) +lazy val munitHedgehogJVM = munitHedgehog.jvm +lazy val munitHedgehogJS = munitHedgehog.js +lazy val munitHedgehogNative = munitHedgehog.native + lazy val tests = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .dependsOn(munit, munitScalacheck) + .dependsOn(munit, munitScalacheck, munitHedgehog) .enablePlugins(BuildInfoPlugin) .settings( sharedSettings, diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala new file mode 100644 index 00000000..87ee304d --- /dev/null +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala @@ -0,0 +1,38 @@ +package munit + +import scala.util.control.NoStackTrace + +import hedgehog.core.Report +import hedgehog.core.{Failed, GaveUp, OK} +import hedgehog.runner.Test + +class HedgehogFailException(message: String, val report: Report, val seed: Long) + extends Exception(message) + with NoStackTrace + +object HedgehogFailException { + + def fromReport(report: Report, seed: Long): Option[HedgehogFailException] = { + report.status match { + case OK => + None + case Failed(shrinks, log) => + val coverage = Test.renderCoverage(report.coverage, report.tests) + val message = render( + s"Falsified after ${report.tests.value} passed tests and ${shrinks.value} shrinks using seed ${seed}", + log.map(Test.renderLog) ++ coverage + ) + Some(new HedgehogFailException(message, report, seed)) + case GaveUp => + val coverage = Test.renderCoverage(report.coverage, report.tests) + val message = render( + s"Gave up after ${report.tests.value} passed tests using seed value $seed. ${report.discards.value} were discarded", + coverage + ) + Some(new HedgehogFailException(message, report, seed)) + } + } + + private def render(msg: String, extras: List[String]): String = + (msg :: extras.map(e => "> " + e)).mkString("\n") +} \ No newline at end of file diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala new file mode 100644 index 00000000..4154c499 --- /dev/null +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala @@ -0,0 +1,113 @@ +package munit + +import hedgehog._ +import hedgehog.core.{PropertyConfig, PropertyT, Seed} +import hedgehog.predef.Monad + +/** + * Provides the ability to write property based tests using the Hedgehog library. + * + * Properties are defined by calling one of the various `property` or `propertyF` + * methods and passing a `PropertyT[Result]` or `PropertyT[F[Result]]` respectively. + * For example: + * {{{ + * property("additive identity") { + * Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)).forAll.map { n => + * assertEquals(n + 0, n) + * } + * } + * }}} + * + * Note both Hedgehog `Result` and munit assertions are supported. + */ +trait HedgehogSuite extends FunSuite { + + /** + * Provides the default configuration for running property tests. + * This can be overriden to change the default or can be changed on + * a test-by-test basis by using an overload of `property` and `propertyF`. + */ + lazy val propertyConfig: PropertyConfig = PropertyConfig.default + lazy val propertySeed: Long = System.currentTimeMillis() + + def property( + name: String, + )(prop: PropertyT[Result])(implicit loc: Location): Unit = + property(name, propertyConfig)(prop) + + def property( + options: TestOptions, + )(prop: PropertyT[Result])(implicit loc: Location): Unit = + property(options, propertyConfig)(prop) + + def property( + name: String, + config: PropertyConfig + )(prop: PropertyT[Result])(implicit loc: Location): Unit = + property(new TestOptions(name, Set.empty, loc), config)(prop) + + def property( + options: TestOptions, + config: PropertyConfig + )(prop: PropertyT[Result])(implicit loc: Location): Unit = + test(options)(check(prop, config, propertySeed)) + + def propertyF[F[_]: Monad]( + name: String, + )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = + propertyF(name, propertyConfig)(prop) + + def propertyF[F[_]: Monad]( + options: TestOptions, + )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = + propertyF(options, propertyConfig)(prop) + + def propertyF[F[_]: Monad]( + name: String, + config: PropertyConfig + )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = + propertyF(new TestOptions(name, Set.empty, loc), config)(prop) + + def propertyF[F[_]: Monad]( + options: TestOptions, + config: PropertyConfig + )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = + test(options)(checkF(prop, config, propertySeed)) + + /** + * Checks the supplied `Property[Result]`, throwing a `HedgehogFailException` + * if the property was falsified. + */ + def check( + prop: PropertyT[Result], + config: PropertyConfig = propertyConfig, + seed: Long = propertySeed + )(implicit loc: Location): Unit = { + val report = Property.check(config, prop, Seed.fromLong(seed)) + HedgehogFailException.fromReport(report, seed) match { + case None => () + case Some(t) => throw t + } + } + + /** + * Checks the supplied `PropertyT[F[Result]]`, throwing a `HedgehogFailException` + * if the property was falsified. + * + * Note: the exception is thrown within a call to `map` on the effect type. Hence, + * this should only be used with effect types that handle exceptions thrown from + * `map`. + */ + def checkF[F[_]: Monad]( + prop: PropertyT[F[Result]], + config: PropertyConfig = propertyConfig, + seed: Long = propertySeed + )(implicit loc: Location): F[Unit] = { + ??? // Waiting for https://github.com/hedgehogqa/scala-hedgehog/pull/147 + } + + /** + * Supports writing properties with munit assertions instead of Hedgehog `Result`s. + */ + implicit def unitToResult: Unit => Result = _ => Result.success +} \ No newline at end of file diff --git a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala new file mode 100644 index 00000000..67c1b1fe --- /dev/null +++ b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala @@ -0,0 +1,72 @@ +package munit + +import hedgehog.{Gen, Range, Result} +import hedgehog.core.SuccessCount + +class HedgehogFrameworkSuite extends HedgehogSuite { + + // The default propertySeed changes on each test run so we fix it here so that test failures are always the same + override lazy val propertySeed = 123L + + private val genInt: Gen[Int] = Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)) + + property("result check (true)") { + for { + l1 <- Gen.list(genInt, Range.linear(0, 100)).forAll + l2 <- Gen.list(genInt, Range.linear(0, 100)).forAll + } yield Result.assert(l1.size + l2.size == (l1 ::: l2).size) + } + + property("result check (false)") { + genInt.forAll.map(n => Result.assert(scala.math.sqrt(n * n) == n)) + } + + property("tagged".tag(new Tag("a"))) { + genInt.forAll.map(n => Result.assert(n + 0 == n)) + } + + property("assertions (true)") { + genInt.forAll.map { n => + assertEquals(n * 2, n + n) + assertEquals(n * 0, 0) + } + } + + property("assertions (false)") { + genInt.forAll.map { n => + assertEquals(n * 1, n) + assertEquals(n * n, n) + assertEquals(n + 0, n) + } + } + + property("custom config", config = propertyConfig.copy(testLimit = SuccessCount(1000))) { + genInt.forAll.map(n => assertEquals(n + 0, n)) + } +} + +object HedgehogFrameworkSuite + extends FrameworkTest( + classOf[HedgehogFrameworkSuite], + s"""|==> success munit.HedgehogFrameworkSuite.result check (true) + |==> failure munit.HedgehogFrameworkSuite.result check (false) - Falsified after 0 passed tests and 24 shrinks using seed 123 + |> -1 + |==> success munit.HedgehogFrameworkSuite.tagged + |==> success munit.HedgehogFrameworkSuite.assertions (true) + |==> failure munit.HedgehogFrameworkSuite.assertions (false) - Falsified after 0 passed tests and 24 shrinks using seed 123 + |> -1 + |> munit.FailException: /scala/munit/HedgehogFrameworkSuite.scala:38 + |37: assertEquals(n * 1, n) + |38: assertEquals(n * n, n) + |39: assertEquals(n + 0, n) + |values are not the same + |=> Obtained + |1 + |=> Diff (- obtained, + expected) + |-1 + |+-1 + |\tat munit.Assertions.fail(Assertions.scala:178) + | + |==> success munit.HedgehogFrameworkSuite.custom config + |""".stripMargin + ) diff --git a/tests/shared/src/main/scala/munit/ScalaCheckFrameworkSuite.scala b/tests/shared/src/main/scala/munit/ScalaCheckFrameworkSuite.scala index 42a24ffb..54566b88 100644 --- a/tests/shared/src/main/scala/munit/ScalaCheckFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/ScalaCheckFrameworkSuite.scala @@ -7,7 +7,7 @@ class ScalaCheckFrameworkSuite extends ScalaCheckSuite { // NOTE(gabro): this is needed for making the test output stable for the failed test below. // It also serves as a test for overriding these parameters. - override def scalaCheckTestParameters = + override def scalaCheckTestParameters: org.scalacheck.Test.Parameters = super.scalaCheckTestParameters.withInitialSeed(Seed(123L)) property("boolean check (true)") { diff --git a/tests/shared/src/test/scala/munit/FrameworkSuite.scala b/tests/shared/src/test/scala/munit/FrameworkSuite.scala index a6533e08..e8bd3218 100644 --- a/tests/shared/src/test/scala/munit/FrameworkSuite.scala +++ b/tests/shared/src/test/scala/munit/FrameworkSuite.scala @@ -20,7 +20,8 @@ class FrameworkSuite extends BaseFrameworkSuite { TestTransformFrameworkSuite, ValueTransformCrashFrameworkSuite, ValueTransformFrameworkSuite, - ScalaCheckFrameworkSuite + ScalaCheckFrameworkSuite, + HedgehogFrameworkSuite ) tests.foreach { t => check(t) From 9537fa60775cceeacea819a74183ae15797ed71a Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 28 Mar 2020 11:35:40 -0400 Subject: [PATCH 2/7] Fix 2.11 and 0.23 builds --- .../shared/src/main/scala/munit/HedgehogSuite.scala | 12 +++++++----- .../src/main/scala-2.13/munit/internal/Compat.scala | 1 + .../main/scala-pre-2.13/munit/internal/Compat.scala | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala index 4154c499..d799d74f 100644 --- a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala @@ -4,6 +4,8 @@ import hedgehog._ import hedgehog.core.{PropertyConfig, PropertyT, Seed} import hedgehog.predef.Monad +import munit.internal.Compat._ + /** * Provides the ability to write property based tests using the Hedgehog library. * @@ -31,12 +33,12 @@ trait HedgehogSuite extends FunSuite { lazy val propertySeed: Long = System.currentTimeMillis() def property( - name: String, + name: String )(prop: PropertyT[Result])(implicit loc: Location): Unit = property(name, propertyConfig)(prop) def property( - options: TestOptions, + options: TestOptions )(prop: PropertyT[Result])(implicit loc: Location): Unit = property(options, propertyConfig)(prop) @@ -53,12 +55,12 @@ trait HedgehogSuite extends FunSuite { test(options)(check(prop, config, propertySeed)) def propertyF[F[_]: Monad]( - name: String, + name: String )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = propertyF(name, propertyConfig)(prop) def propertyF[F[_]: Monad]( - options: TestOptions, + options: TestOptions )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = propertyF(options, propertyConfig)(prop) @@ -109,5 +111,5 @@ trait HedgehogSuite extends FunSuite { /** * Supports writing properties with munit assertions instead of Hedgehog `Result`s. */ - implicit def unitToResult: Unit => Result = _ => Result.success + implicit val unitToResult: Conversion[Unit, Result] = _=> Result.Success } \ No newline at end of file diff --git a/munit/shared/src/main/scala-2.13/munit/internal/Compat.scala b/munit/shared/src/main/scala-2.13/munit/internal/Compat.scala index e0606348..2aa896de 100644 --- a/munit/shared/src/main/scala-2.13/munit/internal/Compat.scala +++ b/munit/shared/src/main/scala-2.13/munit/internal/Compat.scala @@ -7,4 +7,5 @@ object Compat { p.productElementNames def collectionClassName(i: Iterable[_]): String = i.asInstanceOf[{ def collectionClassName: String }].collectionClassName + type Conversion[-A, +B] = A => B } diff --git a/munit/shared/src/main/scala-pre-2.13/munit/internal/Compat.scala b/munit/shared/src/main/scala-pre-2.13/munit/internal/Compat.scala index cadb0ab8..39e98361 100644 --- a/munit/shared/src/main/scala-pre-2.13/munit/internal/Compat.scala +++ b/munit/shared/src/main/scala-pre-2.13/munit/internal/Compat.scala @@ -7,4 +7,5 @@ object Compat { Iterator.continually("") def collectionClassName(i: Iterable[_]): String = i.stringPrefix + type Conversion[-A, +B] = A => B } From 5a50759cf4c4b507c9773e4ecd99cae9715798b5 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 28 Mar 2020 11:37:47 -0400 Subject: [PATCH 3/7] Format source --- .../scala/munit/HedgehogFailException.scala | 14 ++--- .../src/main/scala/munit/HedgehogSuite.scala | 60 +++++++++---------- .../scala/munit/HedgehogFrameworkSuite.scala | 10 +++- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala index 87ee304d..3f501748 100644 --- a/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala @@ -19,20 +19,20 @@ object HedgehogFailException { case Failed(shrinks, log) => val coverage = Test.renderCoverage(report.coverage, report.tests) val message = render( - s"Falsified after ${report.tests.value} passed tests and ${shrinks.value} shrinks using seed ${seed}", - log.map(Test.renderLog) ++ coverage - ) + s"Falsified after ${report.tests.value} passed tests and ${shrinks.value} shrinks using seed ${seed}", + log.map(Test.renderLog) ++ coverage + ) Some(new HedgehogFailException(message, report, seed)) case GaveUp => val coverage = Test.renderCoverage(report.coverage, report.tests) val message = render( - s"Gave up after ${report.tests.value} passed tests using seed value $seed. ${report.discards.value} were discarded", - coverage - ) + s"Gave up after ${report.tests.value} passed tests using seed value $seed. ${report.discards.value} were discarded", + coverage + ) Some(new HedgehogFailException(message, report, seed)) } } private def render(msg: String, extras: List[String]): String = (msg :: extras.map(e => "> " + e)).mkString("\n") -} \ No newline at end of file +} diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala index d799d74f..907c62af 100644 --- a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala @@ -7,21 +7,21 @@ import hedgehog.predef.Monad import munit.internal.Compat._ /** - * Provides the ability to write property based tests using the Hedgehog library. - * - * Properties are defined by calling one of the various `property` or `propertyF` - * methods and passing a `PropertyT[Result]` or `PropertyT[F[Result]]` respectively. - * For example: - * {{{ - * property("additive identity") { - * Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)).forAll.map { n => - * assertEquals(n + 0, n) - * } - * } - * }}} - * - * Note both Hedgehog `Result` and munit assertions are supported. - */ + * Provides the ability to write property based tests using the Hedgehog library. + * + * Properties are defined by calling one of the various `property` or `propertyF` + * methods and passing a `PropertyT[Result]` or `PropertyT[F[Result]]` respectively. + * For example: + * {{{ + * property("additive identity") { + * Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)).forAll.map { n => + * assertEquals(n + 0, n) + * } + * } + * }}} + * + * Note both Hedgehog `Result` and munit assertions are supported. + */ trait HedgehogSuite extends FunSuite { /** @@ -77,9 +77,9 @@ trait HedgehogSuite extends FunSuite { test(options)(checkF(prop, config, propertySeed)) /** - * Checks the supplied `Property[Result]`, throwing a `HedgehogFailException` - * if the property was falsified. - */ + * Checks the supplied `Property[Result]`, throwing a `HedgehogFailException` + * if the property was falsified. + */ def check( prop: PropertyT[Result], config: PropertyConfig = propertyConfig, @@ -87,19 +87,19 @@ trait HedgehogSuite extends FunSuite { )(implicit loc: Location): Unit = { val report = Property.check(config, prop, Seed.fromLong(seed)) HedgehogFailException.fromReport(report, seed) match { - case None => () + case None => () case Some(t) => throw t } } /** - * Checks the supplied `PropertyT[F[Result]]`, throwing a `HedgehogFailException` - * if the property was falsified. - * - * Note: the exception is thrown within a call to `map` on the effect type. Hence, - * this should only be used with effect types that handle exceptions thrown from - * `map`. - */ + * Checks the supplied `PropertyT[F[Result]]`, throwing a `HedgehogFailException` + * if the property was falsified. + * + * Note: the exception is thrown within a call to `map` on the effect type. Hence, + * this should only be used with effect types that handle exceptions thrown from + * `map`. + */ def checkF[F[_]: Monad]( prop: PropertyT[F[Result]], config: PropertyConfig = propertyConfig, @@ -109,7 +109,7 @@ trait HedgehogSuite extends FunSuite { } /** - * Supports writing properties with munit assertions instead of Hedgehog `Result`s. - */ - implicit val unitToResult: Conversion[Unit, Result] = _=> Result.Success -} \ No newline at end of file + * Supports writing properties with munit assertions instead of Hedgehog `Result`s. + */ + implicit val unitToResult: Conversion[Unit, Result] = _ => Result.Success +} diff --git a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala index 67c1b1fe..0788d7e9 100644 --- a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala @@ -7,8 +7,9 @@ class HedgehogFrameworkSuite extends HedgehogSuite { // The default propertySeed changes on each test run so we fix it here so that test failures are always the same override lazy val propertySeed = 123L - - private val genInt: Gen[Int] = Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)) + + private val genInt: Gen[Int] = + Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)) property("result check (true)") { for { @@ -40,7 +41,10 @@ class HedgehogFrameworkSuite extends HedgehogSuite { } } - property("custom config", config = propertyConfig.copy(testLimit = SuccessCount(1000))) { + property( + "custom config", + config = propertyConfig.copy(testLimit = SuccessCount(1000)) + ) { genInt.forAll.map(n => assertEquals(n + 0, n)) } } From b17edc2aeb32c9d2967d57232af94b2f644d140e Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 28 Mar 2020 11:39:31 -0400 Subject: [PATCH 4/7] Format sbt --- build.sbt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 625bfa94..cd03dc02 100644 --- a/build.sbt +++ b/build.sbt @@ -230,8 +230,11 @@ lazy val munitHedgehog = crossProject(JSPlatform, JVMPlatform, NativePlatform) moduleName := "munit-hedgehog", sharedSettings, crossScalaVersions := List(scala213, scala212, scala211, dotty), - resolvers += "bintray-scala-hedgehog".at("https://dl.bintray.com/hedgehogqa/scala-hedgehog"), - libraryDependencies += ("qa.hedgehog" %% "hedgehog-runner" % "97854199ef795a5dfba15478fd9abe66035ddea2").withDottyCompat(scalaVersion.value) + resolvers += "bintray-scala-hedgehog".at( + "https://dl.bintray.com/hedgehogqa/scala-hedgehog" + ), + libraryDependencies += ("qa.hedgehog" %% "hedgehog-runner" % "97854199ef795a5dfba15478fd9abe66035ddea2") + .withDottyCompat(scalaVersion.value) ) .jvmSettings( skip in publish := customScalaJSVersion.isDefined From 11ab263d36493990659ad8ae23084f33e49ac8c9 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 28 Mar 2020 11:43:43 -0400 Subject: [PATCH 5/7] Fix Scala.js dependency and fix JVM test --- build.sbt | 2 +- .../src/main/scala/munit/HedgehogFrameworkSuite.scala | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index cd03dc02..374ae3a4 100644 --- a/build.sbt +++ b/build.sbt @@ -233,7 +233,7 @@ lazy val munitHedgehog = crossProject(JSPlatform, JVMPlatform, NativePlatform) resolvers += "bintray-scala-hedgehog".at( "https://dl.bintray.com/hedgehogqa/scala-hedgehog" ), - libraryDependencies += ("qa.hedgehog" %% "hedgehog-runner" % "97854199ef795a5dfba15478fd9abe66035ddea2") + libraryDependencies += ("qa.hedgehog" %%% "hedgehog-runner" % "97854199ef795a5dfba15478fd9abe66035ddea2") .withDottyCompat(scalaVersion.value) ) .jvmSettings( diff --git a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala index 0788d7e9..71361084 100644 --- a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala @@ -59,10 +59,10 @@ object HedgehogFrameworkSuite |==> success munit.HedgehogFrameworkSuite.assertions (true) |==> failure munit.HedgehogFrameworkSuite.assertions (false) - Falsified after 0 passed tests and 24 shrinks using seed 123 |> -1 - |> munit.FailException: /scala/munit/HedgehogFrameworkSuite.scala:38 - |37: assertEquals(n * 1, n) - |38: assertEquals(n * n, n) - |39: assertEquals(n + 0, n) + |> munit.FailException: /scala/munit/HedgehogFrameworkSuite.scala:39 + |38: assertEquals(n * 1, n) + |39: assertEquals(n * n, n) + |40: assertEquals(n + 0, n) |values are not the same |=> Obtained |1 From 2c7e56f88563ee108bbb696b283ae1414de03372 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 28 Mar 2020 11:49:19 -0400 Subject: [PATCH 6/7] Fix tests by removing result stack trace --- .../scala/munit/HedgehogFailException.scala | 17 ++++++++++++++--- .../scala/munit/HedgehogFrameworkSuite.scala | 2 -- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala index 3f501748..02fd9a2a 100644 --- a/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogFailException.scala @@ -2,8 +2,7 @@ package munit import scala.util.control.NoStackTrace -import hedgehog.core.Report -import hedgehog.core.{Failed, GaveUp, OK} +import hedgehog.core._ import hedgehog.runner.Test class HedgehogFailException(message: String, val report: Report, val seed: Long) @@ -20,7 +19,7 @@ object HedgehogFailException { val coverage = Test.renderCoverage(report.coverage, report.tests) val message = render( s"Falsified after ${report.tests.value} passed tests and ${shrinks.value} shrinks using seed ${seed}", - log.map(Test.renderLog) ++ coverage + log.map(renderLog) ++ coverage ) Some(new HedgehogFailException(message, report, seed)) case GaveUp => @@ -35,4 +34,16 @@ object HedgehogFailException { private def render(msg: String, extras: List[String]): String = (msg :: extras.map(e => "> " + e)).mkString("\n") + + // From Hedgehog, but customized to *not* include the stack trace and instead print exception toString + private def renderLog(log: Log): String = { + log match { + case ForAll(name, value) => + s"${name.value}: $value" + case Info(value) => + value + case Error(e) => + e.toString + } + } } diff --git a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala index 71361084..3e63acc0 100644 --- a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala @@ -69,8 +69,6 @@ object HedgehogFrameworkSuite |=> Diff (- obtained, + expected) |-1 |+-1 - |\tat munit.Assertions.fail(Assertions.scala:178) - | |==> success munit.HedgehogFrameworkSuite.custom config |""".stripMargin ) From 13d17ebec97436d361b7fc8bc721312522550238 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Fri, 3 Apr 2020 17:07:05 -0400 Subject: [PATCH 7/7] Review comments --- .../src/main/scala/munit/HedgehogConfig.scala | 5 ++ .../src/main/scala/munit/HedgehogSuite.scala | 56 ++++++------------- .../scala/munit/HedgehogFrameworkSuite.scala | 7 +-- 3 files changed, 26 insertions(+), 42 deletions(-) create mode 100644 munit-hedgehog/shared/src/main/scala/munit/HedgehogConfig.scala diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogConfig.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogConfig.scala new file mode 100644 index 00000000..42deb36f --- /dev/null +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogConfig.scala @@ -0,0 +1,5 @@ +package munit + +import hedgehog.core.PropertyConfig + +case class HedgehogConfig(config: PropertyConfig) extends Tag("HedgehogConfig") \ No newline at end of file diff --git a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala index 907c62af..25920b26 100644 --- a/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala +++ b/munit-hedgehog/shared/src/main/scala/munit/HedgehogSuite.scala @@ -29,61 +29,41 @@ trait HedgehogSuite extends FunSuite { * This can be overriden to change the default or can be changed on * a test-by-test basis by using an overload of `property` and `propertyF`. */ - lazy val propertyConfig: PropertyConfig = PropertyConfig.default - lazy val propertySeed: Long = System.currentTimeMillis() + protected def hedgehogPropertyConfig: PropertyConfig = PropertyConfig.default + protected def hedgehogSeed: Long = System.currentTimeMillis() def property( name: String )(prop: PropertyT[Result])(implicit loc: Location): Unit = - property(name, propertyConfig)(prop) + property(new TestOptions(name, Set.empty, loc))(prop) def property( options: TestOptions - )(prop: PropertyT[Result])(implicit loc: Location): Unit = - property(options, propertyConfig)(prop) - - def property( - name: String, - config: PropertyConfig - )(prop: PropertyT[Result])(implicit loc: Location): Unit = - property(new TestOptions(name, Set.empty, loc), config)(prop) - - def property( - options: TestOptions, - config: PropertyConfig - )(prop: PropertyT[Result])(implicit loc: Location): Unit = - test(options)(check(prop, config, propertySeed)) + )(prop: PropertyT[Result])(implicit loc: Location): Unit = { + val config = options.tags.collectFirst { case HedgehogConfig(config) => config }.getOrElse(hedgehogPropertyConfig) + test(options)(check(prop, config, hedgehogSeed)) + } def propertyF[F[_]: Monad]( name: String )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = - propertyF(name, propertyConfig)(prop) + propertyF(new TestOptions(name, Set.empty, loc))(prop) def propertyF[F[_]: Monad]( options: TestOptions - )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = - propertyF(options, propertyConfig)(prop) - - def propertyF[F[_]: Monad]( - name: String, - config: PropertyConfig - )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = - propertyF(new TestOptions(name, Set.empty, loc), config)(prop) - - def propertyF[F[_]: Monad]( - options: TestOptions, - config: PropertyConfig - )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = - test(options)(checkF(prop, config, propertySeed)) + )(prop: PropertyT[F[Result]])(implicit loc: Location): Unit = { + val config = options.tags.collectFirst { case HedgehogConfig(config) => config }.getOrElse(hedgehogPropertyConfig) + test(options)(checkF(prop, config, hedgehogSeed)) + } /** * Checks the supplied `Property[Result]`, throwing a `HedgehogFailException` * if the property was falsified. */ - def check( + private def check( prop: PropertyT[Result], - config: PropertyConfig = propertyConfig, - seed: Long = propertySeed + config: PropertyConfig = hedgehogPropertyConfig, + seed: Long = hedgehogSeed )(implicit loc: Location): Unit = { val report = Property.check(config, prop, Seed.fromLong(seed)) HedgehogFailException.fromReport(report, seed) match { @@ -100,10 +80,10 @@ trait HedgehogSuite extends FunSuite { * this should only be used with effect types that handle exceptions thrown from * `map`. */ - def checkF[F[_]: Monad]( + private def checkF[F[_]: Monad]( prop: PropertyT[F[Result]], - config: PropertyConfig = propertyConfig, - seed: Long = propertySeed + config: PropertyConfig = hedgehogPropertyConfig, + seed: Long = hedgehogSeed )(implicit loc: Location): F[Unit] = { ??? // Waiting for https://github.com/hedgehogqa/scala-hedgehog/pull/147 } diff --git a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala index 3e63acc0..a45976a3 100644 --- a/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/HedgehogFrameworkSuite.scala @@ -5,8 +5,8 @@ import hedgehog.core.SuccessCount class HedgehogFrameworkSuite extends HedgehogSuite { - // The default propertySeed changes on each test run so we fix it here so that test failures are always the same - override lazy val propertySeed = 123L + // The default property seed changes on each test run so we fix it here so that test failures are always the same + override val hedgehogSeed = 123L private val genInt: Gen[Int] = Gen.int(Range.linearFrom(0, Int.MinValue, Int.MaxValue)) @@ -42,8 +42,7 @@ class HedgehogFrameworkSuite extends HedgehogSuite { } property( - "custom config", - config = propertyConfig.copy(testLimit = SuccessCount(1000)) + "custom config".tag(HedgehogConfig(hedgehogPropertyConfig.copy(testLimit = SuccessCount(1000)))) ) { genInt.forAll.map(n => assertEquals(n + 0, n)) }