diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fabb3cf3..0f8f60fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,10 @@ on: branches: ["master", "series/*"] 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}} 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 b270e9b9..0701e557 100644 --- a/build.sbt +++ b/build.sbt @@ -38,12 +38,12 @@ sonatypeCredentialHost := "s01.oss.sonatype.org" val Version = new { object CE3 { - val fs2 = "3.2.12" + val fs2 = "3.3.0" val cats = "3.3.14" 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" @@ -204,10 +204,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) 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 new file mode 100644 index 00000000..af8c30f1 --- /dev/null +++ b/modules/core/cats/src-native/weaver/CatsUnsafeRunPlatformCompat.scala @@ -0,0 +1,20 @@ +package weaver + +import scala.concurrent.Await +import scala.concurrent.duration._ + +import cats.effect.IO +import cats.effect.unsafe.implicits.global + +private[weaver] trait CatsUnsafeRunPlatformCompat { + self: CatsUnsafeRun => + + def unsafeRunSync(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/cats/src/weaver/CatsUnsafeRun.scala b/modules/core/cats/src/weaver/CatsUnsafeRun.scala index cb7230c4..72b67a3d 100644 --- a/modules/core/cats/src/weaver/CatsUnsafeRun.scala +++ b/modules/core/cats/src/weaver/CatsUnsafeRun.scala @@ -1,5 +1,7 @@ package weaver +import scala.concurrent.Future + import cats.effect.unsafe.implicits.global import cats.effect.{ FiberIO, IO } @@ -12,8 +14,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-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..f80174b8 --- /dev/null +++ b/modules/core/src-native/weaver/internals/Timestamp.scala @@ -0,0 +1,34 @@ +package weaver.internals + +import scala.scalanative.posix +import scala.scalanative.unsafe._ + +import posix.time +import posix.timeOps._ + +private[weaver] object Timestamp { + + 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 + val minutes = gmTime.tm_min + val seconds = gmTime.tm_sec + s"$hour:$minutes:$seconds" + } + + 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() + } +} 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/modules/core/src/weaver/UnsafeRun.scala b/modules/core/src/weaver/UnsafeRun.scala index 4b119bee..56767644 100644 --- a/modules/core/src/weaver/UnsafeRun.scala +++ b/modules/core/src/weaver/UnsafeRun.scala @@ -1,5 +1,6 @@ package weaver +import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration import cats.Parallel @@ -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 3f933c9e..323ec1ef 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] { @@ -164,4 +164,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..0abc4866 100644 --- a/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala +++ b/modules/core/zio/src/weaver/ziocompat/ZIOUnsafeRun.scala @@ -1,6 +1,8 @@ package weaver package ziocompat +import scala.concurrent.Future + import cats.Parallel import cats.effect.Async @@ -23,7 +25,11 @@ 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 unsafeRunAndForget(task: T[Unit]): Unit = + runtime.unsafeRunAsync(task)(_ => ()) - def async(task: T[Unit]): Unit = runtime.unsafeRunAsync(task)(_ => ()) + def unsafeRunToFuture(task: T[Unit]): Future[Unit] = + runtime.unsafeRunToFuture(task) } 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-js-native/AsyncTask.scala b/modules/framework/src-js-native/AsyncTask.scala new file mode 100644 index 00000000..0fe83915 --- /dev/null +++ b/modules/framework/src-js-native/AsyncTask.scala @@ -0,0 +1,11 @@ +package weaver + +import scala.concurrent.Future + +import sbt.testing.{ EventHandler, Logger, Task } + +private[weaver] trait AsyncTask extends Task { + def executeFuture( + eventHandler: EventHandler, + loggers: Array[Logger]): Future[Unit] +} diff --git a/modules/framework/src-js/DogFoodCompat.scala b/modules/framework/src-js-native/DogFoodCompat.scala similarity index 86% rename from modules/framework/src-js/DogFoodCompat.scala rename to modules/framework/src-js-native/DogFoodCompat.scala index f9bbf85b..dc6b7d97 100644 --- a/modules/framework/src-js/DogFoodCompat.scala +++ b/modules/framework/src-js-native/DogFoodCompat.scala @@ -17,9 +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)) => - task.execute(eventHandler, Array(logger), _ => cb(Right(()))) + self.framework.unsafeRun.fromFuture { + task.asInstanceOf[AsyncTask].executeFuture(eventHandler, Array(logger)) } }.map { _ => Reporter.logRunFinished(Array(logger))( diff --git a/modules/framework/src-js/RunnerCompat.scala b/modules/framework/src-js-native/RunnerCompat.scala similarity index 63% rename from modules/framework/src-js/RunnerCompat.scala rename to modules/framework/src-js-native/RunnerCompat.scala index 7e43dddc..a37921d6 100644 --- a/modules/framework/src-js/RunnerCompat.scala +++ b/modules/framework/src-js-native/RunnerCompat.scala @@ -1,10 +1,11 @@ package weaver package framework +import java.nio.ByteBuffer + import scala.collection.mutable.ListBuffer +import scala.concurrent.Future import scala.concurrent.duration._ -import scala.scalajs.js -import scala.scalajs.js.JSON import cats.data.Chain import cats.effect.kernel.Async @@ -23,15 +24,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 = ReadWriter.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 +72,17 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } override def receiveMessage(msg: String): Option[String] = { - val outcome = JSON.parse(msg).asInstanceOf[TestOutcomeJS] + val outcome = ReadWriter.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 } @@ -80,18 +98,12 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } private case class SbtTask(td: TaskDef, loader: Option[suiteLoader.SuiteRef]) - extends Task { + extends PlatformTask { override def tags(): Array[String] = Array() - override def execute( + def executeFuture( eventHandler: EventHandler, - loggers: Array[Logger]): Array[Task] = Array() - - override def execute( - eventHandler: EventHandler, - loggers: Array[Logger], - continuation: Array[Task] => Unit): Unit = { - + loggers: Array[Logger]): Future[Unit] = { val fqn = taskDef().fullyQualifiedName() def reportTest(outcome: TestOutcome) = @@ -116,9 +128,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,31 +146,28 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => 0.seconds, Result.from(error), Chain.empty) - reportTest(outcome) - .productR(reportDoneF(TestOutcomeJS.from(fqn)(outcome))) + reportTest(outcome).productR( + reportDoneF(TestOutcomeNative.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) - ) + loadAndRun = loader.suite.flatMap(runSuite(fqn, _, outcomes)) + _ <- Async[F].background(loadAndRun).use { + _.flatMap { + _.fold( + canceled = effect.unit, + completed = _ *> finaliseCompleted(outcomes), + errored = finaliseError(outcomes) ) + } + } } yield () } - unsafeRun.async(action.attempt.map { exc => - continuation(Array()) - }) + unsafeRun.unsafeRunToFuture(action) } override def taskDef(): TaskDef = td @@ -167,34 +176,61 @@ trait RunnerCompat[F[_]] { self: sbt.testing.Runner => } -class TestOutcomeJS( - val suiteName: String, - val testName: String, - val durationMs: Double, - val verboseFormatting: String -) extends js.Object {} +private[weaver] object ReadWriter { + class Reader(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 Writer(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: Reader => A) = { + val buf = ByteBuffer.wrap(s.getBytes) + f(new Reader(buf, 0)) + } + + def writer(f: Writer => Unit): String = { + val buf = ByteBuffer.allocate(2048) + + f(new Writer(buf)) + + new String(buf.array()) + + } +} + +case class TestOutcomeNative( + suiteName: String, + testName: String, + durationMs: Double, + verboseFormatting: String +) -object TestOutcomeJS { - def from(suiteName: String)(outcome: TestOutcome): TestOutcomeJS = { - TestOutcomeJS( +object TestOutcomeNative { + def from(suiteName: String)(outcome: TestOutcome): TestOutcomeNative = { + new TestOutcomeNative( 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) = { + def rehydrate(t: TestOutcomeNative): (SuiteName, TestOutcome) = { SuiteName(t.suiteName) -> DecodedOutcome( t.testName, t.durationMs.millis, 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/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-native/PlatformTask.scala b/modules/framework/src-native/PlatformTask.scala new file mode 100644 index 00000000..cddf4970 --- /dev/null +++ b/modules/framework/src-native/PlatformTask.scala @@ -0,0 +1,18 @@ +package weaver + +import scala.concurrent.Await +import scala.concurrent.duration._ + +import sbt.testing.{ EventHandler, Logger, Task } + +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/weaver/framework/DogFood.scala b/modules/framework/src/weaver/framework/DogFood.scala index 030e08c7..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 => 2.seconds.some - case JVM => none + case JS => 2.seconds.some + case JVM | Native => none } def runSuites( diff --git a/modules/framework/src/weaver/framework/Fingerprints.scala b/modules/framework/src/weaver/framework/Fingerprints.scala index c78024cd..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 { - 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 diff --git a/project/WeaverPlugin.scala b/project/WeaverPlugin.scala index 04a15b0e..33aa42c5 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), @@ -67,21 +72,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 _) @@ -135,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 @@ -298,8 +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.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") @@ -381,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 { @@ -400,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 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 diff --git a/project/plugins.sbt b/project/plugins.sbt index 2e9e91b8..c696ed65 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")