From a2d639806716b956ccaad1dda4f06c3e61abedd6 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 6 Jul 2022 17:35:24 +0100 Subject: [PATCH 01/33] The Great Schism: Remove Cats Effect 2 and monix --- .github/workflows/ci.yml | 7 +- build.sbt | 120 +++--------------- docs/monix_bio_usage.md | 95 -------------- docs/monix_usage.md | 91 ------------- .../cats/src-ce2-js/PlatformECCompat.scala | 9 -- .../cats/src-ce2-jvm/PlatformECCompat.scala | 7 - .../cats/src-ce2/weaver/BaseIOSuites.scala | 18 --- .../cats/src-ce2/weaver/CatsUnsafeRun.scala | 29 ----- .../weaver/CatsUnsafeRunPlatformCompat.scala | 0 .../weaver/CatsUnsafeRunPlatformCompat.scala | 0 .../{src-ce3 => src}/weaver/BaseIOSuite.scala | 0 .../weaver/CatsUnsafeRun.scala | 0 .../core/monix/src-js/PlatformCompat.scala | 12 -- .../core/monix/src-jvm/PlatformCompat.scala | 14 -- .../weaver/monixcompat/MonixUnsafeRun.scala | 32 ----- .../monixcompat/TaskGlobalResource.scala | 10 -- .../src/weaver/monixcompat/package.scala | 12 -- .../monix/src/weaver/monixcompat/suites.scala | 51 -------- .../core/monixBio/src-js/PlatformCompat.scala | 13 -- .../monixBio/src-jvm/PlatformCompat.scala | 13 -- .../monixbiocompat/IOGlobalResource.scala | 10 -- .../monixbiocompat/MonixBioUnsafeRun.scala | 25 ---- .../src/weaver/monixbiocompat/package.scala | 12 -- .../src/weaver/monixbiocompat/suites.scala | 78 ------------ .../weaver/PlatformEffectCompat.scala | 3 - .../weaver/PlatformEffectCompat.scala | 14 -- modules/core/src-ce2/weaver/CECompat.scala | 69 ---------- modules/core/src-ce2/weaver/UnsafeRun.scala | 40 ------ .../{src-ce3 => src}/weaver/CECompat.scala | 0 .../{src-ce3 => src}/weaver/UnsafeRun.scala | 0 .../weaver/ziocompat/ZIOUnsafeRun.scala | 33 ----- .../weaver/ziocompat/ZIOUnsafeRun.scala | 0 project/WeaverPlugin.scala | 69 ++++------ project/build.properties | 2 +- 34 files changed, 47 insertions(+), 841 deletions(-) delete mode 100644 docs/monix_bio_usage.md delete mode 100644 docs/monix_usage.md delete mode 100644 modules/core/cats/src-ce2-js/PlatformECCompat.scala delete mode 100644 modules/core/cats/src-ce2-jvm/PlatformECCompat.scala delete mode 100644 modules/core/cats/src-ce2/weaver/BaseIOSuites.scala delete mode 100644 modules/core/cats/src-ce2/weaver/CatsUnsafeRun.scala rename modules/core/cats/{src-ce3-js => src-js}/weaver/CatsUnsafeRunPlatformCompat.scala (100%) rename modules/core/cats/{src-ce3-jvm => src-jvm}/weaver/CatsUnsafeRunPlatformCompat.scala (100%) rename modules/core/cats/{src-ce3 => src}/weaver/BaseIOSuite.scala (100%) rename modules/core/cats/{src-ce3 => src}/weaver/CatsUnsafeRun.scala (100%) delete mode 100644 modules/core/monix/src-js/PlatformCompat.scala delete mode 100644 modules/core/monix/src-jvm/PlatformCompat.scala delete mode 100644 modules/core/monix/src/weaver/monixcompat/MonixUnsafeRun.scala delete mode 100644 modules/core/monix/src/weaver/monixcompat/TaskGlobalResource.scala delete mode 100644 modules/core/monix/src/weaver/monixcompat/package.scala delete mode 100644 modules/core/monix/src/weaver/monixcompat/suites.scala delete mode 100644 modules/core/monixBio/src-js/PlatformCompat.scala delete mode 100644 modules/core/monixBio/src-jvm/PlatformCompat.scala delete mode 100644 modules/core/monixBio/src/weaver/monixbiocompat/IOGlobalResource.scala delete mode 100644 modules/core/monixBio/src/weaver/monixbiocompat/MonixBioUnsafeRun.scala delete mode 100644 modules/core/monixBio/src/weaver/monixbiocompat/package.scala delete mode 100644 modules/core/monixBio/src/weaver/monixbiocompat/suites.scala delete mode 100644 modules/core/src-ce2-js/weaver/PlatformEffectCompat.scala delete mode 100644 modules/core/src-ce2-jvm/weaver/PlatformEffectCompat.scala delete mode 100644 modules/core/src-ce2/weaver/CECompat.scala delete mode 100644 modules/core/src-ce2/weaver/UnsafeRun.scala rename modules/core/{src-ce3 => src}/weaver/CECompat.scala (100%) rename modules/core/{src-ce3 => src}/weaver/UnsafeRun.scala (100%) delete mode 100644 modules/core/zio/src-ce2/weaver/ziocompat/ZIOUnsafeRun.scala rename modules/core/zio/{src-ce3 => src}/weaver/ziocompat/ZIOUnsafeRun.scala (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63af922c..e636c8d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,14 +2,14 @@ name: CI on: pull_request: - branches: ["*"] + branches: ["master"] push: branches: ["master"] tags: ["v*"] jobs: build: - name: Test ${{matrix.ceVersion}} ${{matrix.scalaVersion}} (${{matrix.scalaPlatform}}) + name: Test ${{matrix.scalaVersion}} (${{matrix.scalaPlatform}}) strategy: fail-fast: false matrix: @@ -17,10 +17,9 @@ jobs: java: [adopt@1.8] scalaVersion: ["2_12", "2_13", "3"] scalaPlatform: ["jvm", "js"] - ceVersion: ["CE2", "CE3"] runs-on: ${{ matrix.os }} env: - BUILD_KEY: ${{matrix.ceVersion}}_${{matrix.scalaVersion}}_${{matrix.scalaPlatform}} + BUILD_KEY: ${{matrix.scalaVersion}}_${{matrix.scalaPlatform}} steps: - name: Checkout current branch uses: actions/checkout@v2 diff --git a/build.sbt b/build.sbt index e4c83008..44c23b66 100644 --- a/build.sbt +++ b/build.sbt @@ -41,12 +41,6 @@ val Version = new { val zioInterop = "3.2.9.1" } - object CE2 { - val fs2 = "2.5.11" - val cats = "2.5.5" - val zioInterop = "2.5.1.0" - } - val expecty = "0.15.4" val portableReflect = "1.1.2" val junit = "4.13.2" @@ -55,8 +49,6 @@ val Version = new { val discipline = "1.5.1" val catsLaws = "2.8.0" val scalacheck = "1.16.0" - val monix = "3.4.1" - val monixBio = "1.2.0" val testInterface = "1.0" val scalaJavaTime = "2.4.0" val scalajsMacroTask = "1.0.0" @@ -80,18 +72,11 @@ lazy val allModules = Seq( def catsEffectDependencies(proj: Project): Project = { proj.settings( - libraryDependencies ++= { - if (virtualAxes.value.contains(CatsEffect2Axis)) - Seq( - "co.fs2" %%% "fs2-core" % Version.CE2.fs2, - "org.typelevel" %%% "cats-effect" % Version.CE2.cats - ) - else - Seq( - "co.fs2" %%% "fs2-core" % Version.CE3.fs2, - "org.typelevel" %%% "cats-effect" % Version.CE3.cats - ) - } + libraryDependencies ++= + Seq( + "co.fs2" %%% "fs2-core" % Version.CE3.fs2, + "org.typelevel" %%% "cats-effect" % Version.CE3.cats + ) ) } @@ -145,7 +130,7 @@ lazy val docs = projectMatrix .in(file("modules/docs")) .jvmPlatform(WeaverPlugin.supportedScala2Versions) .enablePlugins(DocusaurusPlugin, MdocPlugin) - .dependsOn(core, scalacheck, cats, zio, monix, monixBio, specs2, discipline) + .dependsOn(core, scalacheck, cats, zio, specs2, discipline) .settings( moduleName := "docs", watchSources += (ThisBuild / baseDirectory).value / "docs", @@ -173,17 +158,11 @@ lazy val docs = projectMatrix case a: VirtualAxis.ScalaVersionAxis => a }.get.scalaVersion - val CE = axes.collectFirst { - case CatsEffect2Axis => "CE2" - case CatsEffect3Axis => "CE3" - }.get - List( s"name = ${q(name)}", s"jvm = $isJVM", s"js = $isJS", s"scalaVersion = ${q(scalaVersion)}", - s"catsEffect = $CE", s"version = ${q(ver)}" ).mkString("Artifact(", ",", ")") }.mkString("List(", ",\n", ")") @@ -192,13 +171,7 @@ lazy val docs = projectMatrix val integrations = process(projectsWithAxes.all(allIntegrationsCoresFilter).value) - val artifactsCE2Version = (cats.finder( - VirtualAxis.jvm, - CatsEffect2Axis).apply(scala213) / version).value - - val artifactsCE3Version = (cats.finder( - VirtualAxis.jvm, - CatsEffect3Axis).apply(scala213) / version).value + val artifactsCE3Version = (cats.jvm(scala213) / version).value IO.write( filePath, @@ -207,7 +180,6 @@ lazy val docs = projectMatrix | | object BuildMatrix { | val catsEffect3Version = ${q(Version.CE3.cats)} - | val artifactsCE2Version = ${q(artifactsCE2Version)} | val artifactsCE3Version = ${q(artifactsCE3Version)} | val effects = $effects | val integrations = $integrations @@ -246,15 +218,13 @@ lazy val scalacheck = projectMatrix .settings( testFrameworks := Seq(new TestFramework("weaver.framework.CatsEffect")), libraryDependencies ++= Seq( - "org.scalacheck" %%% "scalacheck" % Version.scalacheck - ) ++ Seq( - "org.typelevel" %%% "cats-effect-testkit" % Version.CE3.cats % Test).filter( - _ => virtualAxes.value.contains(CatsEffect3Axis)) + "org.scalacheck" %%% "scalacheck" % Version.scalacheck, + "org.typelevel" %%% "cats-effect-testkit" % Version.CE3.cats % Test) ) lazy val specs2 = projectMatrix .in(file("modules/specs2")) - .sparse(withCE3 = true, withJS = true, withScala3 = false) + .sparse(withJS = true, withScala3 = false) .dependsOn(core, cats % "test->compile") .settings( name := "specs2", @@ -267,7 +237,7 @@ lazy val specs2 = projectMatrix lazy val discipline = projectMatrix .in(file("modules/discipline")) - .sparse(withCE3 = true, withJS = true, withScala3 = true) + .sparse(withJS = true, withScala3 = true) .dependsOn(core, cats) .settings( name := "discipline", @@ -284,7 +254,7 @@ lazy val discipline = projectMatrix // ################################################################################################# lazy val effectCores: Seq[ProjectReference] = - coreCats.projectRefs ++ coreMonix.projectRefs ++ coreZio.projectRefs ++ coreMonixBio.projectRefs + coreCats.projectRefs ++ coreZio.projectRefs lazy val coreCats = projectMatrix .in(file("modules/core/cats")) @@ -294,47 +264,17 @@ lazy val coreCats = projectMatrix .settings(scalaJSMacroTask) .settings(name := "cats-core") -lazy val coreMonix = projectMatrix - .in(file("modules/core/monix")) - .sparse(withCE3 = false, withJS = true, withScala3 = true) - .dependsOn(core) - .settings(WeaverPlugin.simpleLayout) - .settings( - name := "monix-core", - libraryDependencies ++= Seq( - "io.monix" %%% "monix" % Version.monix - ) - ) - -lazy val coreMonixBio = projectMatrix - .in(file("modules/core/monixBio")) - .sparse(withCE3 = false, withJS = true, withScala3 = true) - .dependsOn(core) - .settings(WeaverPlugin.simpleLayout) - .settings( - name := "monix-bio-core", - libraryDependencies ++= Seq( - "io.monix" %%% "monix-bio" % Version.monixBio - ) - ) - lazy val coreZio = projectMatrix .in(file("modules/core/zio")) - .sparse(withCE3 = true, withJS = true, withScala3 = false) + .sparse(withJS = true, withScala3 = false) .dependsOn(core) .settings(WeaverPlugin.simpleLayout) .settings( name := "zio-core", - libraryDependencies ++= { - if (virtualAxes.value.contains(CatsEffect3Axis)) - Seq( - "dev.zio" %%% "zio-interop-cats" % Version.CE3.zioInterop - ) - else - Seq( - "dev.zio" %%% "zio-interop-cats" % Version.CE2.zioInterop - ) - } + libraryDependencies ++= + Seq( + "dev.zio" %%% "zio-interop-cats" % Version.CE3.zioInterop + ) ) // ################################################################################################# @@ -343,8 +283,6 @@ lazy val coreZio = projectMatrix lazy val effectFrameworks: Seq[ProjectReference] = Seq( cats.projectRefs, - monix.projectRefs, - monixBio.projectRefs, zio.projectRefs ).flatten @@ -358,29 +296,9 @@ lazy val cats = projectMatrix testFrameworks := Seq(new TestFramework("weaver.framework.CatsEffect")) ) -lazy val monix = projectMatrix - .in(file("modules/framework/monix")) - .sparse(withCE3 = false, withJS = true, withScala3 = true) - .dependsOn(framework, coreMonix) - .settings(WeaverPlugin.simpleLayout) - .settings( - name := "monix", - testFrameworks := Seq(new TestFramework("weaver.framework.Monix")) - ) - -lazy val monixBio = projectMatrix - .in(file("modules/framework/monix-bio")) - .sparse(withCE3 = false, withJS = true, withScala3 = true) - .dependsOn(framework, coreMonixBio) - .settings(WeaverPlugin.simpleLayout) - .settings( - name := "monix-bio", - testFrameworks := Seq(new TestFramework("weaver.framework.MonixBIO")) - ) - lazy val zio = projectMatrix .in(file("modules/framework/zio")) - .sparse(withCE3 = true, withJS = true, withScala3 = false) + .sparse(withJS = true, withScala3 = false) .dependsOn(framework, coreZio, scalacheck % "test->compile") .settings(WeaverPlugin.simpleLayout) .settings( @@ -394,7 +312,7 @@ lazy val zio = projectMatrix // ################################################################################################# lazy val intellijRunner = projectMatrix - .sparse(withCE3 = false, withJS = false, withScala3 = false) + .sparse(withJS = false, withScala3 = false) .in(file("modules/intellij-runner")) .dependsOn(core, framework, framework % "test->compile") .settings(WeaverPlugin.simpleLayout) diff --git a/docs/monix_bio_usage.md b/docs/monix_bio_usage.md deleted file mode 100644 index 328569f1..00000000 --- a/docs/monix_bio_usage.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -id: monix_bio -title: Monix BIO usage ---- - -Testing Monix BIO programs is very similar to testing regular Monix programs. - -Tests must return `monix.bio.Task[Expectation]` instances. - -Note that in Monix BIO `Task[A]` [is an alias](https://bio.monix.io/docs/introduction) for the `IO[Throwable, A]` effect. - -## Installation - -You'll need to install an additional dependency in order to use weaver to test Monix BIO rograms. - -### SBT -```scala -libraryDependencies += "com.disneystreaming" %% "weaver-monix-bio" % "@VERSION@" % Test -testFrameworks += new TestFramework("weaver.framework.MonixBIO") -``` - -### Mill -```scala -object test extends Tests { - def ivyDeps = Agg( - ivy"com.disneystreaming::weaver-monix-bio:@VERSION@" - ) - def testFramework = "weaver.framework.MonixBIO" -} -``` - -## Usage - -For basic usage, simply extend `SimpleIOSuite`. Porting the example test, for instance: - -```scala mdoc -import monix.bio.Task -import weaver.monixbiocompat._ - -object MySuite extends SimpleIOSuite { - - val randomUUID = Task(java.util.UUID.randomUUID()) // Use of `Task` instead of `IO` - - test("hello side-effects") { - for { - x <- randomUUID - y <- randomUUID - } yield expect(x != y) - } - -} -``` - -## Usage with shared resources - -Monix BIO program tests make use of shared resorces in the same way as Cats Effect program tests. - -Extend `IOSuite`, implementing the `sharedResource` member. For example: - -```scala mdoc -import monix.bio.Task -import weaver.monixbiocompat._ -import cats.effect._ - -// Using http4s -import org.http4s.client.blaze._ -import org.http4s.client._ - -object HttpSuite extends IOSuite { - - // Sharing a single http client across all tests - override type Res = Client[Task] - override def sharedResource : Resource[Task, Res] = - BlazeClientBuilder[Task](scheduler).resource - - // The test receives the shared client as an argument - test("Good requests lead to good results") { httpClient => - for { - statusCode <- httpClient.get("https://httpbin.org/get"){ - response => Task.pure(response.status.code) - } - } yield expect(statusCode == 200) - } - - test("Bad requests lead to bad results") { httpClient => - for { - statusCode <- httpClient.get("https://httpbin.org/oops"){ - response => Task.pure(response.status.code) - } - } yield expect(statusCode == 404) - } - - -} -``` diff --git a/docs/monix_usage.md b/docs/monix_usage.md deleted file mode 100644 index 3a1fea3a..00000000 --- a/docs/monix_usage.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -id: monix -title: Monix usage ---- - -## Installation - -You'll need to install an additional dependency in order to use weaver to test Monix programs. - -### SBT -```scala -libraryDependencies += "com.disneystreaming" %% "weaver-monix" % "@VERSION@" % Test -testFrameworks += new TestFramework("weaver.framework.Monix") -``` - -### Mill -```scala -object test extends Tests { - def ivyDeps = Agg( - ivy"com.disneystreaming::weaver-monix:@VERSION@" - ) - def testFramework = "weaver.framework.Monix" -} -``` - -## Usage - -Testing Monix programs is practically the same as testing Cats Effect programs. - -For basic usage, simply extend `SimpleTaskSuite`. Porting the example test, for instance: - -```scala mdoc -import monix.eval.Task -import weaver.monixcompat._ - -object MySuite extends SimpleTaskSuite { - - val randomUUID = Task(java.util.UUID.randomUUID()) // Use of `Task` instead of `IO` - - test("hello side-effects") { - for { - x <- randomUUID - y <- randomUUID - } yield expect(x != y) - } - -} -``` - -## Usage with shared resources - -Monix program tests make use of shared resorces in the same way as Cats Effect program tests. - -Extend `TaskSuite`, implementing the `sharedResource` member. For example: - -```scala mdoc -import monix.eval.Task -import weaver.monixcompat._ -import cats.effect._ - -// Using http4s -import org.http4s.client.blaze._ -import org.http4s.client._ - -object HttpSuite extends TaskSuite { - - // Sharing a single http client across all tests - override type Res = Client[Task] - override def sharedResource : Resource[Task, Res] = - BlazeClientBuilder[Task](scheduler).resource - - // The test receives the shared client as an argument - test("Good requests lead to good results") { httpClient => - for { - statusCode <- httpClient.get("https://httpbin.org/get"){ - response => Task.pure(response.status.code) - } - } yield expect(statusCode == 200) - } - - test("Bad requests lead to bad results") { httpClient => - for { - statusCode <- httpClient.get("https://httpbin.org/oops"){ - response => Task.pure(response.status.code) - } - } yield expect(statusCode == 404) - } - - -} -``` diff --git a/modules/core/cats/src-ce2-js/PlatformECCompat.scala b/modules/core/cats/src-ce2-js/PlatformECCompat.scala deleted file mode 100644 index 3efcf6bf..00000000 --- a/modules/core/cats/src-ce2-js/PlatformECCompat.scala +++ /dev/null @@ -1,9 +0,0 @@ -package weaver - -import scala.concurrent.ExecutionContext - -import org.scalajs.macrotaskexecutor.MacrotaskExecutor - -private[weaver] object PlatformECCompat { - val ec: ExecutionContext = MacrotaskExecutor -} diff --git a/modules/core/cats/src-ce2-jvm/PlatformECCompat.scala b/modules/core/cats/src-ce2-jvm/PlatformECCompat.scala deleted file mode 100644 index af27b861..00000000 --- a/modules/core/cats/src-ce2-jvm/PlatformECCompat.scala +++ /dev/null @@ -1,7 +0,0 @@ -package weaver - -import scala.concurrent.ExecutionContext - -private[weaver] object PlatformECCompat { - val ec: ExecutionContext = ExecutionContext.global -} diff --git a/modules/core/cats/src-ce2/weaver/BaseIOSuites.scala b/modules/core/cats/src-ce2/weaver/BaseIOSuites.scala deleted file mode 100644 index ab0a4976..00000000 --- a/modules/core/cats/src-ce2/weaver/BaseIOSuites.scala +++ /dev/null @@ -1,18 +0,0 @@ -package weaver - -import cats.effect.{ ContextShift, IO, Timer } - -trait BaseIOSuite extends BaseCatsSuite { self: RunnableSuite[IO] => - implicit protected def effectCompat: UnsafeRun[EffectType] = CatsUnsafeRun - def unsafeRun: UnsafeRun[EffectType] = CatsUnsafeRun - final implicit protected def contextShift: ContextShift[IO] = - effectCompat.contextShift - final implicit protected def timer: Timer[IO] = effectCompat.timer - def getSuite: EffectSuite[IO] = this -} - -trait BaseFunIOSuite extends FunSuiteF[IO] with BaseCatsSuite { - override implicit protected def effectCompat: UnsafeRun[EffectType] = - CatsUnsafeRun - def getSuite: EffectSuite[IO] = this -} diff --git a/modules/core/cats/src-ce2/weaver/CatsUnsafeRun.scala b/modules/core/cats/src-ce2/weaver/CatsUnsafeRun.scala deleted file mode 100644 index fdb8df61..00000000 --- a/modules/core/cats/src-ce2/weaver/CatsUnsafeRun.scala +++ /dev/null @@ -1,29 +0,0 @@ -package weaver - -import cats.effect.{ ContextShift, IO, Timer } - -object CatsUnsafeRun extends CatsUnsafeRun - -trait CatsUnsafeRun extends UnsafeRun[IO] { - - type CancelToken = IO[Unit] - - override implicit val contextShift: ContextShift[IO] = - IO.contextShift(PlatformECCompat.ec) - - override implicit val timer: Timer[IO] = - IO.timer(PlatformECCompat.ec) - - override implicit val effect = IO.ioConcurrentEffect(contextShift) - override implicit val parallel = IO.ioParallel(contextShift) - - def background(task: IO[Unit]): CancelToken = - task.unsafeRunCancelable { _ => () } - - def cancel(token: CancelToken): Unit = sync(token) - - def sync(task: IO[Unit]): Unit = task.unsafeRunSync() - - def async(task: IO[Unit]): Unit = task.unsafeRunAsyncAndForget() - -} diff --git a/modules/core/cats/src-ce3-js/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-js/weaver/CatsUnsafeRunPlatformCompat.scala similarity index 100% rename from modules/core/cats/src-ce3-js/weaver/CatsUnsafeRunPlatformCompat.scala rename to modules/core/cats/src-js/weaver/CatsUnsafeRunPlatformCompat.scala diff --git a/modules/core/cats/src-ce3-jvm/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-jvm/weaver/CatsUnsafeRunPlatformCompat.scala similarity index 100% rename from modules/core/cats/src-ce3-jvm/weaver/CatsUnsafeRunPlatformCompat.scala rename to modules/core/cats/src-jvm/weaver/CatsUnsafeRunPlatformCompat.scala diff --git a/modules/core/cats/src-ce3/weaver/BaseIOSuite.scala b/modules/core/cats/src/weaver/BaseIOSuite.scala similarity index 100% rename from modules/core/cats/src-ce3/weaver/BaseIOSuite.scala rename to modules/core/cats/src/weaver/BaseIOSuite.scala diff --git a/modules/core/cats/src-ce3/weaver/CatsUnsafeRun.scala b/modules/core/cats/src/weaver/CatsUnsafeRun.scala similarity index 100% rename from modules/core/cats/src-ce3/weaver/CatsUnsafeRun.scala rename to modules/core/cats/src/weaver/CatsUnsafeRun.scala diff --git a/modules/core/monix/src-js/PlatformCompat.scala b/modules/core/monix/src-js/PlatformCompat.scala deleted file mode 100644 index 7e67bd31..00000000 --- a/modules/core/monix/src-js/PlatformCompat.scala +++ /dev/null @@ -1,12 +0,0 @@ -package weaver -package monixcompat - -import monix.execution.Scheduler - -object PlatformCompat { - def runSync(task: monix.eval.Task[Unit])(implicit scheduler: Scheduler) = { - val _ = scheduler - } - - def defaultScheduler: Scheduler = Scheduler.global -} diff --git a/modules/core/monix/src-jvm/PlatformCompat.scala b/modules/core/monix/src-jvm/PlatformCompat.scala deleted file mode 100644 index a5ae8d0f..00000000 --- a/modules/core/monix/src-jvm/PlatformCompat.scala +++ /dev/null @@ -1,14 +0,0 @@ -package weaver -package monixcompat - -import monix.execution.Scheduler - -object PlatformCompat { - def runSync(task: monix.eval.Task[Unit])(implicit scheduler: Scheduler) = - task.runSyncUnsafe() - - def defaultScheduler: Scheduler = Scheduler.fixedPool( - "weaver-monix", - java.lang.Runtime.getRuntime().availableProcessors() - 1) - -} diff --git a/modules/core/monix/src/weaver/monixcompat/MonixUnsafeRun.scala b/modules/core/monix/src/weaver/monixcompat/MonixUnsafeRun.scala deleted file mode 100644 index 092980c4..00000000 --- a/modules/core/monix/src/weaver/monixcompat/MonixUnsafeRun.scala +++ /dev/null @@ -1,32 +0,0 @@ -package weaver -package monixcompat - -import cats.effect.{ ContextShift, Timer } - -import monix.eval.Task -import monix.execution.{ Cancelable, Scheduler } - -object MonixUnsafeRun extends UnsafeRun[Task] { - - type CancelToken = Cancelable - - implicit val scheduler: Scheduler = PlatformCompat.defaultScheduler - - override implicit val contextShift: ContextShift[Task] = - Task.contextShift(scheduler) - override implicit val timer: Timer[Task] = - Task.timer(scheduler) - - override implicit val effect = Task.catsEffect(scheduler) - override implicit val parallel = Task.catsParallel - - def background(task: Task[Unit]): Cancelable = - task.runAsync { _ => () }(scheduler) - - def cancel(token: CancelToken): Unit = token.cancel() - - def sync(task: Task[Unit]): Unit = PlatformCompat.runSync(task) - - def async(task: Task[Unit]): Unit = task.runAsyncAndForget - -} diff --git a/modules/core/monix/src/weaver/monixcompat/TaskGlobalResource.scala b/modules/core/monix/src/weaver/monixcompat/TaskGlobalResource.scala deleted file mode 100644 index 48c9e563..00000000 --- a/modules/core/monix/src/weaver/monixcompat/TaskGlobalResource.scala +++ /dev/null @@ -1,10 +0,0 @@ -package weaver -package monixcompat - -import cats.effect.Resource - -import monix.eval.Task - -trait TaskGlobalResource extends GlobalResourceF[Task] { - def sharedResources(global: GlobalWrite): Resource[Task, Unit] -} diff --git a/modules/core/monix/src/weaver/monixcompat/package.scala b/modules/core/monix/src/weaver/monixcompat/package.scala deleted file mode 100644 index 42302d5c..00000000 --- a/modules/core/monix/src/weaver/monixcompat/package.scala +++ /dev/null @@ -1,12 +0,0 @@ -package weaver - -import monix.eval.Task - -package object monixcompat { - type TaskSuite = MutableTaskSuite - type SimpleTaskSuite = SimpleMutableTaskSuite - type FunSuite = FunTaskSuite - type GlobalResource = TaskGlobalResource - type GlobalRead = GlobalResourceF.Read[Task] - type GlobalWrite = GlobalResourceF.Write[Task] -} diff --git a/modules/core/monix/src/weaver/monixcompat/suites.scala b/modules/core/monix/src/weaver/monixcompat/suites.scala deleted file mode 100644 index b0f49f19..00000000 --- a/modules/core/monix/src/weaver/monixcompat/suites.scala +++ /dev/null @@ -1,51 +0,0 @@ -package weaver -package monixcompat - -import cats.effect.Resource - -import monix.eval.Task -import monix.execution.Scheduler - -trait BaseTaskSuite extends EffectSuite.Provider[Task] - -abstract class PureTaskSuite - extends EffectSuite[Task] - with BaseTaskSuite - with Expectations.Helpers { - - def pureTest(name: String)(run: => Expectations): Task[TestOutcome] = - Test[Task](name, Task(run)) - def simpleTest(name: String)(run: Task[Expectations]): Task[TestOutcome] = - Test[Task](name, run) - def loggedTest(name: String)( - run: Log[Task] => Task[Expectations]): Task[TestOutcome] = - Test[Task](name, run) - - def getSuite: EffectSuite[Task] = this -} - -abstract class MutableTaskSuite - extends MutableFSuite[Task] - with BaseTaskSuite - with Expectations.Helpers { - - implicit protected def effectCompat = MonixUnsafeRun - - final implicit protected def scheduler: Scheduler = MonixUnsafeRun.scheduler - def getSuite: EffectSuite[Task] = this -} - -trait SimpleMutableTaskSuite extends MutableTaskSuite { - type Res = Unit - def sharedResource: Resource[Task, Unit] = Resource.pure[Task, Unit](()) -} - -trait FunTaskSuite - extends FunSuiteF[Task] - with BaseTaskSuite - with Expectations.Helpers { - implicit protected def effectCompat = MonixUnsafeRun - - final implicit protected def scheduler: Scheduler = MonixUnsafeRun.scheduler - def getSuite: EffectSuite[Task] = this -} diff --git a/modules/core/monixBio/src-js/PlatformCompat.scala b/modules/core/monixBio/src-js/PlatformCompat.scala deleted file mode 100644 index 7e308b1a..00000000 --- a/modules/core/monixBio/src-js/PlatformCompat.scala +++ /dev/null @@ -1,13 +0,0 @@ -package weaver -package monixbiocompat - -import monix.execution.Scheduler - -object PlatformCompat { - def runSync(task: monix.bio.Task[Unit])(implicit scheduler: Scheduler) = { - val _ = scheduler - } - - def defaultScheduler: Scheduler = Scheduler.global - -} diff --git a/modules/core/monixBio/src-jvm/PlatformCompat.scala b/modules/core/monixBio/src-jvm/PlatformCompat.scala deleted file mode 100644 index 86fe9ed2..00000000 --- a/modules/core/monixBio/src-jvm/PlatformCompat.scala +++ /dev/null @@ -1,13 +0,0 @@ -package weaver -package monixbiocompat - -import monix.execution.Scheduler - -object PlatformCompat { - def runSync(task: monix.bio.Task[Unit])(implicit scheduler: Scheduler) = - task.runSyncUnsafe() - - def defaultScheduler: Scheduler = Scheduler.fixedPool( - "weaver-monix", - java.lang.Runtime.getRuntime().availableProcessors() - 1) -} diff --git a/modules/core/monixBio/src/weaver/monixbiocompat/IOGlobalResource.scala b/modules/core/monixBio/src/weaver/monixbiocompat/IOGlobalResource.scala deleted file mode 100644 index 2e4f4f4c..00000000 --- a/modules/core/monixBio/src/weaver/monixbiocompat/IOGlobalResource.scala +++ /dev/null @@ -1,10 +0,0 @@ -package weaver -package monixbiocompat - -import cats.effect.Resource - -import monix.bio.Task - -trait IOGlobalResource extends GlobalResourceF[Task] { - def sharedResources(global: GlobalWrite): Resource[Task, Unit] -} diff --git a/modules/core/monixBio/src/weaver/monixbiocompat/MonixBioUnsafeRun.scala b/modules/core/monixBio/src/weaver/monixbiocompat/MonixBioUnsafeRun.scala deleted file mode 100644 index c7ed35f4..00000000 --- a/modules/core/monixBio/src/weaver/monixbiocompat/MonixBioUnsafeRun.scala +++ /dev/null @@ -1,25 +0,0 @@ -package weaver -package monixbiocompat - -import cats.Parallel -import cats.effect.{ ContextShift, Timer } - -import monix.bio.IO -import monix.execution.{ Cancelable, Scheduler } - -object MonixBIOUnsafeRun extends UnsafeRun[monix.bio.Task] { - type CancelToken = Cancelable - implicit val scheduler: Scheduler = monix.execution.Scheduler.global - - implicit val effect = IO.catsEffect(scheduler) - implicit val parallel: Parallel[monix.bio.Task] = IO.catsParallel - implicit val contextShift: ContextShift[monix.bio.Task] = - IO.contextShift(scheduler) - implicit val timer: Timer[monix.bio.Task] = IO.timer(scheduler) - def background(task: monix.bio.Task[Unit]): CancelToken = { - task.runAsync { _ => () }(scheduler) - } - def sync(task: monix.bio.Task[Unit]): Unit = PlatformCompat.runSync(task) - def async(task: monix.bio.Task[Unit]): Unit = task.runAsyncAndForget - def cancel(token: CancelToken): Unit = token.cancel() -} diff --git a/modules/core/monixBio/src/weaver/monixbiocompat/package.scala b/modules/core/monixBio/src/weaver/monixbiocompat/package.scala deleted file mode 100644 index 95c81024..00000000 --- a/modules/core/monixBio/src/weaver/monixbiocompat/package.scala +++ /dev/null @@ -1,12 +0,0 @@ -package weaver - -import monix.bio.Task - -package object monixbiocompat { - type IOSuite = MutableIOSuite - type SimpleIOSuite = SimpleMutableIOSuite - type FunSuite = FunIOSuite - type GlobalResource = IOGlobalResource - type GlobalRead = GlobalResourceF.Read[Task] - type GlobalWrite = GlobalResourceF.Write[Task] -} diff --git a/modules/core/monixBio/src/weaver/monixbiocompat/suites.scala b/modules/core/monixBio/src/weaver/monixbiocompat/suites.scala deleted file mode 100644 index 7e914de4..00000000 --- a/modules/core/monixBio/src/weaver/monixbiocompat/suites.scala +++ /dev/null @@ -1,78 +0,0 @@ -package weaver -package monixbiocompat - -import scala.concurrent.duration.{ MILLISECONDS, _ } - -import cats.data.Chain -import cats.effect.{ Resource } -import cats.effect.concurrent.Ref - -import monix.bio.{ IO, Task } -import monix.execution.Scheduler - -trait BaseIOSuite extends EffectSuite.Provider[Task] - -/** - * Individual test runner for Monix BIO's `IO[Throwable, A]` that properly - * handles unexpected errors, - * i.e. errors that occur in another channel. - */ -object Test { - def apply( - name: String, - f: Log[Task] => Task[Expectations]): Task[TestOutcome] = - for { - ref <- Ref[Task].of(Chain.empty[Log.Entry]) - start <- ts - res <- IO - .defer(f(Log.collected[Task, Chain](ref, ts))) - .map(Result.fromAssertion) - .redeemCause(c => Result.from(c.toThrowable), identity) - end <- ts - logs <- ref.get - } yield TestOutcome(name, (end - start).millis, res, logs) - - private val ts = IO.clock.realTime(MILLISECONDS) - - def apply(name: String, f: Task[Expectations]): Task[TestOutcome] = - apply(name, (_: Log[Task]) => f) -} - -abstract class MutableIOSuite - extends MutableFSuite[Task] - with BaseIOSuite - with Expectations.Helpers { - - implicit protected def effectCompat = MonixBIOUnsafeRun - final implicit protected def scheduler: Scheduler = - MonixBIOUnsafeRun.scheduler - def getSuite: EffectSuite[Task] = this - - override def test(name: TestName): PartiallyAppliedTest = - new SubPartiallyAppliedTest(name) - - class SubPartiallyAppliedTest(name: TestName) - extends super.PartiallyAppliedTest(name) { - override def apply(run: => Task[Expectations]): Unit = - registerTest(name)(_ => Test(name.name, run)) - override def apply(run: Res => Task[Expectations]): Unit = - registerTest(name)(res => Test(name.name, run(res))) - override def apply(run: (Res, Log[Task]) => Task[Expectations]): Unit = - registerTest(name)(res => Test(name.name, log => run(res, log))) - } -} - -abstract class SimpleMutableIOSuite extends MutableIOSuite { - type Res = Unit - def sharedResource: Resource[Task, Unit] = Resource.pure[Task, Unit](()) -} - -trait FunIOSuite - extends FunSuiteF[Task] - with BaseIOSuite - with Expectations.Helpers { - implicit protected def effectCompat = MonixBIOUnsafeRun - final implicit protected def scheduler: Scheduler = - MonixBIOUnsafeRun.scheduler - def getSuite: EffectSuite[Task] = this -} diff --git a/modules/core/src-ce2-js/weaver/PlatformEffectCompat.scala b/modules/core/src-ce2-js/weaver/PlatformEffectCompat.scala deleted file mode 100644 index d1ccb01a..00000000 --- a/modules/core/src-ce2-js/weaver/PlatformEffectCompat.scala +++ /dev/null @@ -1,3 +0,0 @@ -package weaver - -trait PlatformEffectCompat[F[_]] { self: EffectCompat[F] => } diff --git a/modules/core/src-ce2-jvm/weaver/PlatformEffectCompat.scala b/modules/core/src-ce2-jvm/weaver/PlatformEffectCompat.scala deleted file mode 100644 index ee10df3b..00000000 --- a/modules/core/src-ce2-jvm/weaver/PlatformEffectCompat.scala +++ /dev/null @@ -1,14 +0,0 @@ -package weaver - -import cats.effect.{ Blocker, Resource } - -trait PlatformEffectCompat[F[_]] { self: EffectCompat[F] => - - private[weaver] def blocker[T]( - f: BlockerCompat[F] => T): Resource[F, T] = - Blocker[F].map(blocker => - new BlockerCompat[F] { - def block[A](thunk: => A): F[A] = blocker.delay(thunk) - }).map(f) - -} diff --git a/modules/core/src-ce2/weaver/CECompat.scala b/modules/core/src-ce2/weaver/CECompat.scala deleted file mode 100644 index aa5b634d..00000000 --- a/modules/core/src-ce2/weaver/CECompat.scala +++ /dev/null @@ -1,69 +0,0 @@ -package weaver - -import cats.Applicative -import cats.effect.ExitCase.{ Canceled, Completed } -import cats.effect.syntax.all._ -import cats.effect.{ Concurrent, ExitCase, Resource } -import cats.syntax.all._ - -private[weaver] object CECompat extends CECompat - -private[weaver] trait CECompat { - - private[weaver] type Effect[F[_]] = Concurrent[F] - private[weaver] type Ref[F[_], A] = cats.effect.concurrent.Ref[F, A] - private[weaver] val Ref = cats.effect.concurrent.Ref - - private[weaver] type Deferred[F[_], A] = cats.effect.concurrent.Deferred[F, A] - private[weaver] val Deferred = cats.effect.concurrent.Deferred - - private[weaver] type Semaphore[F[_]] = cats.effect.concurrent.Semaphore[F] - private[weaver] val Semaphore = cats.effect.concurrent.Semaphore - - private[weaver] def guaranteeCase[F[_]: Concurrent, A]( - fa: F[A])( - cancelled: => F[Unit], - completed: => F[Unit], - errored: Throwable => F[Unit]): F[A] = - Concurrent[F].guaranteeCase(fa) { - case Canceled => cancelled - case Completed => completed - case cats.effect.ExitCase.Error(e) => errored(e) - } - - private[weaver] def guarantee[F[_]: Concurrent, A]( - fa: F[A])(fin: F[Unit]): F[A] = - Concurrent[F].guarantee(fa)(fin) - - private[weaver] def onErrorEnsure[F[_]: Concurrent, A](r: Resource[F, A])( - f: Throwable => F[Unit]): Resource[F, A] = - r.onFinalizeCase { - case Canceled => Concurrent[F].unit - case Completed => Concurrent[F].unit - case ExitCase.Error(e) => f(e) - } - - private[weaver] def background[F[_]: Concurrent, A, B](fa: F[A], default: A)( - f: F[A] => F[B]): F[B] = - fa.background.use(f) - - private[weaver] def resourceLift[F[_]: Applicative, A]( - fa: F[A]): Resource[F, A] = Resource.eval(fa) - - private[weaver] trait Queue[F[_], A] { - protected def fs2Queue: fs2.concurrent.Queue[F, A] - - def enqueue(a: A): F[Unit] = fs2Queue.enqueue1(a) - def dequeueStream: fs2.Stream[F, A] = fs2Queue.dequeue - } - - private[weaver] object Queue { - def unbounded[F[_]: Concurrent, A] = - fs2.concurrent.Queue.unbounded[F, A].map { - q => - new Queue[F, A] { - override val fs2Queue = q - } - } - } -} diff --git a/modules/core/src-ce2/weaver/UnsafeRun.scala b/modules/core/src-ce2/weaver/UnsafeRun.scala deleted file mode 100644 index bad67cbd..00000000 --- a/modules/core/src-ce2/weaver/UnsafeRun.scala +++ /dev/null @@ -1,40 +0,0 @@ -package weaver - -import java.util.concurrent.TimeUnit - -import scala.concurrent.duration.FiniteDuration - -import cats.Parallel -import cats.effect.{ Async, Concurrent, ContextShift, Timer } - -trait EffectCompat[F[_]] extends PlatformEffectCompat[F] { - implicit def parallel: Parallel[F] - implicit def effect: Concurrent[F] - implicit def timer: Timer[F] - implicit def contextShift: ContextShift[F] - - def realTimeMillis: F[Long] = timer.clock.realTime(TimeUnit.MILLISECONDS) - def sleep(duration: FiniteDuration): F[Unit] = timer.sleep(duration) - def fromFuture[A](thunk: => scala.concurrent.Future[A]): F[A] = - Async.fromFuture(effect.delay(thunk)) - def async[A](cb: (Either[Throwable, A] => Unit) => Unit): F[A] = - effect.async(cb) -} - -/** - * Abstraction allowing for running IO constructs unsafely. - * - * This is meant to delegate to library-specific constructs for running effect - * types. - */ -trait UnsafeRun[F[_]] extends EffectCompat[F] { - - type CancelToken - - def background(task: F[Unit]): CancelToken - def cancel(token: CancelToken): Unit - - def sync(task: F[Unit]): Unit - def async(task: F[Unit]): Unit - -} diff --git a/modules/core/src-ce3/weaver/CECompat.scala b/modules/core/src/weaver/CECompat.scala similarity index 100% rename from modules/core/src-ce3/weaver/CECompat.scala rename to modules/core/src/weaver/CECompat.scala diff --git a/modules/core/src-ce3/weaver/UnsafeRun.scala b/modules/core/src/weaver/UnsafeRun.scala similarity index 100% rename from modules/core/src-ce3/weaver/UnsafeRun.scala rename to modules/core/src/weaver/UnsafeRun.scala diff --git a/modules/core/zio/src-ce2/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src-ce2/weaver/ziocompat/ZIOUnsafeRun.scala deleted file mode 100644 index 3a146827..00000000 --- a/modules/core/zio/src-ce2/weaver/ziocompat/ZIOUnsafeRun.scala +++ /dev/null @@ -1,33 +0,0 @@ -package weaver -package ziocompat - -import cats.Parallel -import cats.effect.{ ConcurrentEffect, ContextShift, Timer } - -import zio._ -import zio.interop.catz - -object ZIOUnsafeRun extends UnsafeRun[T] { - - type CancelToken = Fiber.Id => Exit[Throwable, Unit] - - implicit val runtime = Runtime.default - - implicit def timer: Timer[T] = catz.zioTimer[ZEnv, Throwable] - implicit def effect: ConcurrentEffect[T] = catz.taskEffectInstance[ZEnv] - - implicit def parallel: Parallel[T] = - catz.core.parallelInstance[ZEnv, Throwable] - - implicit def contextShift: ContextShift[T] = - catz.zioContextShift[ZEnv, Throwable] - - def background(task: T[Unit]): CancelToken = - runtime.unsafeRunAsyncCancelable(task)(_ => ()) - def cancel(token: Fiber.Id => Exit[Throwable, Unit]): Unit = - discard[Exit[Throwable, Unit]](token(Fiber.Id.None)) - - def sync(task: T[Unit]): Unit = runtime.unsafeRun(task) - - def async(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) -} diff --git a/modules/core/zio/src-ce3/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala similarity index 100% rename from modules/core/zio/src-ce3/weaver/ziocompat/ZIOUnsafeRun.scala rename to modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index fa449e0b..4b99fdb9 100644 --- a/project/WeaverPlugin.scala +++ b/project/WeaverPlugin.scala @@ -17,30 +17,23 @@ import sbt.VirtualAxis.ScalaVersionAxis import _root_.scalafix.sbt.ScalafixPlugin import org.scalafmt.sbt.ScalafmtPlugin -case class CatsEffectAxis(idSuffix: String, directorySuffix: String) - extends VirtualAxis.WeakAxis - /** * Common project settings. */ object WeaverPlugin extends AutoPlugin { - val CatsEffect2Axis = CatsEffectAxis("_CE2", "ce2") - val CatsEffect3Axis = CatsEffectAxis("_CE3", "ce3") - implicit final class ProjectMatrixOps(pmx: ProjectMatrix) { type ConfigureX = ProjectMatrix => ProjectMatrix type Configure = Project => Project val defaults = Seq[VirtualAxis]( - CatsEffect2Axis, VirtualAxis.jvm, VirtualAxis.scalaVersionAxis(WeaverPlugin.scala213, "2.13")) def addOne( scalaVersion: String, - platform: VirtualAxis.PlatformAxis, - catsEffectAxis: CatsEffectAxis): ConfigureX = { + platform: VirtualAxis.PlatformAxis + ): ConfigureX = { projectMatrix => val addScalafix: Configure = if (scalaVersion == scala213) @@ -56,46 +49,40 @@ object WeaverPlugin extends AutoPlugin { if (platform == VirtualAxis.js) configureScalaJSProject else identity val ce3VersionOverride: Configure = - if (catsEffectAxis == CatsEffect3Axis) - _.settings(versionOverrideForCE3) - else identity + _.settings(versionOverrideForCE3) val configureProject = addScalafix andThen addScalafmt andThen scalaJSSettings andThen ce3VersionOverride projectMatrix.defaultAxes(defaults: _*).customRow( scalaVersions = List(scalaVersion), - axisValues = Seq(catsEffectAxis, platform), + axisValues = Seq(platform), configureProject ) } def add( scalaVersions: Iterable[String], - platform: VirtualAxis.PlatformAxis, - catsEffectAxis: CatsEffectAxis): ConfigureX = { - scalaVersions.map(addOne(_, platform, catsEffectAxis)).reduce(_ andThen _) + platform: VirtualAxis.PlatformAxis + ): ConfigureX = { + scalaVersions.map(addOne(_, platform)).reduce(_ andThen _) } - def full = sparse(true, true, true) + def full = sparse(true, true) def sparse( - withCE3: Boolean, withJS: Boolean, withScala3: Boolean ): ProjectMatrix = { val defaultScalaVersions = supportedScala2Versions val defaultPlatform = List(VirtualAxis.jvm) - val defaultCE = List(CatsEffect2Axis) val addJs = if (withJS) List(VirtualAxis.js) else Nil val addScala3 = if (withScala3) List(scala3) else Nil - val addCE3 = if (withCE3) List(CatsEffect3Axis) else Nil val configurators = for { scalaVersion <- defaultScalaVersions ++ addScala3 platform <- defaultPlatform ++ addJs - catsEffect <- defaultCE ++ addCE3 - } yield addOne(scalaVersion, platform, catsEffect) + } yield addOne(scalaVersion, platform) val configure: ConfigureX = configurators.reduce(_ andThen _) @@ -112,8 +99,11 @@ object WeaverPlugin extends AutoPlugin { original match { case regex(major, minor, patch) => - original.replaceFirst(s"$major.$minor.$patch", - s"$major.${minor.toInt + 1}.$patch") + if (minor == "6") + original.replaceFirst(s"$major.$minor.$patch", + s"$major.${minor.toInt + 1}.$patch") + else + original case _ => throw new RuntimeException( s"Version $original doesn't match SemVer format") @@ -310,8 +300,6 @@ object WeaverPlugin extends AutoPlugin { def suffixes(axes: Seq[VirtualAxis]) = axes.collect { case VirtualAxis.js => List("", "-js") case VirtualAxis.jvm => List("", "-jvm") - case CatsEffect3Axis => List("", "-ce3") - case CatsEffect2Axis => List("", "-ce2") case ScalaVersionAxis(ver, _) => if (ver.startsWith("3.")) List("", "-scala-3") else List("", "-scala-2") @@ -386,23 +374,20 @@ object WeaverPlugin extends AutoPlugin { ) def createBuildCommands(projects: Seq[ProjectReference]) = { - case class Triplet(ce: String, scala: String, platform: String) + case class Duplet(scala: String, platform: String) val scala3Suffix = VirtualAxis.scalaABIVersion(scala3).idSuffix val scala213Suffix = VirtualAxis.scalaABIVersion(scala213).idSuffix val scala212Suffix = VirtualAxis.scalaABIVersion(scala212).idSuffix val jsSuffix = VirtualAxis.js.idSuffix - val ce3Suffix = CatsEffect3Axis.idSuffix - val ce2Suffix = CatsEffect2Axis.idSuffix - val all: List[(Triplet, Seq[String])] = + val all: List[(Duplet, Seq[String])] = projects.collect { case lp: LocalProject => var projectId = lp.project val scalaAxis = - if (projectId.endsWith(scala3Suffix) && !projectId.endsWith( - ce3Suffix)) { + if (projectId.endsWith(scala3Suffix)) { projectId = projectId.dropRight(scala3Suffix.length) "3" } else if (projectId.endsWith(scala212Suffix)) { @@ -418,21 +403,15 @@ object WeaverPlugin extends AutoPlugin { "js" } else "jvm" - val ceAxis = - if (projectId.endsWith(ce3Suffix)) { - projectId = projectId.dropRight(ce3Suffix.length) - "CE3" - } else "CE2" - - Triplet(ceAxis, scalaAxis, platformAxis) -> lp.project + Duplet(scalaAxis, platformAxis) -> lp.project }.groupBy(_._1).mapValues(_.map(_._2)).toList // some commands, like test and compile, are setup for all modules - val any = (t: Triplet) => true + val any = (t: Duplet) => true // things like scalafix and scalafmt are only enabled on jvm 2.13 projects - val jvm2_13 = (t: Triplet) => t.scala == "2_13" && t.platform == "jvm" + val jvm2_13 = (t: Duplet) => t.scala == "2_13" && t.platform == "jvm" - val desiredCommands: Map[String, (String, Triplet => Boolean)] = Map( + val desiredCommands: Map[String, (String, Duplet => Boolean)] = Map( "test" -> ("test", any), "compile" -> ("compile", any), "publishLocal" -> ("publishLocal", any), @@ -443,10 +422,10 @@ object WeaverPlugin extends AutoPlugin { ) val cmds = all.flatMap { - case (triplet, projects) => - desiredCommands.filter(_._2._2(triplet)).map { case (name, (cmd, _)) => + case (duplet, projects) => + desiredCommands.filter(_._2._2(duplet)).map { case (name, (cmd, _)) => Command.command( - s"${name}_${triplet.ce}_${triplet.scala}_${triplet.platform}") { + s"${name}_${duplet.scala}_${duplet.platform}") { state => projects.foldLeft(state) { case (st, proj) => s"$proj/$cmd" :: st diff --git a/project/build.properties b/project/build.properties index c8fcab54..20747124 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.6.2 +sbt.version=1.7.0-RC2 From 63750a8609d2caa4682a88740db8db16dd29c078 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 6 Jul 2022 18:01:09 +0100 Subject: [PATCH 02/33] Fix docs --- build.sbt | 10 +++---- docs/discipline.md | 2 +- docs/expectations.md | 4 +-- docs/funsuite.md | 10 +------ docs/installation.md | 6 ++-- docs/logging.md | 2 +- docs/multiple_suites_failures.md | 2 +- docs/multiple_suites_logging.md | 5 ++-- docs/multiple_suites_success.md | 2 +- docs/resources.md | 7 ++--- docs/scalacheck.md | 2 +- docs/specs2.md | 2 +- docs/tagging.md | 2 +- docs/zio_usage.md | 7 ++--- .../main/scala/weaver/MatrixRendering.scala | 29 ++++--------------- .../docs/src/main/scala/weaver/Output.scala | 12 ++++---- 16 files changed, 37 insertions(+), 67 deletions(-) diff --git a/build.sbt b/build.sbt index 44c23b66..595f8c7e 100644 --- a/build.sbt +++ b/build.sbt @@ -128,7 +128,7 @@ val allIntegrationsCoresFilter: ScopeFilter = lazy val docs = projectMatrix .in(file("modules/docs")) - .jvmPlatform(WeaverPlugin.supportedScala2Versions) + .jvmPlatform(Seq(WeaverPlugin.scala213)) .enablePlugins(DocusaurusPlugin, MdocPlugin) .dependsOn(core, scalacheck, cats, zio, specs2, discipline) .settings( @@ -138,11 +138,11 @@ lazy val docs = projectMatrix "VERSION" -> version.value ), libraryDependencies ++= Seq( - "org.http4s" %% "http4s-dsl" % "0.21.0", - "org.http4s" %% "http4s-blaze-server" % "0.21.0", - "org.http4s" %% "http4s-blaze-client" % "0.21.0", + "org.http4s" %% "http4s-dsl" % "0.23.12", + "org.http4s" %% "http4s-blaze-server" % "0.23.12", + "org.http4s" %% "http4s-blaze-client" % "0.23.12", "com.lihaoyi" %% "fansi" % "0.2.7", - "org.typelevel" %% "cats-kernel-laws" % "2.4.2" + "org.typelevel" %% "cats-kernel-laws" % "2.8.0" ), Compile / sourceGenerators += Def.taskDyn { val filePath = diff --git a/docs/discipline.md b/docs/discipline.md index 02a83bf6..66423cee 100644 --- a/docs/discipline.md +++ b/docs/discipline.md @@ -39,5 +39,5 @@ object DisciplineTests extends FunSuite with Discipline { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(DisciplineTests).unsafeRunSync()) +println(weaver.docs.Output.runSuites(DisciplineTests)) ``` diff --git a/docs/expectations.md b/docs/expectations.md index 83b36108..622cbeff 100644 --- a/docs/expectations.md +++ b/docs/expectations.md @@ -198,7 +198,7 @@ object ExpectationsSuite extends SimpleIOSuite { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(ExpectationsSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(ExpectationsSuite)) ``` ## Tracing locations of failed expectations @@ -223,5 +223,5 @@ object TracingSuite extends SimpleIOSuite { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(TracingSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(TracingSuite)) ``` diff --git a/docs/funsuite.md b/docs/funsuite.md index 517375c0..a68316bb 100644 --- a/docs/funsuite.md +++ b/docs/funsuite.md @@ -21,20 +21,12 @@ object CatsFunSuite extends weaver.FunSuite { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(CatsFunSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(CatsFunSuite)) ``` A `FunSuite` alias is provided in each of the frameworks supported by weaver: ```scala mdoc -object MonixFunSuite extends weaver.monixcompat.FunSuite { - test("asserts") { expect(Some(5).contains(5)) } -} - -object MonixBIOFunSuite extends weaver.monixbiocompat.FunSuite { - test("asserts") { expect(Some(5).contains(5)) } -} - object ZioBIOFunSuite extends weaver.ziocompat.FunSuite { test("asserts") { expect(Some(5).contains(5)) } } diff --git a/docs/installation.md b/docs/installation.md index 19a5919a..e6a93198 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -14,11 +14,11 @@ import BuildMatrix._ val effectsTable = Table .create("Effect types", effects) - .render(catsEffect3Version, artifactsCE2Version, artifactsCE3Version) + .render(artifactsCE3Version) val integrationsTable = Table .create("Integrations", integrations) - .render(catsEffect3Version, artifactsCE2Version, artifactsCE3Version) + .render(artifactsCE3Version) println(effectsTable) println(integrationsTable) @@ -30,6 +30,4 @@ the effect-type library you've elected to use (or test against). Refer yourself to the library specific pages to get the correct configuration. - [cats](cats_effect_usage.md) -- [monix](monix_usage.md) -- [monix-bio](monix_bio_usage.md) - [zio](zio_usage.md) diff --git a/docs/logging.md b/docs/logging.md index 49ce1dc2..1ee6c14e 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -35,5 +35,5 @@ object LoggedTests extends IOSuite { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(LoggedTests).unsafeRunSync()) +println(weaver.docs.Output.runSuites(LoggedTests)) ``` diff --git a/docs/multiple_suites_failures.md b/docs/multiple_suites_failures.md index fd56aeb1..38776181 100644 --- a/docs/multiple_suites_failures.md +++ b/docs/multiple_suites_failures.md @@ -35,5 +35,5 @@ object MyAnotherSuite extends SimpleIOSuite { The report looks like this: ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(MySuite, MyAnotherSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(MySuite, MyAnotherSuite)) ``` diff --git a/docs/multiple_suites_logging.md b/docs/multiple_suites_logging.md index af008a6d..3137ba00 100644 --- a/docs/multiple_suites_logging.md +++ b/docs/multiple_suites_logging.md @@ -26,14 +26,13 @@ object MySuite extends SimpleIOSuite { } object MyAnotherSuite extends SimpleIOSuite { - import java.util.concurrent.TimeUnit import scala.util.Random.alphanumeric val randomString = IO(alphanumeric.take(10).mkString("")) loggedTest("failure should print logs") { log => for { - currentTime <- timer.clock.realTime(TimeUnit.SECONDS) + currentTime <- IO.realTime.map(_.toSeconds) context = Map("time" -> currentTime.toString, "purpose" -> "docs") _ <- log.info("Starting the test...", context) x <- randomString @@ -46,5 +45,5 @@ object MyAnotherSuite extends SimpleIOSuite { The report would look something like this: ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(MySuite, MyAnotherSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(MySuite, MyAnotherSuite)) ``` diff --git a/docs/multiple_suites_success.md b/docs/multiple_suites_success.md index b8f03f2d..873c9924 100644 --- a/docs/multiple_suites_success.md +++ b/docs/multiple_suites_success.md @@ -42,5 +42,5 @@ object MyAnotherSuite extends SimpleIOSuite { The report looks like this: ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(MySuite, MyAnotherSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(MySuite, MyAnotherSuite)) ``` diff --git a/docs/resources.md b/docs/resources.md index eaeb0c5c..e7c98f98 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -13,16 +13,15 @@ import weaver._ import cats.effect._ // Using http4s -import org.http4s.client.blaze._ +import org.http4s.blaze.client._ import org.http4s.client._ -import scala.concurrent.ExecutionContext.global object HttpSuite extends IOSuite { // Sharing a single http client across all tests override type Res = Client[IO] override def sharedResource : Resource[IO, Res] = - BlazeClientBuilder[IO](global).resource + BlazeClientBuilder[IO].resource // The test receives the shared client as an argument test("Good requests lead to good results") { httpClient => @@ -77,7 +76,7 @@ object ResourceDemo extends IOSuite { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(ResourceDemo).unsafeRunSync()) +println(weaver.docs.Output.runSuites(ResourceDemo)) println("Contents of `order` are:\n") println("```") diff --git a/docs/scalacheck.md b/docs/scalacheck.md index a1afe8ce..b3c4a5a1 100644 --- a/docs/scalacheck.md +++ b/docs/scalacheck.md @@ -77,5 +77,5 @@ object ForallExamples extends SimpleIOSuite with Checkers { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(ForallExamples).unsafeRunSync()) +println(weaver.docs.Output.runSuites(ForallExamples)) ``` diff --git a/docs/specs2.md b/docs/specs2.md index 45346da5..5c245019 100644 --- a/docs/specs2.md +++ b/docs/specs2.md @@ -73,5 +73,5 @@ object MatchersSpec extends SimpleIOSuite with IOMatchers { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(MatchersSpec).unsafeRunSync()) +println(weaver.docs.Output.runSuites(MatchersSpec)) ``` diff --git a/docs/tagging.md b/docs/tagging.md index abd0b9cb..1a3f7295 100644 --- a/docs/tagging.md +++ b/docs/tagging.md @@ -34,5 +34,5 @@ object TaggingSuite extends SimpleIOSuite { ``` ```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(TaggingSuite).unsafeRunSync()) +println(weaver.docs.Output.runSuites(TaggingSuite)) ``` diff --git a/docs/zio_usage.md b/docs/zio_usage.md index 10060aaa..a4a6389a 100644 --- a/docs/zio_usage.md +++ b/docs/zio_usage.md @@ -37,7 +37,7 @@ Assuming the following module : ```scala mdoc import zio._ -import org.http4s.client.blaze._ +import org.http4s.blaze.client._ import org.http4s.client._ object modules { @@ -67,9 +67,8 @@ object HttpSuite extends ZIOSuite[Http] { // Sharing a single layer across all tests override val sharedLayer : ZLayer[ZEnv, Throwable, Http] = ZLayer.fromManaged { - val makeHttpClient = ZIO.runtime[Any].map { implicit rts => - val exec = rts.platform.executor.asEC - BlazeClientBuilder[Task](exec).resource.toManagedZIO + val makeHttpClient = ZIO.runtime[ZEnv].map { implicit runtime => + BlazeClientBuilder[Task].resource.toManagedZIO } Managed.fromEffect(makeHttpClient).flatten } diff --git a/modules/docs/src/main/scala/weaver/MatrixRendering.scala b/modules/docs/src/main/scala/weaver/MatrixRendering.scala index 7e0c9ae4..64ab0cd1 100644 --- a/modules/docs/src/main/scala/weaver/MatrixRendering.scala +++ b/modules/docs/src/main/scala/weaver/MatrixRendering.scala @@ -1,15 +1,10 @@ package weaver.docs -sealed trait CatsEffect -case object CE2 extends CatsEffect -case object CE3 extends CatsEffect - case class Artifact( name: String, jvm: Boolean, js: Boolean, scalaVersion: String, - catsEffect: CatsEffect, version: String ) @@ -21,8 +16,7 @@ case class Cell( case class Row( name: String, - ce2: Option[Cell], - ce3: Option[Cell] + ce: Option[Cell] ) case class Table( @@ -53,22 +47,18 @@ case class Table( } } - def render( - catsEffect3Version: String, - ce2ArtifactsVersion: String, - ce3ArtifactsVersion: String) = { + def render(version: String) = { val sb = new StringBuilder sb.append(_row( Seq( name, - s"Cats Effect 2

Weaver version: `$ce2ArtifactsVersion`", - s"Cats Effect $catsEffect3Version

Weaver version: `$ce3ArtifactsVersion`" + s"Cats Effect 3

Weaver version: `$version`" ), header = true )) - rows.map { case Row(name, ce2, ce3) => - sb.append(_row(Seq(name, _cell(ce2), _cell(ce3)))) + rows.map { case Row(name, ce) => + sb.append(_row(Seq(name, _cell(ce)))) } sb.result() } @@ -78,8 +68,6 @@ object Table { def row_name(artif: String) = artif match { case "cats" => "Cats-Effect" case "zio" => "ZIO" - case "monix" => "Monix" - case "monix-bio" => "Monix BIO" case "scalacheck" => "ScalaCheck" case "specs2" => "Specs2 matchers" case "discipline" => "Discipline law testing" @@ -107,12 +95,7 @@ object Table { case (name, artifacts) => val rowName = row_name(name) - val ce2Artifacts = artifacts.filter(_.catsEffect == CE2) - val ce3Artifacts = artifacts.filter(_.catsEffect == CE3) - - Row(rowName, - artifactsToCell(ce2Artifacts), - artifactsToCell(ce3Artifacts)) + Row(rowName, artifactsToCell(artifacts)) } Table( diff --git a/modules/docs/src/main/scala/weaver/Output.scala b/modules/docs/src/main/scala/weaver/Output.scala index f555ba52..81138bd8 100644 --- a/modules/docs/src/main/scala/weaver/Output.scala +++ b/modules/docs/src/main/scala/weaver/Output.scala @@ -2,13 +2,10 @@ package weaver.docs import weaver._ import cats.effect._ -import cats.effect.concurrent.Ref +import cats.effect.Ref import cats.data.NonEmptyChain -import scala.concurrent.ExecutionContext.Implicits.global object Output { - implicit val cs = IO.contextShift(global) - def format(s: String) = { Ansi2Html(removeTrailingNewLine( removeTrailingNewLine( @@ -20,11 +17,12 @@ object Output { if (s.endsWith("\n")) s.substring(0, s.length - 2) else s } - def runSuites(s: Suite[IO]*): IO[String] = { + def runSuites(s: Suite[IO]*): String = { + import cats.effect.unsafe.implicits.global val header = "
"
     val footer = "
" - for { + val program = for { buf <- Ref.of[IO, NonEmptyChain[String]](NonEmptyChain(header)) printLine = (s: String) => buf.update(_.append(format(s))) runner = new Runner[IO](Nil, 10)(s => printLine(s)) @@ -33,6 +31,8 @@ object Output { _ <- printLine(footer) value <- buf.get } yield value.reduceLeft(_ + "\n" + _) + + program.unsafeRunSync() } } From b6af8a2c962274077064f8774de7080f6c3df2dc Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 6 Jul 2022 18:46:35 +0100 Subject: [PATCH 03/33] Remove compatibility layer --- modules/core/src/weaver/CECompat.scala | 69 ------------------- modules/core/src/weaver/GlobalResourceF.scala | 20 +++--- modules/core/src/weaver/Log.scala | 3 +- .../core/src/weaver/MemoisedResource.scala | 2 - modules/core/src/weaver/Runner.scala | 54 ++++++++------- modules/core/src/weaver/Test.scala | 3 +- modules/core/src/weaver/suites.scala | 6 +- .../framework/cats/test/src-jvm/MetaJVM.scala | 16 ++--- .../cats/test/src/MemoisedResourceTests.scala | 2 - modules/framework/src-js/DogFoodCompat.scala | 2 +- modules/framework/src-js/RunnerCompat.scala | 22 +++--- modules/framework/src-jvm/RunnerCompat.scala | 35 ++++++---- 12 files changed, 89 insertions(+), 145 deletions(-) delete mode 100644 modules/core/src/weaver/CECompat.scala diff --git a/modules/core/src/weaver/CECompat.scala b/modules/core/src/weaver/CECompat.scala deleted file mode 100644 index 8513263a..00000000 --- a/modules/core/src/weaver/CECompat.scala +++ /dev/null @@ -1,69 +0,0 @@ -package weaver - -import cats.Applicative -import cats.effect.kernel.GenConcurrent -import cats.effect.kernel.Resource.ExitCase.{ Canceled, Errored, Succeeded } -import cats.effect.syntax.all._ -import cats.effect.{ Async, Resource } -import cats.syntax.all._ - -private[weaver] object CECompat extends CECompat - -private[weaver] trait CECompat { - - private[weaver] type Effect[F[_]] = Async[F] - - private[weaver] type Ref[F[_], A] = cats.effect.kernel.Ref[F, A] - private[weaver] val Ref = cats.effect.kernel.Ref - - private[weaver] type Deferred[F[_], A] = cats.effect.kernel.Deferred[F, A] - private[weaver] val Deferred = cats.effect.kernel.Deferred - - private[weaver] type Semaphore[F[_]] = cats.effect.std.Semaphore[F] - private[weaver] val Semaphore = cats.effect.std.Semaphore - - private[weaver] def guaranteeCase[F[_]: Async, A]( - fa: F[A])( - cancelled: => F[Unit], - completed: => F[Unit], - errored: Throwable => F[Unit]): F[A] = - Async[F].guaranteeCase(fa)(_.fold(cancelled, errored, _ *> completed)) - - private[weaver] def guarantee[F[_]: Async, A]( - fa: F[A])(fin: F[Unit]): F[A] = - Async[F].guarantee(fa, fin) - - private[weaver] def onErrorEnsure[F[_]: Async, A](r: Resource[F, A])( - f: Throwable => F[Unit]): Resource[F, A] = - r.onFinalizeCase { - case Canceled => Async[F].unit - case Succeeded => Async[F].unit - case Errored(e) => f(e) - } - - private[weaver] def background[F[_]: Async, A, B](fa: F[A], default: A)( - f: F[A] => F[B]): F[B] = - fa.background.use(fOutcome => - f(fOutcome.flatMap(_.embed(onCancel = Async[F].pure(default))))) - - private[weaver] def resourceLift[F[_]: Applicative, A]( - fa: F[A]): Resource[F, A] = Resource.eval(fa) - - private[weaver] trait Queue[F[_], A] { - protected def ceQueue: cats.effect.std.Queue[F, A] - - def enqueue(a: A): F[Unit] = ceQueue.offer(a) - def dequeueStream: fs2.Stream[F, A] = fs2.Stream.repeatEval(ceQueue.take) - } - - object Queue { - def unbounded[F[_], A](implicit gc: GenConcurrent[F, _]) = - cats.effect.std.Queue.unbounded[F, A].map { - q => - new Queue[F, A] { - override val ceQueue = q - } - } - } - -} diff --git a/modules/core/src/weaver/GlobalResourceF.scala b/modules/core/src/weaver/GlobalResourceF.scala index 3a511f8e..81a1721a 100644 --- a/modules/core/src/weaver/GlobalResourceF.scala +++ b/modules/core/src/weaver/GlobalResourceF.scala @@ -9,8 +9,6 @@ import cats.syntax.all._ import org.portablescala.reflect.annotation.EnableReflectiveInstantiation -import CECompat.Ref - /** * Top-level instances of this trait are detected by the framework and used to * manage the lifecycle of shared resources. @@ -33,7 +31,7 @@ trait GlobalResourceF[F[_]] extends GlobalResourceBase { object GlobalResourceF { trait Write[F[_]] { - protected implicit def F: CECompat.Effect[F] + protected implicit def F: Async[F] protected def rawPut[A]( pureOrLazy: Either[A, Resource[F, A]], label: Option[String])(implicit rt: ResourceTag[A]): F[Unit] @@ -42,7 +40,7 @@ object GlobalResourceF { implicit rt: ResourceTag[A]): F[Unit] = rawPut(Left(value), label) def putR[A](value: A, label: Option[String] = None)( implicit rt: ResourceTag[A]): Resource[F, Unit] = - CECompat.resourceLift(put(value, label)) + Resource.eval(put(value, label)) /** * Memoises a resource so to optimise its sharing. The memoised resource @@ -66,7 +64,7 @@ object GlobalResourceF { resource: Resource[F, A], label: Option[String] = None)(implicit rt: ResourceTag[A]): Resource[F, Unit] = - CECompat.resourceLift(putLazy(resource, label)) + Resource.eval(putLazy(resource, label)) } trait Read[F[_]] { @@ -83,7 +81,7 @@ object GlobalResourceF { def getR[A](label: Option[String] = None)( implicit rt: ResourceTag[A]): Resource[F, Option[A]] = - CECompat.resourceLift { + Resource.eval { rawGet[A](label) }.flatMap { case Some(Left(value)) => Resource.pure(Some(value)) @@ -105,14 +103,14 @@ object GlobalResourceF { getR[A](label).flatMap { case Some(value) => Resource.pure[F, A](value) case None => - CECompat.resourceLift(F.raiseError(GlobalResourceF.ResourceNotFound( + Resource.eval(F.raiseError(GlobalResourceF.ResourceNotFound( label, rt.description))) } } object Read { - def empty[F[_]](effect: CECompat.Effect[F]): Read[F] = new Read[F] { + def empty[F[_]](effect: Async[F]): Read[F] = new Read[F] { implicit protected def F: MonadError[F, Throwable] = effect protected def rawGet[A](label: Option[String])(implicit @@ -121,7 +119,7 @@ object GlobalResourceF { } } - def createMap[F[_]: CECompat.Effect]: F[Read[F] with Write[F]] = + def createMap[F[_]: Async]: F[Read[F] with Write[F]] = Ref[F] .of(Map.empty[(Option[String], ResourceTag[_]), Either[Any, Resource[F, Any]]]) @@ -131,7 +129,7 @@ object GlobalResourceF { ref: Ref[ F, Map[(Option[String], ResourceTag[_]), Either[Any, Resource[F, Any]]]])( - implicit val F: CECompat.Effect[F]) + implicit val F: Async[F]) extends Read[F] with Write[F] { self => @@ -150,7 +148,7 @@ object GlobalResourceF { case None => F.raiseError(ResourceNotFound(label, rt.description)) case Some(value) => F.pure(value) - }.flatMap(CECompat.resourceLift(_)))) + }.flatMap(Resource.eval(_)))) } } diff --git a/modules/core/src/weaver/Log.scala b/modules/core/src/weaver/Log.scala index 9b233894..b8ffa2c5 100644 --- a/modules/core/src/weaver/Log.scala +++ b/modules/core/src/weaver/Log.scala @@ -1,10 +1,9 @@ package weaver +import cats.effect.Ref import cats.syntax.all._ import cats.{ Applicative, FlatMap, Monoid, MonoidK, Show, ~> } -import CECompat.Ref - abstract class Log[F[_]: FlatMap](timestamp: F[Long]) { self => def log(l: => Log.Entry): F[Unit] diff --git a/modules/core/src/weaver/MemoisedResource.scala b/modules/core/src/weaver/MemoisedResource.scala index 730ff7a4..fea049e5 100644 --- a/modules/core/src/weaver/MemoisedResource.scala +++ b/modules/core/src/weaver/MemoisedResource.scala @@ -3,8 +3,6 @@ package weaver import cats.effect._ import cats.syntax.all._ -import CECompat.{ Deferred, Ref } - object MemoisedResource { def apply[F[_]: Concurrent, A]( resource: Resource[F, A]): F[Resource[F, A]] = diff --git a/modules/core/src/weaver/Runner.scala b/modules/core/src/weaver/Runner.scala index 95ec72cf..d1a013a7 100644 --- a/modules/core/src/weaver/Runner.scala +++ b/modules/core/src/weaver/Runner.scala @@ -2,12 +2,14 @@ package weaver import cats.Monoid import cats.data.Chain +import cats.effect.std.Queue +import cats.effect.{ Async, Ref } import cats.syntax.all._ import TestOutcome.{ Summary, Verbose } import Colours._ -class Runner[F[_]: CECompat.Effect]( +class Runner[F[_]: Async]( args: List[String], maxConcurrentSuites: Int)( printLine: String => F[Unit]) { @@ -15,14 +17,19 @@ class Runner[F[_]: CECompat.Effect]( import Runner._ // Signaling option, because we need to detect completion - private type Channel[A] = CECompat.Queue[F, Option[A]] + private type Channel[A] = Queue[F, Option[A]] + + private[this] def background[A, B](fa: F[A], default: A)( + f: F[A] => F[B]): F[B] = + Async[F].background(fa).use(fOutcome => + f(fOutcome.flatMap(_.embed(onCancel = Async[F].pure(default))))) def run(suites: fs2.Stream[F, Suite[F]]): F[Outcome] = for { - buffer <- CECompat.Ref[F].of(Chain.empty[SpecEvent]) - channel <- CECompat.Queue.unbounded[F, Option[SpecEvent]] + buffer <- Ref[F].of(Chain.empty[SpecEvent]) + channel <- Queue.unbounded[F, Option[SpecEvent]] outcome <- - CECompat.background(consume(channel, buffer), Outcome.empty) { res => + background(consume(channel, buffer), Outcome.empty) { res => suites .parEvalMap(math.max(1, maxConcurrentSuites)) { suite => suite @@ -38,16 +45,16 @@ class Runner[F[_]: CECompat.Effect]( } yield outcome private def produce(ch: Channel[SpecEvent])(event: SpecEvent): F[Unit] = - ch.enqueue(Some(event)) + ch.offer(Some(event)) private def complete(channel: Channel[SpecEvent]): F[Unit] = - channel.enqueue(None) // We are done ! + channel.offer(None) // We are done ! // Recursively consumes from a channel until a "None" gets produced, // indicating the end of the stream. private def consume( ch: Channel[SpecEvent], - buffer: CECompat.Ref[F, Chain[SpecEvent]]): F[Outcome] = { + buffer: Ref[F, Chain[SpecEvent]]): F[Outcome] = { val stars = "*************" @@ -73,21 +80,22 @@ class Runner[F[_]: CECompat.Effect]( } yield outcome } - ch.dequeueStream.unNoneTerminate.evalMap( - handle).compile.foldMonoid.flatMap { - outcome => - for { - failures <- buffer.get - _ <- (printLine(red(stars) + "FAILURES" + red(stars)) *> failures - .traverse[F, Unit] { specEvent => - printLine(cyan(specEvent.name)) *> - specEvent.events.traverse(printTestEvent(Verbose)) *> - newLine - } - .void).whenA(failures.nonEmpty) - _ <- printLine(outcome.formatted) - } yield outcome - } + fs2.Stream.repeatEval(ch.take) + .unNoneTerminate.evalMap(handle) + .compile.foldMonoid.flatMap { + outcome => + for { + failures <- buffer.get + _ <- (printLine(red(stars) + "FAILURES" + red(stars)) *> failures + .traverse[F, Unit] { specEvent => + printLine(cyan(specEvent.name)) *> + specEvent.events.traverse(printTestEvent(Verbose)) *> + newLine + } + .void).whenA(failures.nonEmpty) + _ <- printLine(outcome.formatted) + } yield outcome + } } } diff --git a/modules/core/src/weaver/Test.scala b/modules/core/src/weaver/Test.scala index 23be5a59..397ee715 100644 --- a/modules/core/src/weaver/Test.scala +++ b/modules/core/src/weaver/Test.scala @@ -5,10 +5,9 @@ import scala.util.{ Failure, Success, Try } import cats.Defer import cats.data.Chain +import cats.effect.Ref import cats.syntax.all._ -import CECompat.Ref - object Test { def apply[F[_]](name: String, f: Log[F] => F[Expectations])( diff --git a/modules/core/src/weaver/suites.scala b/modules/core/src/weaver/suites.scala index 8714df5a..8889857d 100644 --- a/modules/core/src/weaver/suites.scala +++ b/modules/core/src/weaver/suites.scala @@ -1,6 +1,6 @@ package weaver -import cats.effect.Resource +import cats.effect.{ Async, Resource } import cats.syntax.all._ import fs2.Stream @@ -19,7 +19,7 @@ trait Suite[F[_]] extends BaseSuiteClass { // A version of EffectSuite that has a type member instead of a type parameter. protected[weaver] trait EffectSuiteAux { type EffectType[A] - implicit protected def effect: CECompat.Effect[EffectType] + implicit protected def effect: Async[EffectType] } // format: off @@ -27,7 +27,7 @@ trait EffectSuite[F[_]] extends Suite[F] with EffectSuiteAux with SourceLocation final type EffectType[A] = F[A] implicit protected def effectCompat: EffectCompat[F] - implicit final protected def effect: CECompat.Effect[F] = effectCompat.effect + implicit final protected def effect: Async[F] = effectCompat.effect /** * Raise an error that leads to the running test being tagged as "cancelled". diff --git a/modules/framework/cats/test/src-jvm/MetaJVM.scala b/modules/framework/cats/test/src-jvm/MetaJVM.scala index 98afb0b7..615fbad1 100644 --- a/modules/framework/cats/test/src-jvm/MetaJVM.scala +++ b/modules/framework/cats/test/src-jvm/MetaJVM.scala @@ -50,8 +50,8 @@ object MetaJVM { class LazyState( initialised: IO[Int], finalised: IO[Int], - totalUses: CECompat.Ref[IO, Int], - uses: CECompat.Ref[IO, Int]) { + totalUses: Ref[IO, Int], + uses: Ref[IO, Int]) { val getState: IO[(Int, Int, Int, Int)] = for { i <- initialised f <- finalised @@ -62,13 +62,13 @@ object MetaJVM { object LazyGlobal extends GlobalResource { def sharedResources(global: weaver.GlobalWrite): Resource[IO, Unit] = - CECompat.resourceLift { + Resource.eval { for { - initialised <- CECompat.Ref[IO].of(0) - finalised <- CECompat.Ref[IO].of(0) - totalUses <- CECompat.Ref[IO].of(0) + initialised <- Ref[IO].of(0) + finalised <- Ref[IO].of(0) + totalUses <- Ref[IO].of(0) resource = - CECompat.resourceLift(CECompat.Ref[IO].of(0)).flatMap { uses => + Resource.eval(Ref[IO].of(0)).flatMap { uses => Resource.make(initialised.update(_ + 1))(_ => finalised.update(_ + 1)).map(_ => new LazyState(initialised.get, finalised.get, totalUses, uses)) @@ -101,7 +101,7 @@ object MetaJVM { extends IOSuite { type Res = LazyState def sharedResource: Resource[IO, Res] = - CECompat.resourceLift(IO.sleep(index * 500.millis)).flatMap(_ => + Resource.eval(IO.sleep(index * 500.millis)).flatMap(_ => global.getOrFailR[LazyState]()) test("Lazy resources should be instantiated several times") { state => diff --git a/modules/framework/cats/test/src/MemoisedResourceTests.scala b/modules/framework/cats/test/src/MemoisedResourceTests.scala index a6bff9e9..112643e4 100644 --- a/modules/framework/cats/test/src/MemoisedResourceTests.scala +++ b/modules/framework/cats/test/src/MemoisedResourceTests.scala @@ -7,8 +7,6 @@ import scala.concurrent.duration._ import cats.effect._ import cats.syntax.all._ -import CECompat.Ref - object MemoisedResourceTests extends SimpleIOSuite { test("""|Memoised resources should be: diff --git a/modules/framework/src-js/DogFoodCompat.scala b/modules/framework/src-js/DogFoodCompat.scala index 48f1cdfb..f9bbf85b 100644 --- a/modules/framework/src-js/DogFoodCompat.scala +++ b/modules/framework/src-js/DogFoodCompat.scala @@ -34,7 +34,7 @@ private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => private[weaver] trait DogFoodCompanion { def make[F[_]](framework: WeaverFramework[F]): Resource[F, DogFood[F]] = { import framework.unsafeRun.effect - CECompat.resourceLift(effect.delay(new DogFood(framework) { + Resource.eval(effect.delay(new DogFood(framework) { def blocker = new BlockerCompat[F] { // can't block on javascript obviously def block[A](thunk: => A): F[A] = effect.delay(thunk) diff --git a/modules/framework/src-js/RunnerCompat.scala b/modules/framework/src-js/RunnerCompat.scala index f08230c1..7e43dddc 100644 --- a/modules/framework/src-js/RunnerCompat.scala +++ b/modules/framework/src-js/RunnerCompat.scala @@ -7,13 +7,12 @@ import scala.scalajs.js import scala.scalajs.js.JSON import cats.data.Chain -import cats.effect.Sync +import cats.effect.kernel.Async +import cats.effect.{ Ref, Sync } import cats.syntax.all._ import sbt.testing.{ EventHandler, Logger, Task, TaskDef } -import CECompat.Ref - trait RunnerCompat[F[_]] { self: sbt.testing.Runner => protected val args: Array[String] protected val suiteLoader: SuiteLoader[F] @@ -143,12 +142,17 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => case None => effect.unit case Some(loader) => for { outcomes <- Ref.of(Chain.empty[TestOutcome]) - _ <- CECompat.guaranteeCase(loader.suite - .flatMap(runSuite(fqn, _, outcomes)))( - cancelled = effect.unit, - completed = finaliseCompleted(outcomes), - errored = finaliseError(outcomes) - ) + _ <- + Async[F] + .guaranteeCase(loader.suite.flatMap(runSuite(fqn, + _, + outcomes)))( + _.fold( + canceled = effect.unit, + completed = _ *> finaliseCompleted(outcomes), + errored = finaliseError(outcomes) + ) + ) } yield () } diff --git a/modules/framework/src-jvm/RunnerCompat.scala b/modules/framework/src-jvm/RunnerCompat.scala index a95f8010..8494d466 100644 --- a/modules/framework/src-jvm/RunnerCompat.scala +++ b/modules/framework/src-jvm/RunnerCompat.scala @@ -10,14 +10,12 @@ import scala.concurrent.{ ExecutionContext, Promise } import scala.util.Try import cats.data.Chain -import cats.effect.{ Sync, _ } +import cats.effect.std.Semaphore +import cats.effect.{ Ref, Sync, _ } import cats.syntax.all._ import sbt.testing.{ Task, TaskDef } -import CECompat.Ref -import CECompat.Semaphore - trait RunnerCompat[F[_]] { self: sbt.testing.Runner => protected val suiteLoader: SuiteLoader[F] @@ -142,6 +140,16 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => tasks(Array(deserializer(task))).head } + private[this] def onErrorEnsure[A](r: Resource[F, A])( + f: Throwable => F[Unit]): Resource[F, A] = { + import Resource.ExitCase._ + r.onFinalizeCase { + case Canceled => Async[F].unit + case Succeeded => Async[F].unit + case Errored(e) => f(e) + } + } + private def run( globalResources: List[GlobalResourceF[F]], waitForResourcesShutdown: java.util.concurrent.Semaphore, @@ -149,7 +157,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => gate: Promise[Unit]): F[Unit] = { def preventDeadlock[A](resource: Resource[F, A]) = { - CECompat.onErrorEnsure(resource) { + onErrorEnsure(resource) { error => effect.delay(isDone.set(true)) *> effect.delay(error.printStackTrace(errorStream)) *> @@ -187,7 +195,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => private def resourceMap( globalResources: List[GlobalResourceF[F]] ): Resource[F, GlobalResourceF.Read[F]] = - CECompat.resourceLift(GlobalResourceF.createMap[F]).flatTap { map => + Resource.eval(GlobalResourceF.createMap[F]).flatTap { map => globalResources.traverse(_.sharedResources(map)).void } @@ -217,9 +225,9 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => val finalizer = maybePublish.productR(broker.send(SuiteFinished(SuiteName(fqn)))) - CECompat.guaranteeCase(runSuite)( - completed = finalizer, - cancelled = finalizer, + Async[F].guaranteeCase(runSuite)(_.fold( + completed = _ *> finalizer, + canceled = finalizer, errored = { (error: Throwable) => val outcome = TestOutcome("Unexpected failure", @@ -227,11 +235,12 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => Result.from(error), Chain.empty) - CECompat.guarantee(outcomes - .update(_.append(SuiteName(fqn) -> outcome)) - .productR(broker.send(TestFinished(outcome))))(finalizer) + Async[F].guarantee(outcomes + .update(_.append(SuiteName(fqn) -> outcome)) + .productR(broker.send(TestFinished(outcome))), + finalizer) } - ).handleErrorWith { case scala.util.control.NonFatal(_) => + )).handleErrorWith { case scala.util.control.NonFatal(_) => effect.unit // avoid non-fatal errors propagating up } } From f9051ed3ac0bcf5658f08a818b1a13d4333449c9 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 6 Jul 2022 19:06:58 +0100 Subject: [PATCH 04/33] Log SBT events to trace those damn failing tests --- .../cats/test/src-jvm/DogFoodTestsJVM.scala | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/modules/framework/cats/test/src-jvm/DogFoodTestsJVM.scala b/modules/framework/cats/test/src-jvm/DogFoodTestsJVM.scala index 16eb72b4..ed3cc4f3 100644 --- a/modules/framework/cats/test/src-jvm/DogFoodTestsJVM.scala +++ b/modules/framework/cats/test/src-jvm/DogFoodTestsJVM.scala @@ -13,6 +13,28 @@ object DogFoodTestsJVM extends IOSuite { def sharedResource: Resource[IO, DogFood[IO]] = DogFood.make(new CatsEffect) + def logState(logger: weaver.Log[IO])(st: DogFood.State): IO[Unit] = { + val (logs, events) = st + import weaver.framework.LoggedEvent._ + + val dumpLogs = logs.traverse { le => + le match { + case Debug(msg) => logger.debug(s"LOG: $msg") + case Warn(msg) => logger.debug(s"LOG: $msg") + case Trace(msg) => logger.debug(s"TRACE: $msg") + case Info(msg) => logger.debug(s"LOG: $msg") + case Error(msg) => logger.error(s"LOG $msg") + } + }.void + + val dumpOutcomes = events.traverse { ev => + val t = if (ev.throwable().isDefined()) ev.throwable.get() else null + logger.info(s"SBT EVENT: ${ev.status()}", cause = t) + }.void + + dumpLogs *> dumpOutcomes + } + // This tests the global resource sharing mechanism by running a suite that // acquires a temporary file that gets created during global resource initialisation. // The suite contains a test which logs the location of the file and fails to ensure logs @@ -20,11 +42,11 @@ object DogFoodTestsJVM extends IOSuite { // We then recover the location of the file, which happens after the dogfooding framework finishes // its run. At this point, the file should have been deleted by the global resource initialisation // mechanism, which we test for. - test("global sharing suites") { dogfood => + test("global sharing suites") { (dogfood, log) => import dogfood._ runSuites(moduleSuite(Meta.MutableSuiteTest), sharingSuite[MetaJVM.TmpFileSuite], - globalInit(MetaJVM.GlobalStub)).flatMap { + globalInit(MetaJVM.GlobalStub)).flatTap(logState(log)).flatMap { case (logs, events) => val file = logs.collectFirst { case LoggedEvent.Error(msg) if msg.contains("file:") => @@ -44,14 +66,14 @@ object DogFoodTestsJVM extends IOSuite { } } - test("global lazy resources (parallel)") { dogfood => + test("global lazy resources (parallel)") { (dogfood, log) => import dogfood._ runSuites( globalInit(MetaJVM.LazyGlobal), sharingSuite[MetaJVM.LazyAccessParallel], sharingSuite[MetaJVM.LazyAccessParallel], sharingSuite[MetaJVM.LazyAccessParallel] - ).map { + ).flatTap(logState(log)).map { case (_, events) => val successCount = events.toList.map(_.status()).count { case Status.Success => true; case _ => false @@ -61,7 +83,7 @@ object DogFoodTestsJVM extends IOSuite { } - test("global lazy resources (sequential)") { dogfood => + test("global lazy resources (sequential)") { (dogfood, log) => import dogfood._ runSuites( Seq( @@ -71,7 +93,7 @@ object DogFoodTestsJVM extends IOSuite { sharingSuite[MetaJVM.LazyAccessSequential2] ), maxParallelism = 1 - ).map { + ).flatTap(logState(log)).map { case (_, events) => val successCount = events.toList.map(_.status()).count { case Status.Success => true; case _ => false From 76e71fc45911545d6ec17be372f7179c72b55980 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Thu, 7 Jul 2022 19:05:30 +0100 Subject: [PATCH 05/33] Inline background method --- modules/core/src/weaver/Runner.scala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/core/src/weaver/Runner.scala b/modules/core/src/weaver/Runner.scala index d1a013a7..18b0e206 100644 --- a/modules/core/src/weaver/Runner.scala +++ b/modules/core/src/weaver/Runner.scala @@ -19,17 +19,14 @@ class Runner[F[_]: Async]( // Signaling option, because we need to detect completion private type Channel[A] = Queue[F, Option[A]] - private[this] def background[A, B](fa: F[A], default: A)( - f: F[A] => F[B]): F[B] = - Async[F].background(fa).use(fOutcome => - f(fOutcome.flatMap(_.embed(onCancel = Async[F].pure(default))))) - def run(suites: fs2.Stream[F, Suite[F]]): F[Outcome] = for { buffer <- Ref[F].of(Chain.empty[SpecEvent]) channel <- Queue.unbounded[F, Option[SpecEvent]] outcome <- - background(consume(channel, buffer), Outcome.empty) { res => + Async[F].background(consume(channel, buffer)).use { outcome => + val res = outcome.flatMap(_.embed(onCancel = Outcome.empty.pure[F])) + suites .parEvalMap(math.max(1, maxConcurrentSuites)) { suite => suite From f56fb5fd74dc69a200b8d7c43fbb8be6d26a361f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Mon, 12 Sep 2022 10:34:00 +0200 Subject: [PATCH 06/33] wip --- .../src-native/org/junit/runner/RunWith.scala | 6 ++++ .../src-native/weaver/PlatformCompat.scala | 8 +++++ .../weaver/internals/Timestamp.scala | 31 +++++++++++++++++++ .../weaver/junit/WeaverRunner.scala | 6 ++++ modules/core/src/weaver/Platform.scala | 6 ++-- project/WeaverPlugin.scala | 15 +++++---- project/build.properties | 2 +- 7 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 modules/core/src-native/org/junit/runner/RunWith.scala create mode 100644 modules/core/src-native/weaver/PlatformCompat.scala create mode 100644 modules/core/src-native/weaver/internals/Timestamp.scala create mode 100644 modules/core/src-native/weaver/junit/WeaverRunner.scala diff --git a/modules/core/src-native/org/junit/runner/RunWith.scala b/modules/core/src-native/org/junit/runner/RunWith.scala new file mode 100644 index 00000000..98c3708b --- /dev/null +++ b/modules/core/src-native/org/junit/runner/RunWith.scala @@ -0,0 +1,6 @@ +package org.junit.runner + +/** + * Stub used for cross-compilation + */ +class RunWith[T](cls: Class[T]) extends scala.annotation.StaticAnnotation diff --git a/modules/core/src-native/weaver/PlatformCompat.scala b/modules/core/src-native/weaver/PlatformCompat.scala new file mode 100644 index 00000000..f981fbf9 --- /dev/null +++ b/modules/core/src-native/weaver/PlatformCompat.scala @@ -0,0 +1,8 @@ +package weaver + +private[weaver] object PlatformCompat { + val platform: Platform = Platform.Native + + def getClassLoader(clazz: java.lang.Class[_]): ClassLoader = + new ClassLoader() {} +} diff --git a/modules/core/src-native/weaver/internals/Timestamp.scala b/modules/core/src-native/weaver/internals/Timestamp.scala new file mode 100644 index 00000000..4d8df6b5 --- /dev/null +++ b/modules/core/src-native/weaver/internals/Timestamp.scala @@ -0,0 +1,31 @@ +package weaver.internals + +import scalajs.js.Date + +private[weaver] object Timestamp { + + def format(l: Long): String = { + val date = new Date(0) + + date.setMilliseconds(l.toDouble) + + val hour = date.getHours() + val minutes = date.getMinutes() + val seconds = date.getSeconds() + + s"$hour:$minutes:$seconds" + } + + def now(): Long = Date.now().toLong + + def localTime(hours: Int, minutes: Int, seconds: Int): Long = { + val date = new Date(Date.now()) + + date.setHours(hours.toDouble, + min = minutes.toDouble, + sec = seconds.toDouble) + + date.getTime().toLong + } + +} diff --git a/modules/core/src-native/weaver/junit/WeaverRunner.scala b/modules/core/src-native/weaver/junit/WeaverRunner.scala new file mode 100644 index 00000000..f74d83df --- /dev/null +++ b/modules/core/src-native/weaver/junit/WeaverRunner.scala @@ -0,0 +1,6 @@ +package weaver.junit + +/** + * Stub used for cross-compilation + */ +class WeaverRunner() diff --git a/modules/core/src/weaver/Platform.scala b/modules/core/src/weaver/Platform.scala index 9c9d6b67..5c42b6c6 100644 --- a/modules/core/src/weaver/Platform.scala +++ b/modules/core/src/weaver/Platform.scala @@ -5,8 +5,10 @@ sealed abstract class Platform(val name: String) object Platform { def isJVM: Boolean = PlatformCompat.platform == JVM def isJS: Boolean = PlatformCompat.platform == JS + def isNative: Boolean = PlatformCompat.platform == Native def isScala3: Boolean = ScalaCompat.isScala3 - case object JS extends Platform("js") - case object JVM extends Platform("jvm") + case object JS extends Platform("js") + case object JVM extends Platform("jvm") + case object Native extends Platform("native") } diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index 4b99fdb9..ef5419cb 100644 --- a/project/WeaverPlugin.scala +++ b/project/WeaverPlugin.scala @@ -67,21 +67,23 @@ object WeaverPlugin extends AutoPlugin { ): ConfigureX = { scalaVersions.map(addOne(_, platform)).reduce(_ andThen _) } - def full = sparse(true, true) + def full = sparse(true, true, true) def sparse( - withJS: Boolean, - withScala3: Boolean + withJS: Boolean = false, + withNative: Boolean = false, + withScala3: Boolean = false ): ProjectMatrix = { val defaultScalaVersions = supportedScala2Versions val defaultPlatform = List(VirtualAxis.jvm) val addJs = if (withJS) List(VirtualAxis.js) else Nil + val addNative = if (withNative) List(VirtualAxis.native) else Nil val addScala3 = if (withScala3) List(scala3) else Nil val configurators = for { scalaVersion <- defaultScalaVersions ++ addScala3 - platform <- defaultPlatform ++ addJs + platform <- defaultPlatform ++ addJs ++ addNative } yield addOne(scalaVersion, platform) val configure: ConfigureX = configurators.reduce(_ andThen _) @@ -298,8 +300,9 @@ object WeaverPlugin extends AutoPlugin { Def.setting((Compile / scalaSource).value.getParentFile().getParentFile().getParentFile()) def suffixes(axes: Seq[VirtualAxis]) = axes.collect { - case VirtualAxis.js => List("", "-js") - case VirtualAxis.jvm => List("", "-jvm") + case VirtualAxis.js => List("", "-js") + case VirtualAxis.jvm => List("", "-jvm") + case VirtualAxis.native => List("", "-native") case ScalaVersionAxis(ver, _) => if (ver.startsWith("3.")) List("", "-scala-3") else List("", "-scala-2") diff --git a/project/build.properties b/project/build.properties index 20747124..22af2628 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.0-RC2 +sbt.version=1.7.1 From 8cfc2c3e0022ea175cc10fd6c5a53e2935cf0cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Mon, 12 Sep 2022 17:47:33 +0200 Subject: [PATCH 07/33] WIP --- .sbtopts | 3 + build.sbt | 4 +- .../weaver/CatsUnsafeRunPlatformCompat.scala | 19 ++ .../weaver/internals/Timestamp.scala | 45 ++-- .../src/weaver/framework/CatsFramework.scala | 4 +- .../framework/src-native/DogFoodCompat.scala | 45 ++++ .../framework/src-native/RunnerCompat.scala | 215 ++++++++++++++++++ modules/framework/src-native/TaskCompat.scala | 6 + .../src/weaver/framework/DogFood.scala | 4 +- project/WeaverPlugin.scala | 14 +- project/plugins.sbt | 1 + 11 files changed, 333 insertions(+), 27 deletions(-) create mode 100644 .sbtopts create mode 100644 modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala create mode 100644 modules/framework/src-native/DogFoodCompat.scala create mode 100644 modules/framework/src-native/RunnerCompat.scala create mode 100644 modules/framework/src-native/TaskCompat.scala diff --git a/.sbtopts b/.sbtopts new file mode 100644 index 00000000..224cb189 --- /dev/null +++ b/.sbtopts @@ -0,0 +1,3 @@ +-J-Xms2g +-J-Xmx4g +-J-XX:MaxMetaspaceSize=512m diff --git a/build.sbt b/build.sbt index 595f8c7e..7e160114 100644 --- a/build.sbt +++ b/build.sbt @@ -36,8 +36,8 @@ Global / (Test / testOptions) += Tests.Argument("--quickstart") val Version = new { object CE3 { - val fs2 = "3.2.9" - val cats = "3.3.13" + val fs2 = "3.2.14-75-7902cbf" + val cats = "3.3.14-1-5d11fe9" val zioInterop = "3.2.9.1" } diff --git a/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala new file mode 100644 index 00000000..01a93568 --- /dev/null +++ b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala @@ -0,0 +1,19 @@ +package weaver + +import cats.effect.IO +import scala.concurrent.Await +import scala.concurrent.duration._ +import cats.effect.unsafe.implicits.global + +private[weaver] trait CatsUnsafeRunPlatformCompat { + self: CatsUnsafeRun => + + def sync(task: IO[Unit]): Unit = { + val future = task.unsafeToFuture() + scalanative.runtime.loop() + Await.result(future, 1.minute) + } + + def background(task: IO[Unit]): CancelToken = ??? + +} diff --git a/modules/core/src-native/weaver/internals/Timestamp.scala b/modules/core/src-native/weaver/internals/Timestamp.scala index 4d8df6b5..f7cc2f0b 100644 --- a/modules/core/src-native/weaver/internals/Timestamp.scala +++ b/modules/core/src-native/weaver/internals/Timestamp.scala @@ -1,31 +1,34 @@ package weaver.internals -import scalajs.js.Date +import scala.scalanative.unsafe._ +import scala.scalanative.posix +import posix.time +import posix.timeOps._ private[weaver] object Timestamp { - def format(l: Long): String = { - val date = new Date(0) - - date.setMilliseconds(l.toDouble) - - val hour = date.getHours() - val minutes = date.getMinutes() - val seconds = date.getSeconds() - + def format(epochSecond: Long): String = Zone { implicit zone => + val out = alloc[time.tm]() + val timePtr = alloc[time.time_t]() + !timePtr = epochSecond + val gmTime: Ptr[time.tm] = time.localtime_r(timePtr, out) + val hour = gmTime.tm_hour + val minutes = gmTime.tm_min + val seconds = gmTime.tm_sec s"$hour:$minutes:$seconds" } - def now(): Long = Date.now().toLong - - def localTime(hours: Int, minutes: Int, seconds: Int): Long = { - val date = new Date(Date.now()) - - date.setHours(hours.toDouble, - min = minutes.toDouble, - sec = seconds.toDouble) - - date.getTime().toLong + def localTime(hours: Int, minutes: Int, seconds: Int): Long = Zone { + implicit zone => + val out = alloc[time.tm]() + val timePtr = alloc[time.time_t]() + !timePtr = time.time(null) + val gmTime: Ptr[time.tm] = time.gmtime_r(timePtr, out) + + gmTime.tm_hour = hours + gmTime.tm_min = minutes + gmTime.tm_sec = seconds + gmTime.tm_isdst = -1; // Is DST on? 1 = yes, 0 = no, -1 = unknown + time.mktime(gmTime).longValue() } - } diff --git a/modules/framework/cats/src/weaver/framework/CatsFramework.scala b/modules/framework/cats/src/weaver/framework/CatsFramework.scala index 7dd9b718..403c8020 100644 --- a/modules/framework/cats/src/weaver/framework/CatsFramework.scala +++ b/modules/framework/cats/src/weaver/framework/CatsFramework.scala @@ -10,7 +10,9 @@ class CatsEffect(errorStream: PrintStream) CatsFingerprints, CatsUnsafeRun, errorStream) { - def this() = this(System.err) + def this() = { + this(System.err) + } } object CatsFingerprints diff --git a/modules/framework/src-native/DogFoodCompat.scala b/modules/framework/src-native/DogFoodCompat.scala new file mode 100644 index 00000000..f9bbf85b --- /dev/null +++ b/modules/framework/src-native/DogFoodCompat.scala @@ -0,0 +1,45 @@ +package weaver +package framework + +import cats.data.Chain +import cats.effect.Resource +import cats.syntax.all._ + +private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => + + import self.framework.unsafeRun._ + + def blocker: BlockerCompat[F] + + def runTasksCompat( + runner: WeaverRunner[F], + eventHandler: sbt.testing.EventHandler, + logger: sbt.testing.Logger, + maxParallelism: Int)(tasks: List[sbt.testing.Task]): F[Unit] = { + tasks.traverse { task => + self.framework.unsafeRun.async { + (cb: (Either[Throwable, Unit] => Unit)) => + task.execute(eventHandler, Array(logger), _ => cb(Right(()))) + } + }.map { _ => + Reporter.logRunFinished(Array(logger))( + Chain(runner.failedTests.toSeq: _*)) + } + } + + def done(runner: sbt.testing.Runner): F[String] = effect.delay(runner.done()) + +} + +private[weaver] trait DogFoodCompanion { + def make[F[_]](framework: WeaverFramework[F]): Resource[F, DogFood[F]] = { + import framework.unsafeRun.effect + Resource.eval(effect.delay(new DogFood(framework) { + def blocker = new BlockerCompat[F] { + // can't block on javascript obviously + def block[A](thunk: => A): F[A] = effect.delay(thunk) + } + })) + } + +} diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-native/RunnerCompat.scala new file mode 100644 index 00000000..2f058fd2 --- /dev/null +++ b/modules/framework/src-native/RunnerCompat.scala @@ -0,0 +1,215 @@ +package weaver +package framework + +import scala.collection.mutable.ListBuffer +import scala.concurrent.duration._ +import scala.scalajs.js +import scala.scalajs.js.JSON + +import cats.data.Chain +import cats.effect.kernel.Async +import cats.effect.{ Ref, Sync } +import cats.syntax.all._ + +import sbt.testing.{ EventHandler, Logger, Task, TaskDef } + +trait RunnerCompat[F[_]] { self: sbt.testing.Runner => + protected val args: Array[String] + protected val suiteLoader: SuiteLoader[F] + protected val unsafeRun: UnsafeRun[F] + protected val channel: Option[String => Unit] + + import unsafeRun._ + + private[weaver] val failedTests = ListBuffer.empty[(SuiteName, TestOutcome)] + + def reportDone(out: TestOutcomeJS): Unit = { + val serialised = JSON.stringify(out, null) + channel match { + case Some(send) => send(serialised) + case None => failedTests.append(TestOutcomeJS.rehydrate(out)) + } + } + + def reportDoneF(out: TestOutcomeJS): F[Unit] = Sync[F].delay(reportDone(out)) + + override def deserializeTask( + task: String, + deserialize: String => sbt.testing.TaskDef): sbt.testing.Task = { + val taskDef = deserialize(task) + + val suiteRefs = suiteLoader(taskDef).collect { + case suite: suiteLoader.SuiteRef => suite + } + + SbtTask(taskDef, suiteRefs) + } + + override def serializeTask( + task: sbt.testing.Task, + serializer: sbt.testing.TaskDef => String): String = { + serializer(task.taskDef()) + } + + override def done(): String = { + val sb = new StringBuilder + + val s = { (str: String) => + val _ = sb.append(str + TaskCompat.lineSeparator) + } + + Reporter.runFinished(s, s)(Chain(failedTests.toSeq: _*)) + + sb.result() + } + + override def receiveMessage(msg: String): Option[String] = { + val outcome = JSON.parse(msg).asInstanceOf[TestOutcomeJS] + reportDone(outcome) + None + } + + override def tasks(taskDefs: Array[TaskDef]): Array[Task] = { + val tasksAndSuites = taskDefs.toList.map { taskDef => + taskDef -> suiteLoader(taskDef) + }.collect { + case (taskDef, Some(suite: suiteLoader.SuiteRef)) => (taskDef, suite) + } + + tasksAndSuites.map { case (td, ld) => SbtTask(td, Some(ld)) }.toArray + } + + private case class SbtTask(td: TaskDef, loader: Option[suiteLoader.SuiteRef]) + extends Task { + override def tags(): Array[String] = Array() + + override def execute( + eventHandler: EventHandler, + loggers: Array[Logger], + continuation: Array[Task] => Unit): Unit = () + + override def execute( + eventHandler: EventHandler, + loggers: Array[Logger]): Array[Task] = { + val fqn = taskDef().fullyQualifiedName() + + def reportTest(outcome: TestOutcome) = + effect.delay(eventHandler.handle(SbtEvent(td, outcome))) + + def runSuite( + fqn: String, + suite: EffectSuite[F], + outcomes: Ref[F, Chain[TestOutcome]]): F[Unit] = for { + _ <- effect.delay(Reporter.logSuiteStarted(loggers)(SuiteName(fqn))) + _ <- suite.run(args.toList) { outcome => + effect.delay(Reporter.logTestFinished(loggers)(outcome)) + .productR(reportTest(outcome)) + .productR(outcomes.update(_.append(outcome))) + } + } yield () + + def finaliseCompleted(outcomes: Ref[F, Chain[TestOutcome]]): F[Unit] = { + val failedF = outcomes.get.map( + _.filter(_.status.isFailed).map(SuiteName(fqn) -> _)) + + failedF.flatMap { + case c if c.isEmpty => effect.unit + case failed => { + val ots: Chain[TestOutcomeJS] = + failed.map { case (SuiteName(name), to) => + TestOutcomeJS.from(name)(to) + } + + ots.traverse(reportDoneF).void + } + } + } + + def finaliseError(outcomes: Ref[ + F, + Chain[TestOutcome]]): Throwable => F[Unit] = { error => + val outcome = + TestOutcome("Unexpected failure", + 0.seconds, + Result.from(error), + Chain.empty) + reportTest(outcome) + .productR(reportDoneF(TestOutcomeJS.from(fqn)(outcome))) + } + + val action = loader match { + case None => effect.unit + case Some(loader) => for { + outcomes <- Ref.of(Chain.empty[TestOutcome]) + _ <- + Async[F] + .guaranteeCase(loader.suite.flatMap(runSuite(fqn, + _, + outcomes)))( + _.fold( + canceled = effect.unit, + completed = _ *> finaliseCompleted(outcomes), + errored = finaliseError(outcomes) + ) + ) + } yield () + } + + unsafeRun.sync(action) + Array() + } + + override def taskDef(): TaskDef = td + + } + +} + +class TestOutcomeJS( + val suiteName: String, + val testName: String, + val durationMs: Double, + val verboseFormatting: String +) extends js.Object {} + +object TestOutcomeJS { + def from(suiteName: String)(outcome: TestOutcome): TestOutcomeJS = { + TestOutcomeJS( + suiteName, + outcome.name, + outcome.duration.toMillis.toDouble, + outcome.formatted(TestOutcome.Verbose)) + } + + def apply( + suiteName: String, + testName: String, + durationMs: Double, + verboseFormatting: String): TestOutcomeJS = + js.Dynamic.literal( + suiteName = suiteName, + testName = testName, + durationMs = durationMs, + verboseFormatting = verboseFormatting).asInstanceOf[TestOutcomeJS] + + def rehydrate(t: TestOutcomeJS): (SuiteName, TestOutcome) = { + SuiteName(t.suiteName) -> DecodedOutcome( + t.testName, + t.durationMs.millis, + t.verboseFormatting + ) + } + + private case class DecodedOutcome( + testName: String, + dur: FiniteDuration, + verboseFormatting: String) + extends TestOutcome { + def name: String = testName + def duration: FiniteDuration = dur + def status: TestStatus = TestStatus.Failure + def log: Chain[Log.Entry] = Chain.empty + def formatted(mode: TestOutcome.Mode): String = verboseFormatting + def cause: Option[Throwable] = None + } +} diff --git a/modules/framework/src-native/TaskCompat.scala b/modules/framework/src-native/TaskCompat.scala new file mode 100644 index 00000000..eb608202 --- /dev/null +++ b/modules/framework/src-native/TaskCompat.scala @@ -0,0 +1,6 @@ +package weaver +package framework + +private[weaver] object TaskCompat { + val lineSeparator = "\n" +} diff --git a/modules/framework/src/weaver/framework/DogFood.scala b/modules/framework/src/weaver/framework/DogFood.scala index 030e08c7..8e41083f 100644 --- a/modules/framework/src/weaver/framework/DogFood.scala +++ b/modules/framework/src/weaver/framework/DogFood.scala @@ -28,8 +28,8 @@ abstract class DogFood[F[_]]( // for some time before getting the logs back. On JVM platform // we do not need to wait, since the suite will run synchronously private val patience: Option[FiniteDuration] = PlatformCompat.platform match { - case JS => 2.seconds.some - case JVM => none + case JS | Native => 2.seconds.some + case JVM => none } def runSuites( diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index ef5419cb..91fd8d4c 100644 --- a/project/WeaverPlugin.scala +++ b/project/WeaverPlugin.scala @@ -8,6 +8,7 @@ import sbtprojectmatrix.ProjectMatrixKeys.virtualAxes import sbt.internal.ProjectMatrix import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport.scalaJSLinkerConfig +import scala.scalanative.sbtplugin.ScalaNativePlugin import org.scalajs.linker.interface.ModuleKind import org.scalajs.sbtplugin.ScalaJSPlugin import scala.collection.immutable.Nil @@ -48,11 +49,15 @@ object WeaverPlugin extends AutoPlugin { val scalaJSSettings: Configure = if (platform == VirtualAxis.js) configureScalaJSProject else identity + val scalaNativeSettings: Configure = + if (platform == VirtualAxis.native) configureScalaNativeProject + else identity + val ce3VersionOverride: Configure = _.settings(versionOverrideForCE3) val configureProject = - addScalafix andThen addScalafmt andThen scalaJSSettings andThen ce3VersionOverride + addScalafix andThen addScalafmt andThen scalaJSSettings andThen scalaNativeSettings andThen ce3VersionOverride projectMatrix.defaultAxes(defaults: _*).customRow( scalaVersions = List(scalaVersion), @@ -137,6 +142,13 @@ object WeaverPlugin extends AutoPlugin { ) } + def configureScalaNativeProject(proj: Project): Project = { + proj.enablePlugins(ScalaNativePlugin) + .settings( + Test / fork := false + ) + } + override def requires = plugins.JvmPlugin override def trigger = allRequirements diff --git a/project/plugins.sbt b/project/plugins.sbt index b819ba01..92fd1ebc 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,7 @@ // format: off addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.1") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.1.1") addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.1.1") From 0b385a6af4255cc9e15c1651b13cb827e164a0f8 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Mon, 12 Sep 2022 18:43:16 +0100 Subject: [PATCH 08/33] Add scala-native test interface --- build.sbt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 7e160114..45a82c1a 100644 --- a/build.sbt +++ b/build.sbt @@ -202,10 +202,15 @@ lazy val framework = projectMatrix "org.scala-sbt" % "test-interface" % Version.testInterface, "org.scala-js" %%% "scalajs-stubs" % Version.scalajsStubs % "provided" cross CrossVersion.for3Use2_13 ) - else + else if (virtualAxes.value.contains(VirtualAxis.js)) Seq( "org.scala-js" %% "scalajs-test-interface" % scalaJSVersion cross CrossVersion.for3Use2_13 ) + else if (virtualAxes.value.contains(VirtualAxis.native)) + Seq( + "org.scala-native" %%% "test-interface" % nativeVersion + ) + else Seq.empty } ++ Seq("junit" % "junit" % Version.junit) ) .settings(WeaverPlugin.simpleLayout) From f6538c8b54172b5bae2345842218a49ee09574ab Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Mon, 12 Sep 2022 20:15:24 +0100 Subject: [PATCH 09/33] Wonky TestOutcome protocol --- .../framework/src-native/DogFoodCompat.scala | 4 +- .../framework/src-native/RunnerCompat.scala | 97 ++++++++++++++----- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/modules/framework/src-native/DogFoodCompat.scala b/modules/framework/src-native/DogFoodCompat.scala index f9bbf85b..8acf535e 100644 --- a/modules/framework/src-native/DogFoodCompat.scala +++ b/modules/framework/src-native/DogFoodCompat.scala @@ -19,7 +19,9 @@ private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => tasks.traverse { task => self.framework.unsafeRun.async { (cb: (Either[Throwable, Unit] => Unit)) => - task.execute(eventHandler, Array(logger), _ => cb(Right(()))) + task.execute(eventHandler, Array(logger)) + scalanative.runtime.loop() + cb(Right(())) } }.map { _ => Reporter.logRunFinished(Array(logger))( diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-native/RunnerCompat.scala index 2f058fd2..5431fa9d 100644 --- a/modules/framework/src-native/RunnerCompat.scala +++ b/modules/framework/src-native/RunnerCompat.scala @@ -3,8 +3,6 @@ package framework import scala.collection.mutable.ListBuffer import scala.concurrent.duration._ -import scala.scalajs.js -import scala.scalajs.js.JSON import cats.data.Chain import cats.effect.kernel.Async @@ -12,6 +10,7 @@ import cats.effect.{ Ref, Sync } import cats.syntax.all._ import sbt.testing.{ EventHandler, Logger, Task, TaskDef } +import java.nio.ByteBuffer trait RunnerCompat[F[_]] { self: sbt.testing.Runner => protected val args: Array[String] @@ -23,15 +22,22 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => private[weaver] val failedTests = ListBuffer.empty[(SuiteName, TestOutcome)] - def reportDone(out: TestOutcomeJS): Unit = { - val serialised = JSON.stringify(out, null) + def reportDone(out: TestOutcomeNative): Unit = { + val serialised = Wonky.writer { p => + p.writeString(out.suiteName) + p.writeString(out.testName) + p.writeDouble(out.durationMs) + p.writeString(out.verboseFormatting) + () + } channel match { case Some(send) => send(serialised) - case None => failedTests.append(TestOutcomeJS.rehydrate(out)) + case None => failedTests.append(TestOutcomeNative.rehydrate(out)) } } - def reportDoneF(out: TestOutcomeJS): F[Unit] = Sync[F].delay(reportDone(out)) + def reportDoneF(out: TestOutcomeNative): F[Unit] = + Sync[F].delay(reportDone(out)) override def deserializeTask( task: String, @@ -64,7 +70,17 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } override def receiveMessage(msg: String): Option[String] = { - val outcome = JSON.parse(msg).asInstanceOf[TestOutcomeJS] + val outcome = Wonky.reader(msg) { p => + val suite = p.readString() + val test = p.readString() + val dur = p.readDouble() + val verb = p.readString() + + new TestOutcomeNative(suiteName = suite, + testName = test, + durationMs = dur, + verboseFormatting = verb) + } reportDone(outcome) None } @@ -83,11 +99,6 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => extends Task { override def tags(): Array[String] = Array() - override def execute( - eventHandler: EventHandler, - loggers: Array[Logger], - continuation: Array[Task] => Unit): Unit = () - override def execute( eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = { @@ -115,9 +126,9 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => failedF.flatMap { case c if c.isEmpty => effect.unit case failed => { - val ots: Chain[TestOutcomeJS] = + val ots: Chain[TestOutcomeNative] = failed.map { case (SuiteName(name), to) => - TestOutcomeJS.from(name)(to) + TestOutcomeNative.from(name)(to) } ots.traverse(reportDoneF).void @@ -134,7 +145,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => Result.from(error), Chain.empty) reportTest(outcome) - .productR(reportDoneF(TestOutcomeJS.from(fqn)(outcome))) + .productR(reportDoneF(TestOutcomeNative.from(fqn)(outcome))) } val action = loader match { @@ -165,16 +176,54 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } -class TestOutcomeJS( +object Wonky { + class Pointer(bytes: ByteBuffer, private var pt: Int) { + def readString() = { + val stringSize = bytes.getInt() + val ar = new Array[Byte](stringSize) + bytes.get(ar) + + new String(ar) + } + + def readDouble() = bytes.getDouble + } + + class Punter(bb: ByteBuffer) { + + def writeString(s: String) = { + bb.putInt(s.getBytes.size) + bb.put(s.getBytes()) + } + + def writeDouble(d: Double) = bb.putDouble(d) + } + + def reader[A](s: String)(f: Pointer => A) = { + val buf = ByteBuffer.wrap(s.getBytes) + f(new Pointer(buf, 0)) + } + + def writer(f: Punter => Unit): String = { + val buf = ByteBuffer.allocate(2048) + + f(new Punter(buf)) + + new String(buf.array()) + + } +} + +class TestOutcomeNative( val suiteName: String, val testName: String, val durationMs: Double, val verboseFormatting: String -) extends js.Object {} +) -object TestOutcomeJS { - def from(suiteName: String)(outcome: TestOutcome): TestOutcomeJS = { - TestOutcomeJS( +object TestOutcomeNative { + def from(suiteName: String)(outcome: TestOutcome): TestOutcomeNative = { + TestOutcomeNative( suiteName, outcome.name, outcome.duration.toMillis.toDouble, @@ -185,14 +234,14 @@ object TestOutcomeJS { suiteName: String, testName: String, durationMs: Double, - verboseFormatting: String): TestOutcomeJS = - js.Dynamic.literal( + verboseFormatting: String): TestOutcomeNative = + TestOutcomeNative( suiteName = suiteName, testName = testName, durationMs = durationMs, - verboseFormatting = verboseFormatting).asInstanceOf[TestOutcomeJS] + verboseFormatting = verboseFormatting) - def rehydrate(t: TestOutcomeJS): (SuiteName, TestOutcome) = { + def rehydrate(t: TestOutcomeNative): (SuiteName, TestOutcome) = { SuiteName(t.suiteName) -> DecodedOutcome( t.testName, t.durationMs.millis, From 282220b18563cc02414d793e3a309dd03b286cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 08:47:54 +0200 Subject: [PATCH 10/33] Stop failing tests from hanging --- .../framework/src-native/RunnerCompat.scala | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-native/RunnerCompat.scala index 5431fa9d..685bb927 100644 --- a/modules/framework/src-native/RunnerCompat.scala +++ b/modules/framework/src-native/RunnerCompat.scala @@ -99,6 +99,11 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => extends Task { override def tags(): Array[String] = Array() + def execute( + eventHandler: EventHandler, + loggers: Array[Logger], + continuation: Array[Task] => Unit): Unit = () + override def execute( eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = { @@ -214,33 +219,22 @@ object Wonky { } } -class TestOutcomeNative( - val suiteName: String, - val testName: String, - val durationMs: Double, - val verboseFormatting: String +case class TestOutcomeNative( + suiteName: String, + testName: String, + durationMs: Double, + verboseFormatting: String ) object TestOutcomeNative { def from(suiteName: String)(outcome: TestOutcome): TestOutcomeNative = { - TestOutcomeNative( + new TestOutcomeNative( suiteName, outcome.name, outcome.duration.toMillis.toDouble, outcome.formatted(TestOutcome.Verbose)) } - def apply( - suiteName: String, - testName: String, - durationMs: Double, - verboseFormatting: String): TestOutcomeNative = - TestOutcomeNative( - suiteName = suiteName, - testName = testName, - durationMs = durationMs, - verboseFormatting = verboseFormatting) - def rehydrate(t: TestOutcomeNative): (SuiteName, TestOutcome) = { SuiteName(t.suiteName) -> DecodedOutcome( t.testName, From 401f7778e9ac8c35e82ec0ef53395326a85a0b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 10:25:42 +0200 Subject: [PATCH 11/33] Cleanup --- .../framework/src-native/DogFoodCompat.scala | 9 +++-- .../framework/src-native/RunnerCompat.scala | 37 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/modules/framework/src-native/DogFoodCompat.scala b/modules/framework/src-native/DogFoodCompat.scala index 8acf535e..98d1087e 100644 --- a/modules/framework/src-native/DogFoodCompat.scala +++ b/modules/framework/src-native/DogFoodCompat.scala @@ -19,9 +19,12 @@ private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => tasks.traverse { task => self.framework.unsafeRun.async { (cb: (Either[Throwable, Unit] => Unit)) => - task.execute(eventHandler, Array(logger)) - scalanative.runtime.loop() - cb(Right(())) + try { + task.execute(eventHandler, Array(logger)) + cb(Right(())) + } catch { + case e: Throwable => cb(Left(e)) + } } }.map { _ => Reporter.logRunFinished(Array(logger))( diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-native/RunnerCompat.scala index 685bb927..85201c1f 100644 --- a/modules/framework/src-native/RunnerCompat.scala +++ b/modules/framework/src-native/RunnerCompat.scala @@ -23,7 +23,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => private[weaver] val failedTests = ListBuffer.empty[(SuiteName, TestOutcome)] def reportDone(out: TestOutcomeNative): Unit = { - val serialised = Wonky.writer { p => + val serialised = ReadWriter.writer { p => p.writeString(out.suiteName) p.writeString(out.testName) p.writeDouble(out.durationMs) @@ -70,7 +70,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } override def receiveMessage(msg: String): Option[String] = { - val outcome = Wonky.reader(msg) { p => + val outcome = ReadWriter.reader(msg) { p => val suite = p.readString() val test = p.readString() val dur = p.readDouble() @@ -157,17 +157,14 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => case None => effect.unit case Some(loader) => for { outcomes <- Ref.of(Chain.empty[TestOutcome]) - _ <- - Async[F] - .guaranteeCase(loader.suite.flatMap(runSuite(fqn, - _, - outcomes)))( - _.fold( - canceled = effect.unit, - completed = _ *> finaliseCompleted(outcomes), - errored = finaliseError(outcomes) - ) - ) + loadAndRun = loader.suite.flatMap(runSuite(fqn, _, outcomes)) + _ <- Async[F].guaranteeCase(loadAndRun)( + _.fold( + canceled = effect.unit, + completed = _ *> finaliseCompleted(outcomes), + errored = finaliseError(outcomes) + ) + ) } yield () } @@ -181,8 +178,8 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } -object Wonky { - class Pointer(bytes: ByteBuffer, private var pt: Int) { +private[weaver] object ReadWriter { + class Reader(bytes: ByteBuffer, private var pt: Int) { def readString() = { val stringSize = bytes.getInt() val ar = new Array[Byte](stringSize) @@ -194,7 +191,7 @@ object Wonky { def readDouble() = bytes.getDouble } - class Punter(bb: ByteBuffer) { + class Writer(bb: ByteBuffer) { def writeString(s: String) = { bb.putInt(s.getBytes.size) @@ -204,15 +201,15 @@ object Wonky { def writeDouble(d: Double) = bb.putDouble(d) } - def reader[A](s: String)(f: Pointer => A) = { + def reader[A](s: String)(f: Reader => A) = { val buf = ByteBuffer.wrap(s.getBytes) - f(new Pointer(buf, 0)) + f(new Reader(buf, 0)) } - def writer(f: Punter => Unit): String = { + def writer(f: Writer => Unit): String = { val buf = ByteBuffer.allocate(2048) - f(new Punter(buf)) + f(new Writer(buf)) new String(buf.array()) From d456a058628d2ba1445c1c315f24ec1fa394a760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 10:55:11 +0200 Subject: [PATCH 12/33] Most DogfoodTests run --- .../weaver/CatsUnsafeRunPlatformCompat.scala | 2 +- .../weaver/CatsUnsafeRunPlatformCompat.scala | 2 +- .../weaver/CatsUnsafeRunPlatformCompat.scala | 2 +- modules/core/cats/src/weaver/CatsUnsafeRun.scala | 6 ++++-- modules/core/src/weaver/UnsafeRun.scala | 6 ++++-- modules/core/src/weaver/suites.scala | 3 +-- .../zio/src/weaver/ziocompat/ZIOUnsafeRun.scala | 4 ++-- modules/framework/src-js/RunnerCompat.scala | 2 +- modules/framework/src-native/DogFoodCompat.scala | 10 ++-------- modules/framework/src-native/RunnerCompat.scala | 16 +++++++++++++--- modules/framework/src-native/SNTask.scala | 11 +++++++++++ 11 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 modules/framework/src-native/SNTask.scala diff --git a/modules/core/cats/src-js/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-js/weaver/CatsUnsafeRunPlatformCompat.scala index 21b31954..4275e7de 100644 --- a/modules/core/cats/src-js/weaver/CatsUnsafeRunPlatformCompat.scala +++ b/modules/core/cats/src-js/weaver/CatsUnsafeRunPlatformCompat.scala @@ -5,7 +5,7 @@ import cats.effect.IO private[weaver] trait CatsUnsafeRunPlatformCompat { self: CatsUnsafeRun => - def sync(task: IO[Unit]): Unit = ??? + def unsafeRunSync(task: IO[Unit]): Unit = ??? def background(task: IO[Unit]): CancelToken = ??? diff --git a/modules/core/cats/src-jvm/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-jvm/weaver/CatsUnsafeRunPlatformCompat.scala index 8937ab72..2c3ad99d 100644 --- a/modules/core/cats/src-jvm/weaver/CatsUnsafeRunPlatformCompat.scala +++ b/modules/core/cats/src-jvm/weaver/CatsUnsafeRunPlatformCompat.scala @@ -5,7 +5,7 @@ import cats.effect.unsafe.implicits.global private[weaver] trait CatsUnsafeRunPlatformCompat { self: CatsUnsafeRun => - def sync(task: IO[Unit]): Unit = task.unsafeRunSync() + def unsafeRunSync(task: IO[Unit]): Unit = task.unsafeRunSync() def background(task: IO[Unit]): CancelToken = task.start.unsafeRunSync() diff --git a/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala index 01a93568..a543425a 100644 --- a/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala +++ b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala @@ -8,7 +8,7 @@ import cats.effect.unsafe.implicits.global private[weaver] trait CatsUnsafeRunPlatformCompat { self: CatsUnsafeRun => - def sync(task: IO[Unit]): Unit = { + def unsafeRunSync(task: IO[Unit]): Unit = { val future = task.unsafeToFuture() scalanative.runtime.loop() Await.result(future, 1.minute) diff --git a/modules/core/cats/src/weaver/CatsUnsafeRun.scala b/modules/core/cats/src/weaver/CatsUnsafeRun.scala index cb7230c4..4935d0a8 100644 --- a/modules/core/cats/src/weaver/CatsUnsafeRun.scala +++ b/modules/core/cats/src/weaver/CatsUnsafeRun.scala @@ -2,6 +2,7 @@ package weaver import cats.effect.unsafe.implicits.global import cats.effect.{ FiberIO, IO } +import scala.concurrent.Future object CatsUnsafeRun extends CatsUnsafeRun @@ -12,8 +13,9 @@ trait CatsUnsafeRun extends UnsafeRun[IO] with CatsUnsafeRunPlatformCompat { override implicit val parallel = IO.parallelForIO override implicit val effect = IO.asyncForIO - def cancel(token: CancelToken): Unit = sync(token.cancel) + def cancel(token: CancelToken): Unit = unsafeRunSync(token.cancel) - def async(task: IO[Unit]): Unit = task.unsafeRunAndForget() + def unsafeRunAndForget(task: IO[Unit]): Unit = task.unsafeRunAndForget() + def unsafeRunToFuture(task: IO[Unit]): Future[Unit] = task.unsafeToFuture() } diff --git a/modules/core/src/weaver/UnsafeRun.scala b/modules/core/src/weaver/UnsafeRun.scala index 4b119bee..baa337e4 100644 --- a/modules/core/src/weaver/UnsafeRun.scala +++ b/modules/core/src/weaver/UnsafeRun.scala @@ -5,6 +5,7 @@ import scala.concurrent.duration.FiniteDuration import cats.Parallel import cats.effect.{ Async, Resource } import cats.syntax.all._ +import scala.concurrent.Future trait EffectCompat[F[_]] { implicit def parallel: Parallel[F] @@ -37,7 +38,8 @@ trait UnsafeRun[F[_]] extends EffectCompat[F] { def background(task: F[Unit]): CancelToken def cancel(token: CancelToken): Unit - def sync(task: F[Unit]): Unit - def async(task: F[Unit]): Unit + def unsafeRunSync(task: F[Unit]): Unit + def unsafeRunAndForget(task: F[Unit]): Unit + def unsafeRunToFuture(task: F[Unit]): Future[Unit] } diff --git a/modules/core/src/weaver/suites.scala b/modules/core/src/weaver/suites.scala index 8889857d..8532e952 100644 --- a/modules/core/src/weaver/suites.scala +++ b/modules/core/src/weaver/suites.scala @@ -63,7 +63,7 @@ abstract class RunnableSuite[F[_]] extends EffectSuite[F] { private[weaver] def getEffectCompat: UnsafeRun[EffectType] = effectCompat def plan : List[TestName] private[weaver] def runUnsafe(args: List[String])(report: TestOutcome => Unit) : Unit = - effectCompat.sync(run(args)(outcome => effectCompat.effect.delay(report(outcome)))) + effectCompat.unsafeRunSync(run(args)(outcome => effectCompat.effect.delay(report(outcome)))) } abstract class MutableFSuite[F[_]] extends RunnableSuite[F] { @@ -162,4 +162,3 @@ abstract class FunSuiteF[F[_]] extends RunnableSuite[F] with FunSuiteAux { self private[weaver] object initError extends AssertionError( "Cannot define new tests after TestSuite was initialized" ) - diff --git a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala index 43ef0230..f542e172 100644 --- a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala +++ b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala @@ -23,7 +23,7 @@ object ZIOUnsafeRun extends UnsafeRun[T] { def cancel(token: Fiber.Id => Exit[Throwable, Unit]): Unit = discard[Exit[Throwable, Unit]](token(Fiber.Id.None)) - def sync(task: T[Unit]): Unit = runtime.unsafeRun(task) + def unsafeRunSync(task: T[Unit]): Unit = runtime.unsafeRun(task) - def async(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) + def runAsyncAndForget(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) } diff --git a/modules/framework/src-js/RunnerCompat.scala b/modules/framework/src-js/RunnerCompat.scala index 7e43dddc..e66544cf 100644 --- a/modules/framework/src-js/RunnerCompat.scala +++ b/modules/framework/src-js/RunnerCompat.scala @@ -156,7 +156,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } yield () } - unsafeRun.async(action.attempt.map { exc => + unsafeRun.unsafeRunAndForget(action.attempt.map { exc => continuation(Array()) }) } diff --git a/modules/framework/src-native/DogFoodCompat.scala b/modules/framework/src-native/DogFoodCompat.scala index 98d1087e..85ca545b 100644 --- a/modules/framework/src-native/DogFoodCompat.scala +++ b/modules/framework/src-native/DogFoodCompat.scala @@ -17,14 +17,8 @@ private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => logger: sbt.testing.Logger, maxParallelism: Int)(tasks: List[sbt.testing.Task]): F[Unit] = { tasks.traverse { task => - self.framework.unsafeRun.async { - (cb: (Either[Throwable, Unit] => Unit)) => - try { - task.execute(eventHandler, Array(logger)) - cb(Right(())) - } catch { - case e: Throwable => cb(Left(e)) - } + self.framework.unsafeRun.fromFuture { + task.asInstanceOf[SNTask].executeFuture(eventHandler, Array(logger)) } }.map { _ => Reporter.logRunFinished(Array(logger))( diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-native/RunnerCompat.scala index 85201c1f..e3052a9f 100644 --- a/modules/framework/src-native/RunnerCompat.scala +++ b/modules/framework/src-native/RunnerCompat.scala @@ -11,6 +11,8 @@ import cats.syntax.all._ import sbt.testing.{ EventHandler, Logger, Task, TaskDef } import java.nio.ByteBuffer +import scala.concurrent.Future +import scala.concurrent.Await trait RunnerCompat[F[_]] { self: sbt.testing.Runner => protected val args: Array[String] @@ -96,7 +98,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } private case class SbtTask(td: TaskDef, loader: Option[suiteLoader.SuiteRef]) - extends Task { + extends SNTask { override def tags(): Array[String] = Array() def execute( @@ -107,6 +109,15 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => override def execute( eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = { + val future = executeFuture(eventHandler, loggers) + scalanative.runtime.loop() + Await.result(future, 5.minutes) + Array.empty[Task] + } + + def executeFuture( + eventHandler: EventHandler, + loggers: Array[Logger]): Future[Unit] = { val fqn = taskDef().fullyQualifiedName() def reportTest(outcome: TestOutcome) = @@ -168,8 +179,7 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } yield () } - unsafeRun.sync(action) - Array() + unsafeRun.unsafeRunToFuture(action) } override def taskDef(): TaskDef = td diff --git a/modules/framework/src-native/SNTask.scala b/modules/framework/src-native/SNTask.scala new file mode 100644 index 00000000..8bfe3a82 --- /dev/null +++ b/modules/framework/src-native/SNTask.scala @@ -0,0 +1,11 @@ +package weaver + +import sbt.testing.Task +import sbt.testing.{ EventHandler, Logger } +import scala.concurrent.Future + +private[weaver] trait SNTask extends Task { + def executeFuture( + eventHandler: EventHandler, + loggers: Array[Logger]): Future[Unit] +} From 5069b78bd61471c3ce4d292b5cc6c7ed1bf38d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 11:18:26 +0200 Subject: [PATCH 13/33] All DogFoodTests are green --- .../framework/src-native/RunnerCompat.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-native/RunnerCompat.scala index e3052a9f..058c4535 100644 --- a/modules/framework/src-native/RunnerCompat.scala +++ b/modules/framework/src-native/RunnerCompat.scala @@ -160,8 +160,8 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => 0.seconds, Result.from(error), Chain.empty) - reportTest(outcome) - .productR(reportDoneF(TestOutcomeNative.from(fqn)(outcome))) + reportTest(outcome).productR( + reportDoneF(TestOutcomeNative.from(fqn)(outcome))) } val action = loader match { @@ -169,13 +169,15 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => case Some(loader) => for { outcomes <- Ref.of(Chain.empty[TestOutcome]) loadAndRun = loader.suite.flatMap(runSuite(fqn, _, outcomes)) - _ <- Async[F].guaranteeCase(loadAndRun)( - _.fold( - canceled = effect.unit, - completed = _ *> finaliseCompleted(outcomes), - errored = finaliseError(outcomes) - ) - ) + _ <- Async[F].background(loadAndRun).use { + _.flatMap { + _.fold( + canceled = effect.unit, + completed = _ *> finaliseCompleted(outcomes), + errored = finaliseError(outcomes) + ) + } + } } yield () } From fdc2281545e8c988bf2e872e9478a33e9aa0d68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 12:02:03 +0200 Subject: [PATCH 14/33] Unify JS/Native implementations --- .../src/weaver/ziocompat/ZIOUnsafeRun.scala | 3 +- .../AsyncTask.scala} | 2 +- .../DogFoodCompat.scala | 2 +- .../RunnerCompat.scala | 17 +- .../TaskCompat.scala | 0 modules/framework/src-js/DogFoodCompat.scala | 45 ---- modules/framework/src-js/PlatformTask.scala | 19 ++ modules/framework/src-js/RunnerCompat.scala | 217 ------------------ .../framework/src-native/PlatformTask.scala | 17 ++ modules/framework/src-native/TaskCompat.scala | 6 - project/WeaverPlugin.scala | 9 +- 11 files changed, 47 insertions(+), 290 deletions(-) rename modules/framework/{src-native/SNTask.scala => src-js-native/AsyncTask.scala} (82%) rename modules/framework/{src-native => src-js-native}/DogFoodCompat.scala (93%) rename modules/framework/{src-native => src-js-native}/RunnerCompat.scala (93%) rename modules/framework/{src-js => src-js-native}/TaskCompat.scala (100%) delete mode 100644 modules/framework/src-js/DogFoodCompat.scala create mode 100644 modules/framework/src-js/PlatformTask.scala delete mode 100644 modules/framework/src-js/RunnerCompat.scala create mode 100644 modules/framework/src-native/PlatformTask.scala delete mode 100644 modules/framework/src-native/TaskCompat.scala diff --git a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala index f542e172..79fff6ff 100644 --- a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala +++ b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala @@ -25,5 +25,6 @@ object ZIOUnsafeRun extends UnsafeRun[T] { def unsafeRunSync(task: T[Unit]): Unit = runtime.unsafeRun(task) - def runAsyncAndForget(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) + def runAsyncAndForget(task: T[Unit]): Unit = + runtime.unsafeRunAsync(task)(_ => ()) } diff --git a/modules/framework/src-native/SNTask.scala b/modules/framework/src-js-native/AsyncTask.scala similarity index 82% rename from modules/framework/src-native/SNTask.scala rename to modules/framework/src-js-native/AsyncTask.scala index 8bfe3a82..42273936 100644 --- a/modules/framework/src-native/SNTask.scala +++ b/modules/framework/src-js-native/AsyncTask.scala @@ -4,7 +4,7 @@ import sbt.testing.Task import sbt.testing.{ EventHandler, Logger } import scala.concurrent.Future -private[weaver] trait SNTask extends Task { +private[weaver] trait AsyncTask extends Task { def executeFuture( eventHandler: EventHandler, loggers: Array[Logger]): Future[Unit] diff --git a/modules/framework/src-native/DogFoodCompat.scala b/modules/framework/src-js-native/DogFoodCompat.scala similarity index 93% rename from modules/framework/src-native/DogFoodCompat.scala rename to modules/framework/src-js-native/DogFoodCompat.scala index 85ca545b..dc6b7d97 100644 --- a/modules/framework/src-native/DogFoodCompat.scala +++ b/modules/framework/src-js-native/DogFoodCompat.scala @@ -18,7 +18,7 @@ private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => maxParallelism: Int)(tasks: List[sbt.testing.Task]): F[Unit] = { tasks.traverse { task => self.framework.unsafeRun.fromFuture { - task.asInstanceOf[SNTask].executeFuture(eventHandler, Array(logger)) + task.asInstanceOf[AsyncTask].executeFuture(eventHandler, Array(logger)) } }.map { _ => Reporter.logRunFinished(Array(logger))( diff --git a/modules/framework/src-native/RunnerCompat.scala b/modules/framework/src-js-native/RunnerCompat.scala similarity index 93% rename from modules/framework/src-native/RunnerCompat.scala rename to modules/framework/src-js-native/RunnerCompat.scala index 058c4535..eb9dc4a5 100644 --- a/modules/framework/src-native/RunnerCompat.scala +++ b/modules/framework/src-js-native/RunnerCompat.scala @@ -12,7 +12,6 @@ import cats.syntax.all._ import sbt.testing.{ EventHandler, Logger, Task, TaskDef } import java.nio.ByteBuffer import scala.concurrent.Future -import scala.concurrent.Await trait RunnerCompat[F[_]] { self: sbt.testing.Runner => protected val args: Array[String] @@ -98,23 +97,9 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } private case class SbtTask(td: TaskDef, loader: Option[suiteLoader.SuiteRef]) - extends SNTask { + extends PlatformTask { override def tags(): Array[String] = Array() - def execute( - eventHandler: EventHandler, - loggers: Array[Logger], - continuation: Array[Task] => Unit): Unit = () - - override def execute( - eventHandler: EventHandler, - loggers: Array[Logger]): Array[Task] = { - val future = executeFuture(eventHandler, loggers) - scalanative.runtime.loop() - Await.result(future, 5.minutes) - Array.empty[Task] - } - def executeFuture( eventHandler: EventHandler, loggers: Array[Logger]): Future[Unit] = { diff --git a/modules/framework/src-js/TaskCompat.scala b/modules/framework/src-js-native/TaskCompat.scala similarity index 100% rename from modules/framework/src-js/TaskCompat.scala rename to modules/framework/src-js-native/TaskCompat.scala diff --git a/modules/framework/src-js/DogFoodCompat.scala b/modules/framework/src-js/DogFoodCompat.scala deleted file mode 100644 index f9bbf85b..00000000 --- a/modules/framework/src-js/DogFoodCompat.scala +++ /dev/null @@ -1,45 +0,0 @@ -package weaver -package framework - -import cats.data.Chain -import cats.effect.Resource -import cats.syntax.all._ - -private[weaver] trait DogFoodCompat[F[_]] { self: DogFood[F] => - - import self.framework.unsafeRun._ - - def blocker: BlockerCompat[F] - - def runTasksCompat( - runner: WeaverRunner[F], - eventHandler: sbt.testing.EventHandler, - logger: sbt.testing.Logger, - maxParallelism: Int)(tasks: List[sbt.testing.Task]): F[Unit] = { - tasks.traverse { task => - self.framework.unsafeRun.async { - (cb: (Either[Throwable, Unit] => Unit)) => - task.execute(eventHandler, Array(logger), _ => cb(Right(()))) - } - }.map { _ => - Reporter.logRunFinished(Array(logger))( - Chain(runner.failedTests.toSeq: _*)) - } - } - - def done(runner: sbt.testing.Runner): F[String] = effect.delay(runner.done()) - -} - -private[weaver] trait DogFoodCompanion { - def make[F[_]](framework: WeaverFramework[F]): Resource[F, DogFood[F]] = { - import framework.unsafeRun.effect - Resource.eval(effect.delay(new DogFood(framework) { - def blocker = new BlockerCompat[F] { - // can't block on javascript obviously - def block[A](thunk: => A): F[A] = effect.delay(thunk) - } - })) - } - -} diff --git a/modules/framework/src-js/PlatformTask.scala b/modules/framework/src-js/PlatformTask.scala new file mode 100644 index 00000000..2f3f81b0 --- /dev/null +++ b/modules/framework/src-js/PlatformTask.scala @@ -0,0 +1,19 @@ +package weaver + +import sbt.testing.{ EventHandler, Logger, Task } + +private[weaver] trait PlatformTask extends AsyncTask { + + override def execute( + eventHandler: EventHandler, + loggers: Array[Logger], + continuation: Array[Task] => Unit): Unit = { + val _ = executeFuture(eventHandler, loggers).map(_ => + continuation(Array.empty[Task]))( + scala.scalajs.concurrent.JSExecutionContext.queue) + } + + override def execute( + eventHandler: EventHandler, + loggers: Array[Logger]): Array[Task] = Array.empty[Task] +} diff --git a/modules/framework/src-js/RunnerCompat.scala b/modules/framework/src-js/RunnerCompat.scala deleted file mode 100644 index e66544cf..00000000 --- a/modules/framework/src-js/RunnerCompat.scala +++ /dev/null @@ -1,217 +0,0 @@ -package weaver -package framework - -import scala.collection.mutable.ListBuffer -import scala.concurrent.duration._ -import scala.scalajs.js -import scala.scalajs.js.JSON - -import cats.data.Chain -import cats.effect.kernel.Async -import cats.effect.{ Ref, Sync } -import cats.syntax.all._ - -import sbt.testing.{ EventHandler, Logger, Task, TaskDef } - -trait RunnerCompat[F[_]] { self: sbt.testing.Runner => - protected val args: Array[String] - protected val suiteLoader: SuiteLoader[F] - protected val unsafeRun: UnsafeRun[F] - protected val channel: Option[String => Unit] - - import unsafeRun._ - - private[weaver] val failedTests = ListBuffer.empty[(SuiteName, TestOutcome)] - - def reportDone(out: TestOutcomeJS): Unit = { - val serialised = JSON.stringify(out, null) - channel match { - case Some(send) => send(serialised) - case None => failedTests.append(TestOutcomeJS.rehydrate(out)) - } - } - - def reportDoneF(out: TestOutcomeJS): F[Unit] = Sync[F].delay(reportDone(out)) - - override def deserializeTask( - task: String, - deserialize: String => sbt.testing.TaskDef): sbt.testing.Task = { - val taskDef = deserialize(task) - - val suiteRefs = suiteLoader(taskDef).collect { - case suite: suiteLoader.SuiteRef => suite - } - - SbtTask(taskDef, suiteRefs) - } - - override def serializeTask( - task: sbt.testing.Task, - serializer: sbt.testing.TaskDef => String): String = { - serializer(task.taskDef()) - } - - override def done(): String = { - val sb = new StringBuilder - - val s = { (str: String) => - val _ = sb.append(str + TaskCompat.lineSeparator) - } - - Reporter.runFinished(s, s)(Chain(failedTests.toSeq: _*)) - - sb.result() - } - - override def receiveMessage(msg: String): Option[String] = { - val outcome = JSON.parse(msg).asInstanceOf[TestOutcomeJS] - reportDone(outcome) - None - } - - override def tasks(taskDefs: Array[TaskDef]): Array[Task] = { - val tasksAndSuites = taskDefs.toList.map { taskDef => - taskDef -> suiteLoader(taskDef) - }.collect { - case (taskDef, Some(suite: suiteLoader.SuiteRef)) => (taskDef, suite) - } - - tasksAndSuites.map { case (td, ld) => SbtTask(td, Some(ld)) }.toArray - } - - private case class SbtTask(td: TaskDef, loader: Option[suiteLoader.SuiteRef]) - extends Task { - override def tags(): Array[String] = Array() - - override def execute( - eventHandler: EventHandler, - loggers: Array[Logger]): Array[Task] = Array() - - override def execute( - eventHandler: EventHandler, - loggers: Array[Logger], - continuation: Array[Task] => Unit): Unit = { - - val fqn = taskDef().fullyQualifiedName() - - def reportTest(outcome: TestOutcome) = - effect.delay(eventHandler.handle(SbtEvent(td, outcome))) - - def runSuite( - fqn: String, - suite: EffectSuite[F], - outcomes: Ref[F, Chain[TestOutcome]]): F[Unit] = for { - _ <- effect.delay(Reporter.logSuiteStarted(loggers)(SuiteName(fqn))) - _ <- suite.run(args.toList) { outcome => - effect.delay(Reporter.logTestFinished(loggers)(outcome)) - .productR(reportTest(outcome)) - .productR(outcomes.update(_.append(outcome))) - } - } yield () - - def finaliseCompleted(outcomes: Ref[F, Chain[TestOutcome]]): F[Unit] = { - val failedF = outcomes.get.map( - _.filter(_.status.isFailed).map(SuiteName(fqn) -> _)) - - failedF.flatMap { - case c if c.isEmpty => effect.unit - case failed => { - val ots: Chain[TestOutcomeJS] = - failed.map { case (SuiteName(name), to) => - TestOutcomeJS.from(name)(to) - } - - ots.traverse(reportDoneF).void - } - } - } - - def finaliseError(outcomes: Ref[ - F, - Chain[TestOutcome]]): Throwable => F[Unit] = { error => - val outcome = - TestOutcome("Unexpected failure", - 0.seconds, - Result.from(error), - Chain.empty) - reportTest(outcome) - .productR(reportDoneF(TestOutcomeJS.from(fqn)(outcome))) - } - - val action = loader match { - case None => effect.unit - case Some(loader) => for { - outcomes <- Ref.of(Chain.empty[TestOutcome]) - _ <- - Async[F] - .guaranteeCase(loader.suite.flatMap(runSuite(fqn, - _, - outcomes)))( - _.fold( - canceled = effect.unit, - completed = _ *> finaliseCompleted(outcomes), - errored = finaliseError(outcomes) - ) - ) - } yield () - } - - unsafeRun.unsafeRunAndForget(action.attempt.map { exc => - continuation(Array()) - }) - } - - override def taskDef(): TaskDef = td - - } - -} - -class TestOutcomeJS( - val suiteName: String, - val testName: String, - val durationMs: Double, - val verboseFormatting: String -) extends js.Object {} - -object TestOutcomeJS { - def from(suiteName: String)(outcome: TestOutcome): TestOutcomeJS = { - TestOutcomeJS( - suiteName, - outcome.name, - outcome.duration.toMillis.toDouble, - outcome.formatted(TestOutcome.Verbose)) - } - - def apply( - suiteName: String, - testName: String, - durationMs: Double, - verboseFormatting: String): TestOutcomeJS = - js.Dynamic.literal( - suiteName = suiteName, - testName = testName, - durationMs = durationMs, - verboseFormatting = verboseFormatting).asInstanceOf[TestOutcomeJS] - - def rehydrate(t: TestOutcomeJS): (SuiteName, TestOutcome) = { - SuiteName(t.suiteName) -> DecodedOutcome( - t.testName, - t.durationMs.millis, - t.verboseFormatting - ) - } - - private case class DecodedOutcome( - testName: String, - dur: FiniteDuration, - verboseFormatting: String) - extends TestOutcome { - def name: String = testName - def duration: FiniteDuration = dur - def status: TestStatus = TestStatus.Failure - def log: Chain[Log.Entry] = Chain.empty - def formatted(mode: TestOutcome.Mode): String = verboseFormatting - def cause: Option[Throwable] = None - } -} diff --git a/modules/framework/src-native/PlatformTask.scala b/modules/framework/src-native/PlatformTask.scala new file mode 100644 index 00000000..f8e07e4b --- /dev/null +++ b/modules/framework/src-native/PlatformTask.scala @@ -0,0 +1,17 @@ +package weaver + +import sbt.testing.{ EventHandler, Logger, Task } +import scala.concurrent.Await +import scala.concurrent.duration._ + +private[weaver] trait PlatformTask extends AsyncTask { + + override def execute( + eventHandler: EventHandler, + loggers: Array[Logger]): Array[Task] = { + val future = executeFuture(eventHandler, loggers) + scalanative.runtime.loop() + Await.result(future, 5.minutes) + Array.empty[Task] + } +} diff --git a/modules/framework/src-native/TaskCompat.scala b/modules/framework/src-native/TaskCompat.scala deleted file mode 100644 index eb608202..00000000 --- a/modules/framework/src-native/TaskCompat.scala +++ /dev/null @@ -1,6 +0,0 @@ -package weaver -package framework - -private[weaver] object TaskCompat { - val lineSeparator = "\n" -} diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index 91fd8d4c..17701da7 100644 --- a/project/WeaverPlugin.scala +++ b/project/WeaverPlugin.scala @@ -312,9 +312,12 @@ object WeaverPlugin extends AutoPlugin { Def.setting((Compile / scalaSource).value.getParentFile().getParentFile().getParentFile()) def suffixes(axes: Seq[VirtualAxis]) = axes.collect { - case VirtualAxis.js => List("", "-js") - case VirtualAxis.jvm => List("", "-jvm") - case VirtualAxis.native => List("", "-native") + case VirtualAxis.js => + List("", "-js", "-jvm-js", "-js-native") + case VirtualAxis.jvm => + List("", "-jvm", "-jvm-js", "-jvm-native") + case VirtualAxis.native => + List("", "-native", "-jvm-native", "-js-native") case ScalaVersionAxis(ver, _) => if (ver.startsWith("3.")) List("", "-scala-3") else List("", "-scala-2") From a5518f91ce4f4fcefabfa2ecb06cc20c1f36c0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 23:43:23 +0200 Subject: [PATCH 15/33] Enable CI for native, add concurrency --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddd66696..fdc56922 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,11 +2,15 @@ name: CI on: pull_request: - branches: ["master"] + branches: ["master", "series/*"] push: branches: ["master"] tags: ["v*"] +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + jobs: build: name: Test ${{matrix.scalaVersion}} (${{matrix.scalaPlatform}}) @@ -16,7 +20,7 @@ jobs: os: [ubuntu-latest] java: [adopt@1.8] scalaVersion: ["2_12", "2_13", "3"] - scalaPlatform: ["jvm", "js"] + scalaPlatform: ["jvm", "js", "native"] runs-on: ${{ matrix.os }} env: BUILD_KEY: ${{matrix.scalaVersion}}_${{matrix.scalaPlatform}} From 9c3ddb10245ed6408165fc8152334edf2d995d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Tue, 13 Sep 2022 23:47:28 +0200 Subject: [PATCH 16/33] Fix ZIO --- modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala index 79fff6ff..0a307518 100644 --- a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala +++ b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala @@ -6,6 +6,7 @@ import cats.effect.Async import zio._ import zio.interop.catz +import scala.concurrent.Future object ZIOUnsafeRun extends UnsafeRun[T] { @@ -25,6 +26,9 @@ object ZIOUnsafeRun extends UnsafeRun[T] { def unsafeRunSync(task: T[Unit]): Unit = runtime.unsafeRun(task) - def runAsyncAndForget(task: T[Unit]): Unit = + def unsafeRunAndForget(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) + + def unsafeRunToFuture(task: T[Unit]): Future[Unit] = + runtime.unsafeRunToFuture(task) } From 4babf217b88ab794e513a969da0e223249c57ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Wed, 14 Sep 2022 04:58:09 +0200 Subject: [PATCH 17/33] Scalafix --- .../cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala | 3 ++- modules/core/cats/src/weaver/CatsUnsafeRun.scala | 3 ++- modules/core/src-native/weaver/internals/Timestamp.scala | 3 ++- modules/core/src/weaver/UnsafeRun.scala | 2 +- modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala | 3 ++- modules/framework/src-js-native/AsyncTask.scala | 4 ++-- modules/framework/src-js-native/RunnerCompat.scala | 5 +++-- modules/framework/src-native/PlatformTask.scala | 3 ++- 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala index a543425a..af8c30f1 100644 --- a/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala +++ b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala @@ -1,8 +1,9 @@ package weaver -import cats.effect.IO import scala.concurrent.Await import scala.concurrent.duration._ + +import cats.effect.IO import cats.effect.unsafe.implicits.global private[weaver] trait CatsUnsafeRunPlatformCompat { diff --git a/modules/core/cats/src/weaver/CatsUnsafeRun.scala b/modules/core/cats/src/weaver/CatsUnsafeRun.scala index 4935d0a8..72b67a3d 100644 --- a/modules/core/cats/src/weaver/CatsUnsafeRun.scala +++ b/modules/core/cats/src/weaver/CatsUnsafeRun.scala @@ -1,8 +1,9 @@ package weaver +import scala.concurrent.Future + import cats.effect.unsafe.implicits.global import cats.effect.{ FiberIO, IO } -import scala.concurrent.Future object CatsUnsafeRun extends CatsUnsafeRun diff --git a/modules/core/src-native/weaver/internals/Timestamp.scala b/modules/core/src-native/weaver/internals/Timestamp.scala index f7cc2f0b..c00c8744 100644 --- a/modules/core/src-native/weaver/internals/Timestamp.scala +++ b/modules/core/src-native/weaver/internals/Timestamp.scala @@ -1,7 +1,8 @@ package weaver.internals -import scala.scalanative.unsafe._ import scala.scalanative.posix +import scala.scalanative.unsafe._ + import posix.time import posix.timeOps._ diff --git a/modules/core/src/weaver/UnsafeRun.scala b/modules/core/src/weaver/UnsafeRun.scala index baa337e4..56767644 100644 --- a/modules/core/src/weaver/UnsafeRun.scala +++ b/modules/core/src/weaver/UnsafeRun.scala @@ -1,11 +1,11 @@ package weaver +import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration import cats.Parallel import cats.effect.{ Async, Resource } import cats.syntax.all._ -import scala.concurrent.Future trait EffectCompat[F[_]] { implicit def parallel: Parallel[F] diff --git a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala index 0a307518..0abc4866 100644 --- a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala +++ b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala @@ -1,12 +1,13 @@ package weaver package ziocompat +import scala.concurrent.Future + import cats.Parallel import cats.effect.Async import zio._ import zio.interop.catz -import scala.concurrent.Future object ZIOUnsafeRun extends UnsafeRun[T] { diff --git a/modules/framework/src-js-native/AsyncTask.scala b/modules/framework/src-js-native/AsyncTask.scala index 42273936..8acf30b2 100644 --- a/modules/framework/src-js-native/AsyncTask.scala +++ b/modules/framework/src-js-native/AsyncTask.scala @@ -1,9 +1,9 @@ package weaver -import sbt.testing.Task -import sbt.testing.{ EventHandler, Logger } import scala.concurrent.Future +import sbt.testing.{EventHandler, Logger, Task} + private[weaver] trait AsyncTask extends Task { def executeFuture( eventHandler: EventHandler, diff --git a/modules/framework/src-js-native/RunnerCompat.scala b/modules/framework/src-js-native/RunnerCompat.scala index eb9dc4a5..a37921d6 100644 --- a/modules/framework/src-js-native/RunnerCompat.scala +++ b/modules/framework/src-js-native/RunnerCompat.scala @@ -1,7 +1,10 @@ package weaver package framework +import java.nio.ByteBuffer + import scala.collection.mutable.ListBuffer +import scala.concurrent.Future import scala.concurrent.duration._ import cats.data.Chain @@ -10,8 +13,6 @@ import cats.effect.{ Ref, Sync } import cats.syntax.all._ import sbt.testing.{ EventHandler, Logger, Task, TaskDef } -import java.nio.ByteBuffer -import scala.concurrent.Future trait RunnerCompat[F[_]] { self: sbt.testing.Runner => protected val args: Array[String] diff --git a/modules/framework/src-native/PlatformTask.scala b/modules/framework/src-native/PlatformTask.scala index f8e07e4b..cddf4970 100644 --- a/modules/framework/src-native/PlatformTask.scala +++ b/modules/framework/src-native/PlatformTask.scala @@ -1,9 +1,10 @@ package weaver -import sbt.testing.{ EventHandler, Logger, Task } import scala.concurrent.Await import scala.concurrent.duration._ +import sbt.testing.{ EventHandler, Logger, Task } + private[weaver] trait PlatformTask extends AsyncTask { override def execute( From a87236b7a19d52dad5545b25aca075184d991675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Wed, 14 Sep 2022 05:06:03 +0200 Subject: [PATCH 18/33] Command synthesis --- project/WeaverPlugin.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index 08c8e684..33aa42c5 100644 --- a/project/WeaverPlugin.scala +++ b/project/WeaverPlugin.scala @@ -399,6 +399,7 @@ object WeaverPlugin extends AutoPlugin { val scala213Suffix = VirtualAxis.scalaABIVersion(scala213).idSuffix val scala212Suffix = VirtualAxis.scalaABIVersion(scala212).idSuffix val jsSuffix = VirtualAxis.js.idSuffix + val nativeSuffix = VirtualAxis.native.idSuffix val all: List[(Duplet, Seq[String])] = projects.collect { @@ -418,8 +419,10 @@ object WeaverPlugin extends AutoPlugin { val platformAxis = if (projectId.endsWith(jsSuffix)) { projectId = projectId.dropRight(jsSuffix.length) - "js" + } else if (projectId.endsWith(nativeSuffix)) { + projectId = projectId.dropRight(nativeSuffix.length) + "native" } else "jvm" Duplet(scalaAxis, platformAxis) -> lp.project From 94c81dcc3197d8f1d4cf475385c16b4ea543bb92 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 14 Sep 2022 09:23:32 +0100 Subject: [PATCH 19/33] Synchronise parallel tests --- .../framework/cats/test/src-jvm/MetaJVM.scala | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/modules/framework/cats/test/src-jvm/MetaJVM.scala b/modules/framework/cats/test/src-jvm/MetaJVM.scala index 615fbad1..78d8e217 100644 --- a/modules/framework/cats/test/src-jvm/MetaJVM.scala +++ b/modules/framework/cats/test/src-jvm/MetaJVM.scala @@ -7,6 +7,8 @@ import java.io.File import scala.concurrent.duration._ import cats.effect._ +import cats.effect.std.CyclicBarrier +import cats.syntax.all._ // The build tool will only detect and run top-level test suites. We can however nest objects // that contain failing tests, to allow for testing the framework without failing the build @@ -51,8 +53,11 @@ object MetaJVM { initialised: IO[Int], finalised: IO[Int], totalUses: Ref[IO, Int], - uses: Ref[IO, Int]) { - val getState: IO[(Int, Int, Int, Int)] = for { + uses: Ref[IO, Int], + latch: IO[Unit] + ) { + def getState(parallelWait: Boolean): IO[(Int, Int, Int, Int)] = for { + _ <- latch.whenA(parallelWait) i <- initialised f <- finalised t <- totalUses.updateAndGet(_ + 1) @@ -64,14 +69,24 @@ object MetaJVM { def sharedResources(global: weaver.GlobalWrite): Resource[IO, Unit] = Resource.eval { for { - initialised <- Ref[IO].of(0) - finalised <- Ref[IO].of(0) - totalUses <- Ref[IO].of(0) + initialised <- Ref[IO].of(0) + finalised <- Ref[IO].of(0) + totalUses <- Ref[IO].of(0) + parallelLatch <- CyclicBarrier[IO](3) resource = Resource.eval(Ref[IO].of(0)).flatMap { uses => - Resource.make(initialised.update(_ + 1))(_ => - finalised.update(_ + 1)).map(_ => - new LazyState(initialised.get, finalised.get, totalUses, uses)) + val resourceInitialisation = + Resource.make(initialised.update(_ + 1))(_ => + finalised.update(_ + 1)) + + val synchronisation = + parallelLatch.await + + resourceInitialisation.as(new LazyState(initialised.get, + finalised.get, + totalUses, + uses, + synchronisation)) } _ <- global.putLazy(resource) } yield () @@ -83,7 +98,7 @@ object MetaJVM { def sharedResource: Resource[IO, Res] = global.getOrFailR[LazyState]() test("Lazy resources should be instantiated only once") { state => - IO.sleep(100.millis) *> state.getState.map { + state.getState(parallelWait = true).map { case (initialised, finalised, totalUses, localUses) => expect.all( initialised == 1, // resource is initialised only once and uses in parallel @@ -105,7 +120,7 @@ object MetaJVM { global.getOrFailR[LazyState]()) test("Lazy resources should be instantiated several times") { state => - state.getState.map { + state.getState(parallelWait = false).map { case (initialised, finalised, totalUses, localUses) => expect.all( initialised == totalUses, // lazy resource will get initialised for each suite From 55efa0c3c4298d7e7625c9557b830cdc5d00d3dd Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 14 Sep 2022 09:30:42 +0100 Subject: [PATCH 20/33] Run CI on series/* --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddd66696..fabb3cf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: pull_request: - branches: ["master"] + branches: ["master", "series/*"] push: - branches: ["master"] + branches: ["master", "series/*"] tags: ["v*"] jobs: From d94e9f48fff8cc632fe29d763760401197fce2c5 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 14 Sep 2022 10:02:27 +0100 Subject: [PATCH 21/33] Enforce sequential access --- .../framework/cats/test/src-jvm/MetaJVM.scala | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/modules/framework/cats/test/src-jvm/MetaJVM.scala b/modules/framework/cats/test/src-jvm/MetaJVM.scala index 78d8e217..6a15ace2 100644 --- a/modules/framework/cats/test/src-jvm/MetaJVM.scala +++ b/modules/framework/cats/test/src-jvm/MetaJVM.scala @@ -4,10 +4,8 @@ package test import java.io.File -import scala.concurrent.duration._ - import cats.effect._ -import cats.effect.std.CyclicBarrier +import cats.effect.std.{ CyclicBarrier, Semaphore } import cats.syntax.all._ // The build tool will only detect and run top-level test suites. We can however nest objects @@ -65,13 +63,31 @@ object MetaJVM { } yield (i, f, t, u) } + case class SequentialAccess(permit: Resource[IO, Unit]) + object LazyGlobal extends GlobalResource { def sharedResources(global: weaver.GlobalWrite): Resource[IO, Unit] = Resource.eval { for { - initialised <- Ref[IO].of(0) - finalised <- Ref[IO].of(0) - totalUses <- Ref[IO].of(0) + initialised <- Ref[IO].of(0) + finalised <- Ref[IO].of(0) + totalUses <- Ref[IO].of(0) + /** + * NOTE: the number 3 refers to the current number of instantiated + * suites in the DogFoodTestsJVM spec, tests involving "global lazy + * resources" + * + * If you do either of the following: + * + * - Change the number of LazyAccessParallel suites in "global lazy + * resources (parallel)" test + * + * - Change the number below 3. Add another test to the + * LazyAccessParallel spec below + * + * You are very likely to face a very confusing non-deterministic + * behaviour. Those numbers need to be kept in sync. + */ parallelLatch <- CyclicBarrier[IO](3) resource = Resource.eval(Ref[IO].of(0)).flatMap { uses => @@ -88,7 +104,10 @@ object MetaJVM { uses, synchronisation)) } + sequential <- + Semaphore[IO](1).map(_.permit).map(SequentialAccess.apply) _ <- global.putLazy(resource) + _ <- global.put(sequential) } yield () } } @@ -115,9 +134,13 @@ object MetaJVM { abstract class LazyAccessSequential(global: GlobalRead, index: Int) extends IOSuite { type Res = LazyState - def sharedResource: Resource[IO, Res] = - Resource.eval(IO.sleep(index * 500.millis)).flatMap(_ => - global.getOrFailR[LazyState]()) + def sharedResource: Resource[IO, Res] = { + global.getOrFailR[SequentialAccess]().flatMap { semRes => + semRes.permit.flatMap { _ => + global.getOrFailR[LazyState]() + } + } + } test("Lazy resources should be instantiated several times") { state => state.getState(parallelWait = false).map { From a6296b042b8d6944d3edadaf034fb930a6ce3e94 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 14 Sep 2022 10:22:04 +0100 Subject: [PATCH 22/33] Update comment --- modules/framework/cats/test/src-jvm/MetaJVM.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/framework/cats/test/src-jvm/MetaJVM.scala b/modules/framework/cats/test/src-jvm/MetaJVM.scala index 6a15ace2..d21d8dcc 100644 --- a/modules/framework/cats/test/src-jvm/MetaJVM.scala +++ b/modules/framework/cats/test/src-jvm/MetaJVM.scala @@ -82,8 +82,9 @@ object MetaJVM { * - Change the number of LazyAccessParallel suites in "global lazy * resources (parallel)" test * - * - Change the number below 3. Add another test to the - * LazyAccessParallel spec below + * - Change the number below + * + * - Add another test to the LazyAccessParallel spec below * * You are very likely to face a very confusing non-deterministic * behaviour. Those numbers need to be kept in sync. From f30ccbb5115f802f6b95d5b2bbf1adc1db2df5b1 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 14 Sep 2022 22:21:26 +0100 Subject: [PATCH 23/33] Fix compilation? --- build.sbt | 2 +- modules/framework/src/weaver/framework/Fingerprints.scala | 6 +++--- modules/framework/src/weaver/framework/WeaverRunner.scala | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index 222ff105..4ef81f59 100644 --- a/build.sbt +++ b/build.sbt @@ -43,7 +43,7 @@ val Version = new { val zioInterop = "3.2.9.1" } - val expecty = "0.15.4" + val expecty = "0.16.0" val portableReflect = "1.1.2" val junit = "4.13.2" val scalajsStubs = "1.1.0" diff --git a/modules/framework/src/weaver/framework/Fingerprints.scala b/modules/framework/src/weaver/framework/Fingerprints.scala index c78024cd..ea8cdbdc 100644 --- a/modules/framework/src/weaver/framework/Fingerprints.scala +++ b/modules/framework/src/weaver/framework/Fingerprints.scala @@ -68,7 +68,7 @@ abstract class WeaverFingerprints[F[_]](implicit F: Sync[F]) { * [[weaver.EffectSuite]]. */ object SuiteFingerprint extends WeaverFingerprint { - val isModule = true + def isModule() = true def requireNoArgConstructor(): Boolean = true def superclassName(): String = SuiteClass.runtimeClass.getName } @@ -79,13 +79,13 @@ abstract class WeaverFingerprints[F[_]](implicit F: Sync[F]) { * [[weaver.GlobalResources.Read]] parameter. */ object ResourceSharingSuiteFingerprint extends WeaverFingerprint { - val isModule = false + def isModule() = false def requireNoArgConstructor(): Boolean = false def superclassName(): String = SuiteClass.runtimeClass.getName } object GlobalResourcesFingerprint extends WeaverFingerprint { - val isModule = true + def isModule() = true def requireNoArgConstructor(): Boolean = true def superclassName(): String = GlobalResourcesInitClass.runtimeClass.getName } diff --git a/modules/framework/src/weaver/framework/WeaverRunner.scala b/modules/framework/src/weaver/framework/WeaverRunner.scala index eecf7fdd..5c2d89ea 100644 --- a/modules/framework/src/weaver/framework/WeaverRunner.scala +++ b/modules/framework/src/weaver/framework/WeaverRunner.scala @@ -7,12 +7,14 @@ import sbt.testing._ class WeaverRunner[F[_]]( val args: Array[String], - val remoteArgs: Array[String], + val rmArgs: Array[String], val suiteLoader: SuiteLoader[F], val unsafeRun: UnsafeRun[F], val channel: Option[String => Unit], val errorStream: PrintStream ) extends Runner - with RunnerCompat[F] + with RunnerCompat[F] { + override def remoteArgs(): Array[String] = rmArgs +} final case class SuiteName(name: String) extends AnyVal From b61b4148b08fc69642a4d0cb53da50547a809dd8 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Wed, 14 Sep 2022 22:26:53 +0100 Subject: [PATCH 24/33] Formatting --- modules/framework/src-js-native/AsyncTask.scala | 2 +- modules/framework/src/weaver/framework/Fingerprints.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/framework/src-js-native/AsyncTask.scala b/modules/framework/src-js-native/AsyncTask.scala index 8acf30b2..0fe83915 100644 --- a/modules/framework/src-js-native/AsyncTask.scala +++ b/modules/framework/src-js-native/AsyncTask.scala @@ -2,7 +2,7 @@ package weaver import scala.concurrent.Future -import sbt.testing.{EventHandler, Logger, Task} +import sbt.testing.{ EventHandler, Logger, Task } private[weaver] trait AsyncTask extends Task { def executeFuture( diff --git a/modules/framework/src/weaver/framework/Fingerprints.scala b/modules/framework/src/weaver/framework/Fingerprints.scala index ea8cdbdc..29bdf3fd 100644 --- a/modules/framework/src/weaver/framework/Fingerprints.scala +++ b/modules/framework/src/weaver/framework/Fingerprints.scala @@ -68,7 +68,7 @@ abstract class WeaverFingerprints[F[_]](implicit F: Sync[F]) { * [[weaver.EffectSuite]]. */ object SuiteFingerprint extends WeaverFingerprint { - def isModule() = true + def isModule() = true def requireNoArgConstructor(): Boolean = true def superclassName(): String = SuiteClass.runtimeClass.getName } @@ -79,13 +79,13 @@ abstract class WeaverFingerprints[F[_]](implicit F: Sync[F]) { * [[weaver.GlobalResources.Read]] parameter. */ object ResourceSharingSuiteFingerprint extends WeaverFingerprint { - def isModule() = false + def isModule() = false def requireNoArgConstructor(): Boolean = false def superclassName(): String = SuiteClass.runtimeClass.getName } object GlobalResourcesFingerprint extends WeaverFingerprint { - def isModule() = true + def isModule() = true def requireNoArgConstructor(): Boolean = true def superclassName(): String = GlobalResourcesInitClass.runtimeClass.getName } From dbfde9104202e4812eb2763d6df14ac56aba2bc3 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Thu, 15 Sep 2022 09:56:08 +0100 Subject: [PATCH 25/33] Drop down to stable CE --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 4ef81f59..106e316f 100644 --- a/build.sbt +++ b/build.sbt @@ -39,7 +39,7 @@ sonatypeCredentialHost := "s01.oss.sonatype.org" val Version = new { object CE3 { val fs2 = "3.2.14-75-7902cbf" - val cats = "3.3.14-1-5d11fe9" + val cats = "3.3.14" val zioInterop = "3.2.9.1" } From aa7296e2be5a9ba2cd6ccad7cda27c5b76c76b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Thu, 15 Sep 2022 12:48:58 +0200 Subject: [PATCH 26/33] No patience on Native --- .../weaver/internals/Timestamp.scala | 27 +++++++++---------- .../src/weaver/framework/DogFood.scala | 6 ++--- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/modules/core/src-native/weaver/internals/Timestamp.scala b/modules/core/src-native/weaver/internals/Timestamp.scala index c00c8744..f80174b8 100644 --- a/modules/core/src-native/weaver/internals/Timestamp.scala +++ b/modules/core/src-native/weaver/internals/Timestamp.scala @@ -8,9 +8,9 @@ import posix.timeOps._ private[weaver] object Timestamp { - def format(epochSecond: Long): String = Zone { implicit zone => - val out = alloc[time.tm]() - val timePtr = alloc[time.time_t]() + def format(epochSecond: Long): String = { + val out = stackalloc[time.tm]() + val timePtr = stackalloc[time.time_t]() !timePtr = epochSecond val gmTime: Ptr[time.tm] = time.localtime_r(timePtr, out) val hour = gmTime.tm_hour @@ -19,17 +19,16 @@ private[weaver] object Timestamp { s"$hour:$minutes:$seconds" } - def localTime(hours: Int, minutes: Int, seconds: Int): Long = Zone { - implicit zone => - val out = alloc[time.tm]() - val timePtr = alloc[time.time_t]() - !timePtr = time.time(null) - val gmTime: Ptr[time.tm] = time.gmtime_r(timePtr, out) + def localTime(hours: Int, minutes: Int, seconds: Int): Long = { + val out = stackalloc[time.tm]() + val timePtr = stackalloc[time.time_t]() + !timePtr = time.time(null) + val gmTime: Ptr[time.tm] = time.gmtime_r(timePtr, out) - gmTime.tm_hour = hours - gmTime.tm_min = minutes - gmTime.tm_sec = seconds - gmTime.tm_isdst = -1; // Is DST on? 1 = yes, 0 = no, -1 = unknown - time.mktime(gmTime).longValue() + gmTime.tm_hour = hours + gmTime.tm_min = minutes + gmTime.tm_sec = seconds + gmTime.tm_isdst = -1; // Is DST on? 1 = yes, 0 = no, -1 = unknown + time.mktime(gmTime).longValue() } } diff --git a/modules/framework/src/weaver/framework/DogFood.scala b/modules/framework/src/weaver/framework/DogFood.scala index 8e41083f..67ae7d41 100644 --- a/modules/framework/src/weaver/framework/DogFood.scala +++ b/modules/framework/src/weaver/framework/DogFood.scala @@ -25,11 +25,11 @@ abstract class DogFood[F[_]]( import DogFood.State // ScalaJS executes asynchronously, therefore we need to wait - // for some time before getting the logs back. On JVM platform + // for some time before getting the logs back. On JVM/Native platform // we do not need to wait, since the suite will run synchronously private val patience: Option[FiniteDuration] = PlatformCompat.platform match { - case JS | Native => 2.seconds.some - case JVM => none + case JS => 2.seconds.some + case JVM | Native => none } def runSuites( From 23fddbb9bec9b93f36d15ee87e3bd5cd84e5a8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Thu, 15 Sep 2022 14:44:02 +0200 Subject: [PATCH 27/33] Dropping a bunch of OOTB modules for 0.8.0. In particular : * Monix * MonixBIO * ZIO * Specs2 --- README.md | 27 +- build.sbt | 55 +--- docs/global_resources.md | 7 +- docs/installation.md | 5 +- docs/intellij.md | 4 +- docs/other_effects.md | 15 + docs/specs2.md | 75 +---- docs/zio_usage.md | 117 ------- .../src/weaver/ziocompat/FiberRefLog.scala | 25 -- .../src/weaver/ziocompat/LTTResourceTag.scala | 15 - .../core/zio/src/weaver/ziocompat/Live.scala | 24 -- .../core/zio/src/weaver/ziocompat/Test.scala | 27 -- .../weaver/ziocompat/ZIOGlobalResource.scala | 36 --- .../src/weaver/ziocompat/ZIOUnsafeRun.scala | 29 -- .../core/zio/src/weaver/ziocompat/log.scala | 31 -- .../zio/src/weaver/ziocompat/package.scala | 46 --- .../zio/src/weaver/ziocompat/suites.scala | 112 ------- .../weaver/framework/MonixBIOFramework.scala | 23 -- .../weaver/monixbiocompat/FunSuiteTest.scala | 16 - .../weaver/monixbiocompat/IOSuiteTest.scala | 69 ----- .../src/weaver/framework/MonixFramework.scala | 19 -- .../src/weaver/monixcompat/FunSuiteTest.scala | 17 - .../weaver/monixcompat/TaskSuiteTest.scala | 74 ----- .../zio/src/weaver/framework/ZIO.scala | 16 - .../framework/zio/test/src-jvm/Global.scala | 33 -- .../src/weaver/ziocompat/CheckersTest.scala | 67 ---- .../src/weaver/ziocompat/FunSuiteTest.scala | 16 - .../ziocompat/MutableZIOSuiteTest.scala | 291 ------------------ website/sidebars.json | 5 +- 29 files changed, 29 insertions(+), 1267 deletions(-) create mode 100644 docs/other_effects.md delete mode 100644 docs/zio_usage.md delete mode 100644 modules/core/zio/src/weaver/ziocompat/FiberRefLog.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/LTTResourceTag.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/Live.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/Test.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/ZIOGlobalResource.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/log.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/package.scala delete mode 100644 modules/core/zio/src/weaver/ziocompat/suites.scala delete mode 100644 modules/framework/monix-bio/src/weaver/framework/MonixBIOFramework.scala delete mode 100644 modules/framework/monix-bio/test/src/weaver/monixbiocompat/FunSuiteTest.scala delete mode 100644 modules/framework/monix-bio/test/src/weaver/monixbiocompat/IOSuiteTest.scala delete mode 100644 modules/framework/monix/src/weaver/framework/MonixFramework.scala delete mode 100644 modules/framework/monix/test/src/weaver/monixcompat/FunSuiteTest.scala delete mode 100644 modules/framework/monix/test/src/weaver/monixcompat/TaskSuiteTest.scala delete mode 100644 modules/framework/zio/src/weaver/framework/ZIO.scala delete mode 100644 modules/framework/zio/test/src-jvm/Global.scala delete mode 100644 modules/framework/zio/test/src/weaver/ziocompat/CheckersTest.scala delete mode 100644 modules/framework/zio/test/src/weaver/ziocompat/FunSuiteTest.scala delete mode 100644 modules/framework/zio/test/src/weaver/ziocompat/MutableZIOSuiteTest.scala diff --git a/README.md b/README.md index ebbf3ff6..62c91bb6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ # Weaver-test A test-framework built on [cats-effect](https://github.com/typelevel/cats-effect) and -[fs2](https://github.com/functional-streams-for-scala/fs2), with [zio](https://zio.dev) and [monix](https://monix.io) interop. +[fs2](https://github.com/functional-streams-for-scala/fs2) ## Installation @@ -23,24 +23,8 @@ Refer yourself to the [releases](https://github.com/disneystreaming/weaver-test/ libraryDependencies += "com.disneystreaming" %% "weaver-cats" % "x.y.z" % Test testFrameworks += new TestFramework("weaver.framework.CatsEffect") -// optionally (for ZIO usage) -libraryDependencies += "com.disneystreaming" %% "weaver-zio" % "x.y.z" % Test -testFrameworks += new TestFramework("weaver.framework.ZIO") - -// optionally (for Monix usage) -libraryDependencies += "com.disneystreaming" %% "weaver-monix" % "x.y.z" % Test -testFrameworks += new TestFramework("weaver.framework.Monix") - -// optionally (for Monix BIO usage) -libraryDependencies += "com.disneystreaming" %% "weaver-monix-bio" % "x.y.z" % Test -testFrameworks += new TestFramework("weaver.framework.MonixBIO") - // optionally (for Scalacheck usage) libraryDependencies += "com.disneystreaming" %% "weaver-scalacheck" % "x.y.z" % Test - -// optionally (for specs2 interop) -libraryDependencies += "com.disneystreaming" %% "weaver-specs2" % "x.y.z" % Test - ``` ## Motivation @@ -132,20 +116,11 @@ object MySuite extends IOSuite { Weaver also includes support for -- `ZIO`-based suites via the optional `weaver-zio` dependency -- `Monix`-based suites via the optional `weaver-monix` dependency -- `Monix BIO`-based suites via the optional `weaver-monix-bio` dependency | Alias | Suite name | Provided by | Use case | | ----------------- | ------------------------ | ------------------ | --------------------------------------------- | | `SimpleIOSuite` | `SimpleMutableIOSuite` | `weaver-cats` | Each test is a standalone `IO` action | | `IOSuite` | `MutableIOSuite` | `weaver-cats` | Each test needs access to a shared `Resource` | -| `SimpleZIOSuite` | `SimpleMutableZIOSuite` | `weaver-zio` | Each test is a standalone `ZIO` action | -| `ZIOSuite[R]` | `MutableZIOSuite[R]` | `weaver-zio` | Each test needs access to a shared `ZLayer` | -| `SimpleTaskSuite` | `SimpleMutableTaskSuite` | `weaver-monix` | Each test is a standalone `Task` action | -| `TaskSuite` | `MutableTaskSuite` | `weaver-monix` | Each test needs access to a shared `Resource` | -| `SimpleIOSuite` | `SimpleMutableIOSuite` | `weaver-monix-bio` | Each test is a standalone `Task` action | -| `IOSuite` | `MutableIOSuite` | `weaver-monix-bio` | Each test needs access to a shared `Resource` | ### Expectations (assertions) diff --git a/build.sbt b/build.sbt index b270e9b9..afdacb08 100644 --- a/build.sbt +++ b/build.sbt @@ -38,16 +38,14 @@ sonatypeCredentialHost := "s01.oss.sonatype.org" val Version = new { object CE3 { - val fs2 = "3.2.12" - val cats = "3.3.14" - val zioInterop = "3.2.9.1" + val fs2 = "3.2.12" + val cats = "3.3.14" } val expecty = "0.15.4" val portableReflect = "1.1.2" val junit = "4.13.2" val scalajsStubs = "1.1.0" - val specs2 = "4.16.1" val discipline = "1.5.1" val catsLaws = "2.8.0" val scalacheck = "1.16.0" @@ -65,7 +63,6 @@ lazy val allModules = Seq( core.projectRefs, framework.projectRefs, scalacheck.projectRefs, - specs2.projectRefs, discipline.projectRefs, intellijRunner.projectRefs, effectCores, @@ -124,7 +121,7 @@ val allEffectCoresFilter: ScopeFilter = val allIntegrationsCoresFilter: ScopeFilter = ScopeFilter( inProjects( - (scalacheck.projectRefs ++ specs2.projectRefs ++ discipline.projectRefs): _*), + (scalacheck.projectRefs ++ discipline.projectRefs): _*), inConfigurations(Compile) ) @@ -132,7 +129,7 @@ lazy val docs = projectMatrix .in(file("modules/docs")) .jvmPlatform(Seq(WeaverPlugin.scala213)) .enablePlugins(DocusaurusPlugin, MdocPlugin) - .dependsOn(core, scalacheck, cats, zio, specs2, discipline) + .dependsOn(core, scalacheck, cats, discipline) .settings( moduleName := "docs", watchSources += (ThisBuild / baseDirectory).value / "docs", @@ -224,19 +221,6 @@ lazy val scalacheck = projectMatrix "org.typelevel" %%% "cats-effect-testkit" % Version.CE3.cats % Test) ) -lazy val specs2 = projectMatrix - .in(file("modules/specs2")) - .sparse(withJS = true, withScala3 = false) - .dependsOn(core, cats % "test->compile") - .settings( - name := "specs2", - testFrameworks := Seq(new TestFramework("weaver.framework.CatsEffect")), - libraryDependencies ++= Seq( - "org.specs2" %%% "specs2-matcher" % Version.specs2 - ) - ) - .settings(WeaverPlugin.simpleLayout) - lazy val discipline = projectMatrix .in(file("modules/discipline")) .sparse(withJS = true, withScala3 = true) @@ -256,7 +240,7 @@ lazy val discipline = projectMatrix // ################################################################################################# lazy val effectCores: Seq[ProjectReference] = - coreCats.projectRefs ++ coreZio.projectRefs + coreCats.projectRefs lazy val coreCats = projectMatrix .in(file("modules/core/cats")) @@ -266,27 +250,11 @@ lazy val coreCats = projectMatrix .settings(scalaJSMacroTask) .settings(name := "cats-core") -lazy val coreZio = projectMatrix - .in(file("modules/core/zio")) - .sparse(withJS = true, withScala3 = false) - .dependsOn(core) - .settings(WeaverPlugin.simpleLayout) - .settings( - name := "zio-core", - libraryDependencies ++= - Seq( - "dev.zio" %%% "zio-interop-cats" % Version.CE3.zioInterop - ) - ) - // ################################################################################################# // Effect-specific frameworks // ################################################################################################# -lazy val effectFrameworks: Seq[ProjectReference] = Seq( - cats.projectRefs, - zio.projectRefs -).flatten +lazy val effectFrameworks: Seq[ProjectReference] = cats.projectRefs lazy val cats = projectMatrix .in(file("modules/framework/cats")) @@ -298,17 +266,6 @@ lazy val cats = projectMatrix testFrameworks := Seq(new TestFramework("weaver.framework.CatsEffect")) ) -lazy val zio = projectMatrix - .in(file("modules/framework/zio")) - .sparse(withJS = true, withScala3 = false) - .dependsOn(framework, coreZio, scalacheck % "test->compile") - .settings(WeaverPlugin.simpleLayout) - .settings( - name := "zio", - testFrameworks := Seq(new TestFramework("weaver.framework.ZIO")), - libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % Version.scalaJavaTime % Test - ) - // ################################################################################################# // Intellij // ################################################################################################# diff --git a/docs/global_resources.md b/docs/global_resources.md index 8fec8043..27dfd0f5 100644 --- a/docs/global_resources.md +++ b/docs/global_resources.md @@ -18,11 +18,6 @@ NB : the implementations have to be static objects. ```scala mdoc import weaver._ -// The same API / developer experience is offered with any of the following imports : -// import weaver.monixcompat._ -// import weaver.monixbiocompat._ -// import weaver.ziocompat._ - import cats.effect.IO import cats.effect.Resource @@ -129,7 +124,7 @@ class MyOtherSuite(global: GlobalRead) extends IOSuite { def sharedResource: Resource[IO, String] = sharedResourceOrFallback(global) - test("oops, forgot something here") { sharedString => + test("oops, forgot something here") { sharedString => IO(expect(sharedString == "hello world!")) } } diff --git a/docs/installation.md b/docs/installation.md index e6a93198..d8899736 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -3,9 +3,7 @@ id: installation title: Installation --- -All of the artifacts below are available for both **JVM and Scala.js**. - -Note, that artifacts that use Cats Effect 3 are published under a different version to those published for Cats Effect 2 (minor version bump), because they're binary incompatible. +The artifacts below are available for **Scala JVM, Scala.js, Scala-native**. ```scala mdoc:passthrough import weaver.docs._ @@ -30,4 +28,3 @@ the effect-type library you've elected to use (or test against). Refer yourself to the library specific pages to get the correct configuration. - [cats](cats_effect_usage.md) -- [zio](zio_usage.md) diff --git a/docs/intellij.md b/docs/intellij.md index 129b0e8e..2e94a977 100644 --- a/docs/intellij.md +++ b/docs/intellij.md @@ -11,7 +11,7 @@ We (the maintainers) had tried to build an IntelliJ plugin. It worked but its ma ## Installation -Ensure the JUnit plugin is enabled in IntelliJ. Nothing else is needed (as long as weaver is declared correctly in your build). +Ensure the JUnit plugin is enabled in IntelliJ. Nothing else is needed (as long as weaver is declared correctly in your build). ## Usage @@ -46,7 +46,7 @@ object MyIgnoreSuite extends SimpleIOSuite { A `.only` extension method is provided on strings, and can be used when declaring tests. When at least one test is "tagged" as such in a suite, weaver will ignore all tests but the ones that have the "only" tag. Note: `.ignore` has precedence over `.only`. -```scala mdoc   +```scala mdoc import weaver._ import cats.effect._ diff --git a/docs/other_effects.md b/docs/other_effects.md new file mode 100644 index 00000000..9bd7d79b --- /dev/null +++ b/docs/other_effects.md @@ -0,0 +1,15 @@ +--- +id: other_effects + +title: Other effects +--- + +Starting with version 0.8.0, Weaver does no longer offer out-of-the-box support for other effect types +than cats-effect. + +We (maintainers) are happy to keep the core of weaver effect-agnostic, in an effort to allow for third party +to resurrect support for the effect types they use in repository they control. + +You can read about the rationale for decision [here](https://github.com/disneystreaming/weaver-test/discussions/570). Feel free to ping us via a github discussion, if you want to tackle. + +If you are looking for documentation of them maintenance branch of weaver that did support other effect types, you can find it [over there](https://disneystreaming.github.io/weaver-test/docs/0.6.15/installation). diff --git a/docs/specs2.md b/docs/specs2.md index 5c245019..420b957d 100644 --- a/docs/specs2.md +++ b/docs/specs2.md @@ -1,77 +1,6 @@ --- id: specs2 -title: specs2 integration +title: specs2 (discontinued) --- -Weaver comes with [specs2](http://specs2.org/) matchers interop, allowing for matcher style testing. - -## Installation - -You'll need to install an additional dependency in order to use specs2 matchers with Weaver. - -### SBT -```scala -libraryDependencies += "com.disneystreaming" %% "weaver-specs2" % "@VERSION@" % Test -``` - -### Mill -```scala -object test extends Tests { - def ivyDeps = Agg( - ivy"com.disneystreaming::weaver-specs2:@VERSION@" - ) -} -``` - -## Usage - -Add the `weaver.specs2compat.IOMatchers` mixin to use specs2 matchers within your test suite. - -```scala mdoc -import weaver.SimpleIOSuite - -import weaver.specs2compat.IOMatchers - -object MatchersSpec extends SimpleIOSuite with IOMatchers { - pureTest("pureTest { 1 must beEqualTo(1) }") { - 1 must beEqualTo(1) - } - - pureTest("pureTest { 1 must be_==(1) }") { - 1 must be_==(1) - } - - pureTest("pureTest { 1 mustEqual 1 }") { - 1 mustEqual 1 - } - - pureTest("pureTest { 1 === 1 }") { - 1 === 1 - } - - pureTest("pureTest { 1 must beEqualTo(1) }") { - 1 must beEqualTo(1) - } - - pureTest("pureTest { 1 must be_==(1) }") { - 1 must be_==(1) - } - - pureTest("pureTest { 1 mustEqual 1 }") { - 1 mustEqual 1 - } - - pureTest("pureTest { 1 === 1 }") { - 1 === 1 - } - - pureTest("failure example") { - 1 must beEqualTo(2) - } -} - -``` - -```scala mdoc:passthrough -println(weaver.docs.Output.runSuites(MatchersSpec)) -``` +The specs2-matchers integration has been dropped in weaver 0.8.0. If you want to test CE code with specs2 UX, please consider using [cats-effect-testing](https://github.com/typelevel/cats-effect-testing). diff --git a/docs/zio_usage.md b/docs/zio_usage.md deleted file mode 100644 index a4a6389a..00000000 --- a/docs/zio_usage.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -id: zio -title: ZIO usage ---- - -## Installation - -You'll need to install an additional dependency in order to use weaver to test ZIO programs. - -### SBT -```scala -libraryDependencies += "com.disneystreaming" %% "weaver-zio" % "@VERSION@" % Test -testFrameworks += new TestFramework("weaver.framework.ZIO") -``` - -### Mill -```scala -object test extends Tests { - def ivyDeps = Agg( - ivy"com.disneystreaming::weaver-zio:@VERSION@" - ) - def testFramework = "weaver.framework.ZIO" -} -``` - -## Usage - -Start with the following imports : - -```scala mdoc -import weaver.ziocompat._ -``` - -Weaver tries to respect ZIO's idiomatic usage, and leverages the [ZLayer](https://zio.dev/docs/howto/howto_use_layers) construct to provide environment. `ZLayer` can be trivially constructed from a `Managed` (the zio counterpart to `cats.effect.Resource`), and therefore encompasses `beforeAndAfterAll` semantics. - -Assuming the following module : - -```scala mdoc -import zio._ -import org.http4s.blaze.client._ -import org.http4s.client._ - -object modules { - type Http = Has[Client[Task]] - object Http { - def get(uri : String) : RIO[modules.Http, Int] = - ZIO.accessM(_.get.get(uri)(response => UIO(response.status.code))) - } -} -import modules._ -``` - -declare a shared layer as such : - -```scala mdoc -// Needed to instantiate an http4s client against ZIO -import zio.interop.catz._ - -// ZIOSuite requires the type of the shared layer to be defined as a type -// parameter. That is because zio needs to implicitly derive some type-tag -// instance for its built-in dependency injection mechanism to work. -// -// NB, the default environment ZEnv is provided by default. -// -object HttpSuite extends ZIOSuite[Http] { - - // Sharing a single layer across all tests - override val sharedLayer : ZLayer[ZEnv, Throwable, Http] = - ZLayer.fromManaged { - val makeHttpClient = ZIO.runtime[ZEnv].map { implicit runtime => - BlazeClientBuilder[Task].resource.toManagedZIO - } - Managed.fromEffect(makeHttpClient).flatten - } - - test("Standard test") { - for { - statusCode <- Http.get("https://httpbin.org/get") - } yield expect(statusCode == 200) - } - - test("A log module is available") { - for { - _ <- log.info("Calling https://httpbin.org/oops") - statusCode <- Http.get("https://httpbin.org/oops") - } yield expect(statusCode == 404) - } - -} -``` - -## Usage without shared layer - -If you have no need for a shared layer, you can simply use the following. -Note that you can still use side effects provided by the default environment `ZEnv`  - -```scala mdoc -import zio.duration._ - -object SimpleExample extends SimpleZIOSuite { - - pureTest("no side effect"){ - expect("hello".size == 5) - } - - test("with side effects"){ - for { - before <- zio.clock.currentDateTime - _ <- zio.clock.sleep(2.second) - after <- zio.clock.currentDateTime - } yield expect(before != after) - } - -} -``` - - diff --git a/modules/core/zio/src/weaver/ziocompat/FiberRefLog.scala b/modules/core/zio/src/weaver/ziocompat/FiberRefLog.scala deleted file mode 100644 index ff8201a3..00000000 --- a/modules/core/zio/src/weaver/ziocompat/FiberRefLog.scala +++ /dev/null @@ -1,25 +0,0 @@ -package weaver.ziocompat - -import cats.data.Chain - -import weaver.Log - -import zio._ -import zio.clock.Clock - -class FiberRefLog(ref: FiberRef[Chain[Log.Entry]], clock: Clock.Service) - extends LogModule.Service(clock) { - self => - def log(l: => Log.Entry): UIO[Unit] = - ref.modify(current => ((), current.append(l))) - - def logs: UIO[Chain[Log.Entry]] = - ref.get -} - -object FiberRefLog { - def apply( - ref: FiberRef[Chain[Log.Entry]], - clock: Clock.Service): LogModule.Service = - new FiberRefLog(ref, clock) -} diff --git a/modules/core/zio/src/weaver/ziocompat/LTTResourceTag.scala b/modules/core/zio/src/weaver/ziocompat/LTTResourceTag.scala deleted file mode 100644 index 42f5992d..00000000 --- a/modules/core/zio/src/weaver/ziocompat/LTTResourceTag.scala +++ /dev/null @@ -1,15 +0,0 @@ -package weaver -package ziocompat - -import zio.LightTypeTag - -case class LTTResourceTag[A](tag: LightTypeTag) extends ResourceTag[A] { - def description: String = tag.repr - - def cast(obj: Any): Option[A] = { - try { Some(obj.asInstanceOf[A]) } - catch { - case _: Throwable => None - } - } -} diff --git a/modules/core/zio/src/weaver/ziocompat/Live.scala b/modules/core/zio/src/weaver/ziocompat/Live.scala deleted file mode 100644 index 94ae7c22..00000000 --- a/modules/core/zio/src/weaver/ziocompat/Live.scala +++ /dev/null @@ -1,24 +0,0 @@ -package weaver -package ziocompat - -import zio.{ IO, URLayer, ZEnv, ZIO } - -/** - * Service used for getting the real environment during tests. This is useful - * for timing or getting random values during tests. For example, getting a - * random port for starting servers or timing an action with the real clock. - * This pattern is inspired by ZIO-test - */ -object Live { - trait Service { - def live[E, A](zio: ZIO[ZEnv, E, A]): IO[E, A] - } - - def live[E, A](zio: ZIO[ZEnv, E, A]): ZIO[Live, E, A] = - ZIO.serviceWith[Service](_.live(zio)) - - def apply(): URLayer[ZEnv, Live] = ZIO.environment[ZEnv].map(env => - new Service { - override def live[E, A](zio: ZIO[ZEnv, E, A]): IO[E, A] = zio.provide(env) - }).toLayer -} diff --git a/modules/core/zio/src/weaver/ziocompat/Test.scala b/modules/core/zio/src/weaver/ziocompat/Test.scala deleted file mode 100644 index e903aa17..00000000 --- a/modules/core/zio/src/weaver/ziocompat/Test.scala +++ /dev/null @@ -1,27 +0,0 @@ -package weaver.ziocompat - -import java.util.concurrent.TimeUnit - -import scala.concurrent.duration._ -import scala.util.control.NonFatal - -import weaver.{ Expectations, Result, TestOutcome } - -import zio._ - -object Test { - - def apply[R <: Has[_]]( - name: String, - f: ZIO[Env[R], Throwable, Expectations] - ): ZIO[Env[R], Nothing, TestOutcome] = - for { - start <- Live.live(zio.clock.currentTime(TimeUnit.MILLISECONDS)) - res <- f - .unrefine { case NonFatal(e) => e } - .fold(Result.from, Result.fromAssertion) - end <- Live.live(zio.clock.currentTime(TimeUnit.MILLISECONDS)) - logs <- LogModule.logs - } yield TestOutcome(name, (end - start).millis, res, logs) - -} diff --git a/modules/core/zio/src/weaver/ziocompat/ZIOGlobalResource.scala b/modules/core/zio/src/weaver/ziocompat/ZIOGlobalResource.scala deleted file mode 100644 index ee3e3eeb..00000000 --- a/modules/core/zio/src/weaver/ziocompat/ZIOGlobalResource.scala +++ /dev/null @@ -1,36 +0,0 @@ -package weaver -package ziocompat - -import cats.effect.Resource - -import zio._ -import zio.interop.catz._ - -trait ZIOGlobalResource extends weaver.GlobalResourceF[T] { - - def share(global: GlobalWrite): RManaged[ZEnv, Unit] - - final def sharedResources( - global: weaver.GlobalResourceF.Write[T]): Resource[T, Unit] = - share(ZIOGlobalResource.toZIO(global)).toResourceZIO - -} - -object ZIOGlobalResource { - - trait Write { - def put[A: Tag](value: A, label: Option[String] = None): RIO[ZEnv, Unit] - - def putM[A: Tag]( - value: A, - label: Option[String] = None): RManaged[ZEnv, Unit] = - ZManaged.fromEffect(put(value, label)) - } - - private def toZIO(global: weaver.GlobalResourceF.Write[T]): Write = - new Write { - def put[A: Tag](value: A, label: Option[String]): RIO[ZEnv, Unit] = - global.put(value, label) - } - -} diff --git a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala deleted file mode 100644 index 43ef0230..00000000 --- a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala +++ /dev/null @@ -1,29 +0,0 @@ -package weaver -package ziocompat - -import cats.Parallel -import cats.effect.Async - -import zio._ -import zio.interop.catz - -object ZIOUnsafeRun extends UnsafeRun[T] { - - type CancelToken = Fiber.Id => Exit[Throwable, Unit] - - implicit val runtime = Runtime.default - - implicit def effect: Async[T] = catz.asyncInstance[ZEnv] - - implicit def parallel: Parallel[T] = - catz.core.parallelInstance[ZEnv, Throwable] - - def background(task: T[Unit]): CancelToken = - runtime.unsafeRunAsyncCancelable(task)(_ => ()) - def cancel(token: Fiber.Id => Exit[Throwable, Unit]): Unit = - discard[Exit[Throwable, Unit]](token(Fiber.Id.None)) - - def sync(task: T[Unit]): Unit = runtime.unsafeRun(task) - - def async(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) -} diff --git a/modules/core/zio/src/weaver/ziocompat/log.scala b/modules/core/zio/src/weaver/ziocompat/log.scala deleted file mode 100644 index a1ef9258..00000000 --- a/modules/core/zio/src/weaver/ziocompat/log.scala +++ /dev/null @@ -1,31 +0,0 @@ -package weaver -package ziocompat - -import zio._ - -object log { - - def info( - msg: => String, - ctx: Map[String, String] = Map.empty, - cause: Throwable = null)(implicit loc: SourceLocation) = - ZIO.accessM[LogModule](_.get[LogModule.Service].info(msg, ctx, cause)) - - def debug( - msg: => String, - ctx: Map[String, String] = Map.empty, - cause: Throwable = null)(implicit loc: SourceLocation) = - ZIO.accessM[LogModule](_.get[LogModule.Service].debug(msg, ctx, cause)) - - def warn( - msg: => String, - ctx: Map[String, String] = Map.empty, - cause: Throwable = null)(implicit loc: SourceLocation) = - ZIO.accessM[LogModule](_.get[LogModule.Service].warn(msg, ctx, cause)) - - def error( - msg: => String, - ctx: Map[String, String] = Map.empty, - cause: Throwable = null)(implicit loc: SourceLocation) = - ZIO.accessM[LogModule](_.get[LogModule.Service].error(msg, ctx, cause)) -} diff --git a/modules/core/zio/src/weaver/ziocompat/package.scala b/modules/core/zio/src/weaver/ziocompat/package.scala deleted file mode 100644 index 6187aff2..00000000 --- a/modules/core/zio/src/weaver/ziocompat/package.scala +++ /dev/null @@ -1,46 +0,0 @@ -package weaver - -import java.util.concurrent.TimeUnit - -import cats.data.Chain - -import zio._ -import zio.clock.Clock -import zio.interop.catz._ - -package object ziocompat { - - object LogModule { - abstract class Service(clock: Clock.Service) - extends Log[UIO](clock.currentTime(TimeUnit.MILLISECONDS)) { - def logs: UIO[Chain[Log.Entry]] - } - def logs: URIO[LogModule, Chain[Log.Entry]] = ZIO.accessM(_.get.logs) - } - type LogModule = Has[LogModule.Service] - type Live = Has[Live.Service] - - type T[A] = RIO[ZEnv, A] - type Env[R <: Has[_]] = ZEnv with Live with R with LogModule - - val unitTag = implicitly[Tag[Unit]] - type ZIOSuite[R <: Has[_]] = MutableZIOSuite[R] - type SimpleZIOSuite = SimpleMutableZIOSuite - type GlobalResource = ZIOGlobalResource - type GlobalRead = GlobalResourceF.Read[T] - type GlobalWrite = ZIOGlobalResource.Write - type FunSuite = FunZIOSuite - - implicit class GlobalReadExt(private val read: GlobalRead) extends AnyVal { - def getManaged[A](label: Option[String] = None)( - implicit rt: ResourceTag[A]): RManaged[ZEnv, A] = - ZManaged.fromEffect(read.getOrFail[A](label)) - def getLayer[A](label: Option[String] = None)( - implicit rt: Tag[A]): RLayer[ZEnv, Has[A]] = - ZLayer.fromEffect(read.getOrFail[A](label)) - } - - implicit def resourceTagFromTag[A](implicit A: Tag[A]): ResourceTag[A] = - LTTResourceTag(A.tag) - -} diff --git a/modules/core/zio/src/weaver/ziocompat/suites.scala b/modules/core/zio/src/weaver/ziocompat/suites.scala deleted file mode 100644 index b8856279..00000000 --- a/modules/core/zio/src/weaver/ziocompat/suites.scala +++ /dev/null @@ -1,112 +0,0 @@ -package weaver -package ziocompat - -import scala.util.Try - -import cats.data.Chain - -import fs2._ -import zio._ -import zio.clock.Clock -import zio.interop.catz._ - -trait BaseZIOSuite extends RunnableSuite[T] with EffectSuite.Provider[T] - -abstract class BaseMutableZIOSuite[Res <: Has[_]](implicit tag: Tag[Res]) - extends BaseZIOSuite { - - override implicit protected def effectCompat = ZIOUnsafeRun - - val sharedLayer: ZLayer[ZEnv with LogModule, Throwable, Res] - - def maxParallelism: Int = 10000 - - private[this] type Test = ZIO[Env[Res], Nothing, TestOutcome] - - protected def registerTest(name: TestName)(test: Test): Unit = - synchronized { - if (isInitialized) throw initError() - testSeq = testSeq :+ ((name, test)) - } - - def getSuite: EffectSuite[T] = this - - def plan: List[TestName] = testSeq.map(_._1).toList - - def pureTest(name: TestName)(run: => Expectations): Unit = - registerTest(name)(Test(name.name, ZIO(run))) - - def test(name: TestName)( - run: => ZIO[Env[Res], Throwable, Expectations]): Unit = - registerTest(name)(Test(name.name, ZIO.fromTry(Try { run }).flatten)) - - override def spec(args: List[String]): Stream[T, TestOutcome] = - synchronized { - if (!isInitialized) isInitialized = true - val argsFilter = Filters.filterTests(this.name)(args) - val filteredTests = testSeq.collect { - case (name, test) if argsFilter(name) => test - } - if (filteredTests.isEmpty) Stream.empty // no need to allocate resources - else { - for { - ref <- Stream.eval(FiberRef.make(Chain.empty[Log.Entry])) - testLayer: RLayer[ZEnv, Live with LogModule with ZEnv] = - ZEnv.any >+> Live() ++ ZLayer.fromService[ - Clock.Service, - LogModule.Service](FiberRefLog(ref, _)) - suiteLayer = - (testLayer >+> sharedLayer).passthrough - resource <- Stream.resource(suiteLayer.build.toResourceZIO) - result <- - if (maxParallelism > 1) { - Stream - .emits(filteredTests) - .lift[Task] - .parEvalMap(math.max(1, maxParallelism))(_.provide(resource)) - } else { - // Forcing a fork in `evalMap` to trigger a fiber ref copy. - Stream.emits(filteredTests) - .lift[Task] - .evalMap { t => - for { - before <- ref.get - fiber <- t.provide(resource).fork - result <- fiber.join - _ <- ref.set(before) - } yield result - } - } - } yield result - } - } - - private[this] var testSeq = Seq.empty[(TestName, Test)] - private[this] var isInitialized = false - - private[this] def initError() = - new AssertionError( - "Cannot define new tests after TestSuite was initialized" - ) - - override protected def adaptRunError: PartialFunction[Throwable, Throwable] = { - case FiberFailure(cause) => cause.asInstanceOf[Cause[Throwable]].squash - } -} - -abstract class MutableZIOSuite[Res <: Has[_]](implicit tag: Tag[Res]) - extends BaseMutableZIOSuite()(tag) - with Expectations.Helpers - -abstract class SimpleMutableZIOSuite extends MutableZIOSuite[Has[Unit]] { - override val sharedLayer: zio.ZLayer[ZEnv, Throwable, Has[Unit]] = - ZLayer.fromEffect(UIO.unit) -} - -trait FunZIOSuite - extends FunSuiteF[T] - with BaseZIOSuite - with Expectations.Helpers { - override implicit protected def effectCompat = ZIOUnsafeRun - def getSuite: EffectSuite[T] = this -} diff --git a/modules/framework/monix-bio/src/weaver/framework/MonixBIOFramework.scala b/modules/framework/monix-bio/src/weaver/framework/MonixBIOFramework.scala deleted file mode 100644 index 20d0469a..00000000 --- a/modules/framework/monix-bio/src/weaver/framework/MonixBIOFramework.scala +++ /dev/null @@ -1,23 +0,0 @@ -package weaver -package framework - -import java.io.PrintStream - -import weaver.monixbiocompat.{ - BaseIOSuite, - IOGlobalResource, - MonixBIOUnsafeRun -} - -import monix.bio.Task - -class MonixBIO(errorStream: PrintStream) - extends WeaverFramework("monix-bio", - MonixBIOFingerprints, - MonixBIOUnsafeRun, - errorStream) { - def this() = this(System.err) -} - -object MonixBIOFingerprints - extends WeaverFingerprints.Mixin[Task, BaseIOSuite, IOGlobalResource] diff --git a/modules/framework/monix-bio/test/src/weaver/monixbiocompat/FunSuiteTest.scala b/modules/framework/monix-bio/test/src/weaver/monixbiocompat/FunSuiteTest.scala deleted file mode 100644 index f0f6a76a..00000000 --- a/modules/framework/monix-bio/test/src/weaver/monixbiocompat/FunSuiteTest.scala +++ /dev/null @@ -1,16 +0,0 @@ -package weaver.monixbiocompat - -object FunSuiteTest extends FunSuite { - - test("and") { - expect(1 == 1) and expect(2 == 2) && not(expect(1 == 2)) - } - - test("forall (success)") { - forEach(List(true, true))(value => expect(value == true)) - } - - test("forall (failure)") { - not(forEach(List(true, false))(value => expect(value == true))) - } -} diff --git a/modules/framework/monix-bio/test/src/weaver/monixbiocompat/IOSuiteTest.scala b/modules/framework/monix-bio/test/src/weaver/monixbiocompat/IOSuiteTest.scala deleted file mode 100644 index c4cd5372..00000000 --- a/modules/framework/monix-bio/test/src/weaver/monixbiocompat/IOSuiteTest.scala +++ /dev/null @@ -1,69 +0,0 @@ -package weaver.monixbiocompat - -import cats.effect.Resource - -import weaver.framework.{ DogFood, MonixBIO } - -import monix.bio.Task -import sbt.testing.Status.{ Error, Failure } - -object IOSuiteTest extends MutableIOSuite { - override type Res = DogFood[Task] - override def sharedResource: Resource[monix.bio.Task, Res] = - DogFood.make(new MonixBIO) - - List( - TestWithExceptionInTest, - TestWithExceptionInExpectation, - TestWithExceptionInInitialisation - ).foreach { testSuite => - test(s"fail properly in ${testSuite.getClass.getSimpleName}") { dogfood => - dogfood.runSuite(testSuite).map { case (_, events) => - val maybeEvent = events.headOption - val maybeThrowable = maybeEvent.flatMap { event => - if (event.throwable().isDefined()) Some(event.throwable().get()) - else None - } - val maybeStatus = maybeEvent.map(_.status()) - expect(maybeStatus.contains(Error)) && - expect(maybeThrowable.map(_.getMessage).contains("oh no")) - } - } - } - - test("fail properly on failed expectations") { dogfood => - dogfood.runSuite(TestWithFailedExpectation).map { case (_, events) => - val maybeEvent = events.headOption - val maybeStatus = maybeEvent.map(_.status()) - expect(maybeStatus.contains(Failure)) - } - } - - object TestWithExceptionInTest extends SimpleIOSuite { - test("example test") { - Task.raiseError(new RuntimeException("oh no")) - } - } - - object TestWithExceptionInExpectation extends SimpleIOSuite { - test("example test") { - for { - _ <- Task.unit - } yield throw new RuntimeException("oh no") - } - } - - object TestWithExceptionInInitialisation extends SimpleIOSuite { - test("example test") { _ => - throw new RuntimeException("oh no") - } - } - - object TestWithFailedExpectation extends SimpleIOSuite { - test("example test") { _ => - for { - _ <- Task.unit - } yield expect(false) - } - } -} diff --git a/modules/framework/monix/src/weaver/framework/MonixFramework.scala b/modules/framework/monix/src/weaver/framework/MonixFramework.scala deleted file mode 100644 index 06c0122a..00000000 --- a/modules/framework/monix/src/weaver/framework/MonixFramework.scala +++ /dev/null @@ -1,19 +0,0 @@ -package weaver -package framework - -import java.io.PrintStream - -import weaver.monixcompat.{ BaseTaskSuite, MonixUnsafeRun, TaskGlobalResource } - -import monix.eval.Task - -class Monix(errorStream: PrintStream) - extends WeaverFramework("monix", - MonixFingerprints, - MonixUnsafeRun, - errorStream) { - def this() = this(System.err) -} - -object MonixFingerprints - extends WeaverFingerprints.Mixin[Task, BaseTaskSuite, TaskGlobalResource] diff --git a/modules/framework/monix/test/src/weaver/monixcompat/FunSuiteTest.scala b/modules/framework/monix/test/src/weaver/monixcompat/FunSuiteTest.scala deleted file mode 100644 index 5f45d304..00000000 --- a/modules/framework/monix/test/src/weaver/monixcompat/FunSuiteTest.scala +++ /dev/null @@ -1,17 +0,0 @@ -package weaver -package monixcompat - -object FunSuiteTest extends FunSuite { - - test("and") { - expect(1 == 1) and expect(2 == 2) && not(expect(1 == 2)) - } - - test("forall (success)") { - forEach(List(true, true))(value => expect(value == true)) - } - - test("forall (failure)") { - not(forEach(List(true, false))(value => expect(value == true))) - } -} diff --git a/modules/framework/monix/test/src/weaver/monixcompat/TaskSuiteTest.scala b/modules/framework/monix/test/src/weaver/monixcompat/TaskSuiteTest.scala deleted file mode 100644 index 9543ad4d..00000000 --- a/modules/framework/monix/test/src/weaver/monixcompat/TaskSuiteTest.scala +++ /dev/null @@ -1,74 +0,0 @@ -package weaver.monixcompat - -import cats.effect.Resource - -import weaver.framework.{ DogFood, Monix } - -import monix.eval.Task -import sbt.testing.Status.{ Error, Failure } - -object TaskSuiteTest extends MutableTaskSuite { - - type Res = DogFood[Task] - def sharedResource: Resource[Task, DogFood[Task]] = - DogFood.make(new Monix) - - List( - TestWithExceptionInTest, - TestWithExceptionInExpectation, - TestWithExceptionInInitialisation - ).foreach { testSuite => - test(s"fail properly in ${testSuite.getClass.getSimpleName}") { dogfood => - dogfood.runSuite(testSuite).map { case (_, events) => - val maybeEvent = events.headOption - val maybeThrowable = maybeEvent.flatMap { event => - if (event.throwable().isDefined()) Some(event.throwable().get()) - else None - } - val maybeStatus = maybeEvent.map(_.status()) - - expect.all( - maybeStatus.contains(Error), - maybeThrowable.map(_.getMessage).contains("oh no") - ) - - } - } - } - - test("fail properly on failed expectations") { dogfood => - dogfood.runSuite(TestWithFailedExpectation).map { case (_, events) => - val maybeEvent = events.headOption - val maybeStatus = maybeEvent.map(_.status()) - expect(maybeStatus.contains(Failure)) - } - } - - object TestWithExceptionInTest extends SimpleTaskSuite { - test("example test") { - Task.raiseError(new RuntimeException("oh no")) - } - } - - object TestWithExceptionInExpectation extends SimpleTaskSuite { - test("example test") { - for { - _ <- Task.unit - } yield throw new RuntimeException("oh no") - } - } - - object TestWithExceptionInInitialisation extends SimpleTaskSuite { - test("example test") { _ => - throw new RuntimeException("oh no") - } - } - - object TestWithFailedExpectation extends SimpleTaskSuite { - test("example test") { _ => - for { - _ <- Task.unit - } yield expect(false) - } - } -} diff --git a/modules/framework/zio/src/weaver/framework/ZIO.scala b/modules/framework/zio/src/weaver/framework/ZIO.scala deleted file mode 100644 index 324339fd..00000000 --- a/modules/framework/zio/src/weaver/framework/ZIO.scala +++ /dev/null @@ -1,16 +0,0 @@ -package weaver -package framework - -import java.io.PrintStream - -import weaver.ziocompat.{ BaseZIOSuite, T, ZIOGlobalResource, ZIOUnsafeRun } - -import ZIOUnsafeRun.effect - -class ZIO(errorStream: PrintStream) - extends WeaverFramework("zio", ZIOFingerprints, ZIOUnsafeRun, errorStream) { - def this() = this(System.err) -} - -object ZIOFingerprints - extends WeaverFingerprints.Mixin[T, BaseZIOSuite, ZIOGlobalResource] diff --git a/modules/framework/zio/test/src-jvm/Global.scala b/modules/framework/zio/test/src-jvm/Global.scala deleted file mode 100644 index 789c1793..00000000 --- a/modules/framework/zio/test/src-jvm/Global.scala +++ /dev/null @@ -1,33 +0,0 @@ -package weaver -package ziocompat - -import zio._ - -object SharedResources extends ZIOGlobalResource { - def share(global: GlobalWrite): RManaged[ZEnv, Unit] = - for { - foo <- ZManaged.succeed("hello world!") - _ <- global.putM(foo) - } yield () -} - -class ResourceSharingSuite(global: GlobalRead) extends ZIOSuite[Has[String]] { - - val sharedLayer: RLayer[ZEnv, Has[String]] = - global.getLayer[String]() - - test("a stranger, from the outside ! ooooh") { - ZIO.access[Has[String]](_.get).map(s => expect(s == "hello world!")) - } -} - -class OtherResourceSharingSuite(global: GlobalRead) - extends ZIOSuite[Has[Option[Int]]] { - val sharedLayer: RLayer[ZEnv, Has[Option[Int]]] = - ZLayer.fromEffect(global.get[Int]()) - - test("oops, forgot something here") { - ZIO.access[Has[Option[Int]]](_.get).map(o => expect(o.isEmpty)) - } - -} diff --git a/modules/framework/zio/test/src/weaver/ziocompat/CheckersTest.scala b/modules/framework/zio/test/src/weaver/ziocompat/CheckersTest.scala deleted file mode 100644 index 0731b2fe..00000000 --- a/modules/framework/zio/test/src/weaver/ziocompat/CheckersTest.scala +++ /dev/null @@ -1,67 +0,0 @@ -package weaver.ziocompat - -import weaver.scalacheck._ - -import org.scalacheck.Gen -import zio.duration._ - -object CheckersTest extends SimpleZIOSuite with Checkers { - - override def checkConfig: CheckConfig = - super.checkConfig.copy(perPropertyParallelism = 100) - - test("universal") { - forall(Gen.posNum[Int]) { a => - expect(a > 0) - } - } - - test("form 1") { - forall { (a: Int) => - expect(a * 2 == 2 * a) - } - } - - test("form 2") { - forall { (a1: Int, a2: Int) => - expect(a1 * a2 == a2 * a1) - } - } - - test("form 3") { - forall { (a1: Int, a2: Int, a3: Int) => - expect(a1 * a2 * a3 == a3 * a2 * a1) - } - } - - test("form 4") { - forall { (a1: Int, a2: Int, a3: Int, a4: Int) => - expect(a1 * a2 * a3 * a4 == a4 * a3 * a2 * a1) - } - } - - test("form 5") { - forall { (a1: Int, a2: Int, a3: Int, a4: Int, a5: Int) => - expect(a1 * a2 * a3 * a4 * a5 == a5 * a4 * a3 * a2 * a1) - } - } - - test("form 6") { - forall { (a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int) => - expect(a1 * a2 * a3 * a4 * a5 * a6 == a6 * a5 * a4 * a3 * a2 * a1) - } - } - - test("IO form (1)") { - forall { (a1: Int) => - zio.clock.sleep(100.millis).map(_ => expect(a1 * 2 == a1 + a1)) - } - } - - test("IO form (2)") { - forall { (a1: Int, a2: Int) => - zio.clock.sleep(1.second).map(_ => expect(a1 + a2 == a2 + a1)) - } - } - -} diff --git a/modules/framework/zio/test/src/weaver/ziocompat/FunSuiteTest.scala b/modules/framework/zio/test/src/weaver/ziocompat/FunSuiteTest.scala deleted file mode 100644 index 58cb717c..00000000 --- a/modules/framework/zio/test/src/weaver/ziocompat/FunSuiteTest.scala +++ /dev/null @@ -1,16 +0,0 @@ -package weaver.ziocompat - -object FunSuiteTest extends FunSuite { - - test("and") { - expect(1 == 1) and expect(2 == 2) && not(expect(1 == 2)) - } - - test("forall (success)") { - forEach(List(true, true))(value => expect(value == true)) - } - - test("forall (failure)") { - not(forEach(List(true, false))(value => expect(value == true))) - } -} diff --git a/modules/framework/zio/test/src/weaver/ziocompat/MutableZIOSuiteTest.scala b/modules/framework/zio/test/src/weaver/ziocompat/MutableZIOSuiteTest.scala deleted file mode 100644 index 88f79c36..00000000 --- a/modules/framework/zio/test/src/weaver/ziocompat/MutableZIOSuiteTest.scala +++ /dev/null @@ -1,291 +0,0 @@ -package weaver.ziocompat - -import java.time.{ DateTimeException, OffsetDateTime } -import java.util.concurrent.TimeUnit - -import weaver.Log -import weaver.framework.DogFood -import weaver.ziocompat.modules._ - -import sbt.testing.Status -import zio._ -import zio.clock.Clock -import zio.duration._ -import zio.interop.catz._ - -object ZIOSuiteTest extends ZIOSuite[KVStore with DogFoodz] { - - override def maxParallelism: Int = 1 - - override val sharedLayer: ZLayer[ZEnv, Throwable, KVStore with DogFoodz] = { - val kvstore: ZLayer[ZEnv, Throwable, KVStore] = ZLayer.fromEffect { - Ref - .make(Map.empty[String, String]) - .map(new KVStore.RefBased(_)) - } - val dogfood: ZLayer[ZEnv, Throwable, DogFoodz] = { - ZLayer.fromManaged(DogFood.make(new weaver.framework.ZIO()).toManagedZIO) - } - - dogfood ++ kvstore - } - - // Don't do this at home, kids - test("setting some value in a shared store") { - for { - _ <- zio.clock.sleep(1.seconds) - _ <- KVStore.put("hello", "world") - helloValue <- KVStore.get("hello") - _ <- log.info(helloValue.getOrElse("empty")) - } yield expect(List(1, 2, 3).size == 3) - } - - test("getting the value set in a previous test") { - for { - previous <- KVStore.delete("hello") - now <- KVStore.get("hello") - } yield expect(previous == Some("world")) and expect(now == None) - } - - List( - TestWithExceptionInTest, - TestWithExceptionInExpectation, - TestWithExceptionInInitialisation, - TestWithEventualDiedSharedLayer, - TestWithFailedSharedLayer - ).foreach { testSuite => - test(s"fail properly in ${testSuite.getClass.getSimpleName}") { - for { - (_, events) <- DogFoodz.runSuite(testSuite) - } yield { - val maybeEvent = events.headOption - val maybeThrowable = maybeEvent.flatMap { event => - if (event.throwable().isDefined()) Some(event.throwable().get()) - else None - } - val maybeStatus = maybeEvent.map(_.status()) - expect(maybeStatus.contains(Status.Error)) && - expect(maybeThrowable.map(_.getMessage).contains("oh no")) - } - } - } - - test("fail properly on failed expectations") { - for { - (_, events) <- DogFoodz.runSuite(TestWithFailedExpectation) - } yield { - val maybeEvent = events.headOption - val maybeStatus = maybeEvent.map(_.status()) - expect(maybeStatus.contains(Status.Failure)) - } - } - - test("logs contain only the logs from each test (parallelism = 3)") { - new FiberRefLogTest(3).spec(List.empty) - .map(outcome => - expect(!outcome.status.isFailed) and expect(outcome.log.size == 1)) - .compile - .foldMonoid - } - - test("logs contain only the logs from each test (parallelism = 1)") { - new FiberRefLogTest(1).spec(List.empty) - .map(outcome => - expect(!outcome.status.isFailed) and expect(outcome.log.size == 1)) - .compile - .foldMonoid - } - - test("logs can use adapter to give logs from app") { - LogAdapterTest.spec(List.empty).map(outcome => - expect(!outcome.status.isFailed) and - expect(outcome.log.map(_.msg) == cats.data.Chain( - "one", - "two", - "three", - "four"))).compile.foldMonoid - } - - test("live clock") { - TestLiveSharedLayer.spec(List.empty).map(outcome => - expect(!outcome.status.isFailed) and - expect(outcome.duration.length > 0)).compile.foldMonoid - } - - object LogAdapterTest extends ZIOSuite[Has[SomeApp.Service]] { - - val loggingAdapterLayer: RLayer[LogModule, Has[SomeLogger.Service]] = - ZLayer.fromService[LogModule.Service, SomeLogger.Service] { - weaverLogger => - new SomeLogger.Service { - override def log(msg: String): Task[Unit] = - weaverLogger.log(Log.Entry( - 0L, - msg, - Map.empty, - Log.debug, - None, - weaver.SourceLocation("", "", 0))) - } - } - - val sharedLayer: ZLayer[ZEnv with LogModule, Throwable, Has[SomeApp.Service]] = - loggingAdapterLayer >>> SomeApp.program - - test("can run and log") { - for { - _ <- SomeApp.run() - logs <- LogModule.logs - } yield expect(logs.size == 4) - } - - } - - object SomeLogger { - trait Service { - def log(msg: String): Task[Unit] - } - def log(msg: String): RIO[Has[Service], Unit] = - ZIO.accessM[Has[Service]](_.get.log(msg)) - } - - object SomeApp { - class Program(logger: SomeLogger.Service) extends SomeApp.Service { - def run(): Task[Unit] = for { - _ <- logger.log("one") - _ <- logger.log("two") - _ <- logger.log("three") - _ <- logger.log("four") - } yield () - } - - val program: URLayer[Has[SomeLogger.Service], Has[SomeApp.Service]] = - ZLayer.fromService[SomeLogger.Service, SomeApp.Service](new Program(_)) - - trait Service { - def run(): Task[Unit] - } - def run(): RIO[Has[Service], Unit] = - ZIO.accessM[Has[Service]](_.get.run()) - } - - class FiberRefLogTest(override val maxParallelism: Int) - extends SimpleZIOSuite { - test("debug log") { - (log.debug("a log") *> LogModule.logs).map(logs => expect(logs.size == 1)) - } - - test("error log") { - (log.error("a log") *> LogModule.logs).map(logs => expect(logs.size == 1)) - } - - test("info log") { - (log.info("a log") *> LogModule.logs).map(logs => expect(logs.size == 1)) - } - - test("warning log") { - (log.warn("a log") *> LogModule.logs).map(logs => expect(logs.size == 1)) - } - } - - object TestWithExceptionInTest extends SimpleZIOSuite { - test("example test") { - Task.fail(new RuntimeException("oh no")) - } - } - - object TestWithExceptionInExpectation extends SimpleZIOSuite { - test("example test") { - for { - _ <- Task.succeed(()) - } yield throw new RuntimeException("oh no") - } - } - - object TestWithExceptionInInitialisation extends SimpleZIOSuite { - test("example test") { - throw new RuntimeException("oh no") - } - } - - object TestWithFailedExpectation extends SimpleZIOSuite { - test("example test") { - for { - _ <- Task.succeed(()) - } yield expect(false) - } - } - - object TestWithFailedSharedLayer extends MutableZIOSuite[Has[Unit]] { - override val sharedLayer: ZLayer[zio.ZEnv, Throwable, Has[Unit]] = - ZLayer.fail(new RuntimeException("oh no")) - - test("example test") { - ZIO.succeed(expect(true)) - } - } - - object TestWithEventualDiedSharedLayer extends MutableZIOSuite[Has[Unit]] { - override val sharedLayer: ZLayer[zio.ZEnv, Throwable, Has[Unit]] = - ZLayer.fromEffect(ZIO.effect(throw new RuntimeException("oh no"))) - - test("example test") { - ZIO.succeed(expect(true)) - } - } - object TestLiveSharedLayer extends MutableZIOSuite[Clock] { - override val sharedLayer: ZLayer[zio.ZEnv, Throwable, Clock] = - ZLayer.succeed(new Clock.Service { - def currentTime(unit: TimeUnit) = ??? - def currentDateTime: IO[DateTimeException, OffsetDateTime] = ??? - def nanoTime: UIO[Long] = UIO(42) - def sleep(duration: Duration): UIO[Unit] = ??? - }) - - test("can allow overriding of the clock whilst still having correct test timings") { - // Ensure the test duration is longer than 0ms so test duration can be asserted when the suite is run - Live.live(clock.sleep(1.millis)) *> clock.nanoTime.map(time => - expect(time == 42)) - } - } -} - -object modules { - - type DogFoodz = Has[DogFood[T]] - type KVStore = Has[KVStore.Service] - - object DogFoodz { - def runSuite(suite: BaseZIOSuite): RIO[ZEnv with DogFoodz, DogFood.State] = - ZIO.accessM(_.get[DogFood[T]].runSuite(suite)) - } - - object KVStore { - - // boileplate usually written via macro - def put(k: String, v: String): RIO[KVStore, Unit] = - ZIO.accessM(_.get.put(k, v)) - def get(k: String): RIO[KVStore, Option[String]] = - ZIO.accessM(_.get.get(k)) - def delete(k: String): RIO[KVStore, Option[String]] = - ZIO.accessM(_.get.delete(k)) - - trait Service { - def put(k: String, v: String): UIO[Unit] - def get(k: String): UIO[Option[String]] - def delete(k: String): UIO[Option[String]] - } - - class RefBased(ref: Ref[Map[String, String]]) extends Service { - def put(k: String, v: String): zio.UIO[Unit] = ref.update(_ + (k -> v)) - - def get(k: String): zio.UIO[Option[String]] = ref.get.map(_.get(k)) - - def delete(k: String): zio.UIO[Option[String]] = - ref.getAndUpdate(_ - k).map(_.get(k)) - - } - - } - -} diff --git a/website/sidebars.json b/website/sidebars.json index c38d4834..b1842ffb 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -6,9 +6,7 @@ ], "Effect types": [ "cats", - "monix", - "monix_bio", - "zio" + "other_effects" ], "Features": [ "expectations", @@ -17,7 +15,6 @@ "logging", "tagging", "scalacheck", - "specs2", "discipline", "parallelism", "funsuite", From 6467ab15cc4ce5bb438b20556ee3749249e019d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Thu, 15 Sep 2022 14:52:42 +0200 Subject: [PATCH 28/33] Add version summary --- docs/other_effects.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/other_effects.md b/docs/other_effects.md index 9bd7d79b..3af9bff0 100644 --- a/docs/other_effects.md +++ b/docs/other_effects.md @@ -10,6 +10,14 @@ than cats-effect. We (maintainers) are happy to keep the core of weaver effect-agnostic, in an effort to allow for third party to resurrect support for the effect types they use in repository they control. -You can read about the rationale for decision [here](https://github.com/disneystreaming/weaver-test/discussions/570). Feel free to ping us via a github discussion, if you want to tackle. +You can read about the rationale for decision [here](https://github.com/disneystreaming/weaver-test/discussions/570). Feel free to ping us via a github discussion, if you seek to resurrect support for a given effect-type. If you are looking for documentation of them maintenance branch of weaver that did support other effect types, you can find it [over there](https://disneystreaming.github.io/weaver-test/docs/0.6.15/installation). + +We will try to fix critical bugs on the 0.6/0.7 series, as they get found. + +To summarise : + +* `0.8.x` and further -> CE3 only, active development, +* `0.7.x` -> CE3 + ZIO 1 support (maintenance mode, fixing critical bugs that not have workarounds) +* `0.6.x` -> CE2 + Monix/MonixBIO/ZIO 1 support (maintenance mode, fixing critical bugs that do not have workarounds) From eaedb978426f4254311a517f1de5ad4623388b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Thu, 15 Sep 2022 14:53:47 +0200 Subject: [PATCH 29/33] More clean-up --- docs/funsuite.md | 8 -------- docs/motivation.md | 2 +- modules/docs/src/main/scala/weaver/MatrixRendering.scala | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/funsuite.md b/docs/funsuite.md index a68316bb..7bb53084 100644 --- a/docs/funsuite.md +++ b/docs/funsuite.md @@ -23,11 +23,3 @@ object CatsFunSuite extends weaver.FunSuite { ```scala mdoc:passthrough println(weaver.docs.Output.runSuites(CatsFunSuite)) ``` - -A `FunSuite` alias is provided in each of the frameworks supported by weaver: - -```scala mdoc -object ZioBIOFunSuite extends weaver.ziocompat.FunSuite { - test("asserts") { expect(Some(5).contains(5)) } -} -``` diff --git a/docs/motivation.md b/docs/motivation.md index 705e9de5..745e14ab 100644 --- a/docs/motivation.md +++ b/docs/motivation.md @@ -5,7 +5,7 @@ title: Motivation ## A test framework for integration tests -Weaver was built for integration/end-to-end tests. It aims at making tests faster and make issues easier to debug, by treating effect types (IO/Task/ZIO) as first-class citizens. +Weaver was built for integration/end-to-end tests. It aims at making tests faster and make issues easier to debug, by treating effect types as first-class citizens. ## History diff --git a/modules/docs/src/main/scala/weaver/MatrixRendering.scala b/modules/docs/src/main/scala/weaver/MatrixRendering.scala index 64ab0cd1..d65e83b3 100644 --- a/modules/docs/src/main/scala/weaver/MatrixRendering.scala +++ b/modules/docs/src/main/scala/weaver/MatrixRendering.scala @@ -67,9 +67,7 @@ case class Table( object Table { def row_name(artif: String) = artif match { case "cats" => "Cats-Effect" - case "zio" => "ZIO" case "scalacheck" => "ScalaCheck" - case "specs2" => "Specs2 matchers" case "discipline" => "Discipline law testing" case _ => throw new RuntimeException("Not another effect type!") } From 0067a8c81be19a16eb862d26eb122fa1d9477c09 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Thu, 15 Sep 2022 14:33:49 +0100 Subject: [PATCH 30/33] Update fs2 to 3.3.0 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 106e316f..0701e557 100644 --- a/build.sbt +++ b/build.sbt @@ -38,7 +38,7 @@ sonatypeCredentialHost := "s01.oss.sonatype.org" val Version = new { object CE3 { - val fs2 = "3.2.14-75-7902cbf" + val fs2 = "3.3.0" val cats = "3.3.14" val zioInterop = "3.2.9.1" } From 91f5c47fd79d27ab6d4de24057011ee3ae4e4c70 Mon Sep 17 00:00:00 2001 From: Anton Sviridov Date: Thu, 15 Sep 2022 21:19:38 +0100 Subject: [PATCH 31/33] Use sbt-ci-release and setup-java GHA --- .github/workflows/ci.yml | 38 +++++++++++++++++--------------------- project/WeaverPlugin.scala | 16 +--------------- project/plugins.sbt | 4 +--- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f8f60fa..46309077 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - java: [adopt@1.8] + java: [8] scalaVersion: ["2_12", "2_13", "3"] scalaPlatform: ["jvm", "js", "native"] runs-on: ${{ matrix.os }} @@ -33,10 +33,11 @@ jobs: with: extraKey: ${{ env.BUILD_KEY }} - - name: Setup Java and Scala - uses: olafurpg/setup-scala@v10 + - uses: actions/setup-java@v3 with: + distribution: 'temurin' java-version: ${{ matrix.java }} + cache: 'sbt' - name: Run tests run: | @@ -71,15 +72,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout current branch - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - name: Cache - uses: coursier/cache-action@v6 - - - name: Setup Java and Scala - uses: olafurpg/setup-scala@v10 + - uses: actions/setup-java@v3 with: - java-version: adopt@1.8 + distribution: 'temurin' + java-version: '8' + cache: 'sbt' - name: Run mdoc run: sbt "docs/mdoc" @@ -87,21 +86,19 @@ jobs: publish: name: Publish needs: [documentation, build] - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || (github.ref == 'refs/heads/main')) runs-on: ubuntu-latest steps: - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Setup Java and Scala - uses: olafurpg/setup-scala@v10 + - uses: actions/setup-java@v3 with: - java-version: adopt@1.8 - - - name: Cache - uses: coursier/cache-action@v6 + distribution: 'temurin' + java-version: '8' + cache: 'sbt' - name: Download compilation cache uses: actions/download-artifact@v2 @@ -113,8 +110,7 @@ jobs: - name: Publish ${{ github.ref }} run: | - echo $PGP_SECRET | base64 --decode | gpg --import --no-tty --batch --yes - sbt 'pullRemoteCache; release' + sbt 'pullRemoteCache; ci-release' env: PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} PGP_SECRET: ${{ secrets.PGP_SECRET }} @@ -128,7 +124,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 lfs: true diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index 33aa42c5..763102f1 100644 --- a/project/WeaverPlugin.scala +++ b/project/WeaverPlugin.scala @@ -375,21 +375,7 @@ object WeaverPlugin extends AutoPlugin { email = "anton.sviridov@disneystreaming.com", url = url("https://github.com/keynmol") ) - ), - credentials ++= - sys.env - .get("SONATYPE_USER") - .zip(sys.env.get("SONATYPE_PASSWORD")) - .map { - case (username, password) => - Credentials( - "Sonatype Nexus Repository Manager", - "oss.sonatype.org", - username, - password - ) - } - .toSeq + ) ) def createBuildCommands(projects: Seq[ProjectReference]) = { diff --git a/project/plugins.sbt b/project/plugins.sbt index c696ed65..b66bf23b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,9 +3,7 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.1" addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1") addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.0") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.1.1") -addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.1.1") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.13") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.3") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") From 31a14c85e82f289b47e6862b1e0999997bc7c0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Fri, 16 Sep 2022 11:15:30 +0200 Subject: [PATCH 32/33] Update docs/other_effects.md Co-authored-by: Anton Sviridov --- docs/other_effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/other_effects.md b/docs/other_effects.md index 3af9bff0..94fa99f6 100644 --- a/docs/other_effects.md +++ b/docs/other_effects.md @@ -4,7 +4,7 @@ id: other_effects title: Other effects --- -Starting with version 0.8.0, Weaver does no longer offer out-of-the-box support for other effect types +Starting with version 0.8.0, Weaver no longer offers out-of-the-box support for other effect types than cats-effect. We (maintainers) are happy to keep the core of weaver effect-agnostic, in an effort to allow for third party From c57b4059856ff3ac9a94cbceb13d42085f9cce9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Fri, 16 Sep 2022 11:15:34 +0200 Subject: [PATCH 33/33] Update docs/other_effects.md Co-authored-by: Anton Sviridov --- docs/other_effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/other_effects.md b/docs/other_effects.md index 94fa99f6..ca2d42f3 100644 --- a/docs/other_effects.md +++ b/docs/other_effects.md @@ -12,7 +12,7 @@ to resurrect support for the effect types they use in repository they control. You can read about the rationale for decision [here](https://github.com/disneystreaming/weaver-test/discussions/570). Feel free to ping us via a github discussion, if you seek to resurrect support for a given effect-type. -If you are looking for documentation of them maintenance branch of weaver that did support other effect types, you can find it [over there](https://disneystreaming.github.io/weaver-test/docs/0.6.15/installation). +If you are looking for documentation of the maintenance branch of weaver that did support other effect types, you can find it [over there](https://disneystreaming.github.io/weaver-test/docs/0.6.15/installation). We will try to fix critical bugs on the 0.6/0.7 series, as they get found.