diff --git a/munit/js-native/src/main/scala/munit/internal/junitinterface/JUnitReporter.scala b/munit/js-native/src/main/scala/munit/internal/junitinterface/JUnitReporter.scala index 4669faa2..fcedafe9 100644 --- a/munit/js-native/src/main/scala/munit/internal/junitinterface/JUnitReporter.scala +++ b/munit/js-native/src/main/scala/munit/internal/junitinterface/JUnitReporter.scala @@ -80,7 +80,6 @@ final class JUnitReporter( .append(ex.getMessage()) .toString() ) - trace(ex) emitEvent(method, Status.Failure, new OptionalThrowable(ex)) } @@ -268,7 +267,7 @@ final class JUnitReporter( .toString() } private def formatTime(elapsedMillis: Double): String = - AnsiColors.c("%.2fs".format(elapsedMillis / 1000.0), AnsiColors.DarkGrey) + AnsiColors.c("%.3fs".format(elapsedMillis / 1000.0), AnsiColors.DarkGrey) private val Trace = 0 private val Debug = 1 private val Info = 2 diff --git a/munit/native/src/main/scala/munit/internal/PlatformCompat.scala b/munit/native/src/main/scala/munit/internal/PlatformCompat.scala index 9ddbd8e3..7bf177db 100644 --- a/munit/native/src/main/scala/munit/internal/PlatformCompat.scala +++ b/munit/native/src/main/scala/munit/internal/PlatformCompat.scala @@ -9,12 +9,40 @@ import sbt.testing.EventHandler import sbt.testing.Logger import scala.concurrent.Await import scala.concurrent.Awaitable +import scala.concurrent.Promise import scala.concurrent.duration.Duration import scala.concurrent.ExecutionContext +import java.util.concurrent.{ + Executors, + ThreadFactory, + TimeoutException, + TimeUnit +} +import java.util.concurrent.atomic.AtomicInteger +import scala.scalanative.meta.LinktimeInfo.isMultithreadingEnabled +// Delay reachability of multithreading capability +// Would not force multithreading support unless explicitly configured by the user +// or if using threads in the tests +private object LazyMultithreadingSupport { + val sh = Executors.newSingleThreadScheduledExecutor( + new ThreadFactory { + val counter = new AtomicInteger + def threadNumber() = counter.incrementAndGet() + def newThread(r: Runnable) = + new Thread(r, s"munit-scheduler-${threadNumber()}") { + setDaemon(true) + setPriority(Thread.NORM_PRIORITY) + } + } + ) +} object PlatformCompat { + import LazyMultithreadingSupport._ + def awaitResult[T](awaitable: Awaitable[T]): T = { - scalanative.runtime.loop() + if (!isMultithreadingEnabled) + Thread.`yield`() // invokes SN 0.4 scalanative.runtime.loop() Await.result(awaitable, Duration.Inf) } @@ -26,18 +54,55 @@ object PlatformCompat { task.execute(eventHandler, loggers) Future.successful(()) } + def waitAtMost[T]( startFuture: () => Future[T], duration: Duration, ec: ExecutionContext ): Future[T] = { - startFuture() + if (!isMultithreadingEnabled) startFuture() + else { + val onComplete = Promise[T]() + val timeout = + if (duration.isFinite) + Some( + sh.schedule[Unit]( + () => + onComplete.tryFailure( + new TimeoutException(s"test timed out after $duration") + ), + duration.toMillis, + TimeUnit.MILLISECONDS + ) + ) + else + None + ec.execute(new Runnable { + def run(): Unit = { + startFuture().onComplete { result => + onComplete.tryComplete(result) + timeout.foreach(_.cancel(false)) + }(ec) + } + }) + onComplete.future + } } + def setTimeout(ms: Int)(body: => Unit): () => Unit = { - Thread.sleep(ms) - body + if (!isMultithreadingEnabled) { + // Thread.sleep(ms) + body + () => () + } else { + val scheduled = sh.schedule[Unit]( + () => body, + ms, + TimeUnit.MILLISECONDS + ) - () => () + () => scheduled.cancel(false) + } } // Scala Native does not support looking up annotations at runtime. diff --git a/tests/shared/src/main/scala/munit/BaseFrameworkSuite.scala b/tests/shared/src/main/scala/munit/BaseFrameworkSuite.scala index f54e3224..76ac72e2 100644 --- a/tests/shared/src/main/scala/munit/BaseFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/BaseFrameworkSuite.scala @@ -82,7 +82,7 @@ abstract class BaseFrameworkSuite extends BaseSuite { } } implicit val ec = munitExecutionContext - val elapsedTimePattern = Pattern.compile(" \\d+\\.\\d+s ?") + val elapsedTimePattern = Pattern.compile(" ? \\d+\\.\\d+s ?") TestingConsole.out = out TestingConsole.err = out for { diff --git a/tests/shared/src/main/scala/munit/Issue285FrameworkSuite.scala b/tests/shared/src/main/scala/munit/Issue285FrameworkSuite.scala index d45334e9..bdb30490 100644 --- a/tests/shared/src/main/scala/munit/Issue285FrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/Issue285FrameworkSuite.scala @@ -48,18 +48,11 @@ object Issue285FrameworkSuite | + issue-285-ok |beforeEach - issue-285-fail |afterEach - issue-285-fail - |==> X munit.Issue285FrameworkSuite.issue-285-fail java.util.concurrent.TimeoutException: test timed out after 5 milliseconds + |==> X munit.Issue285FrameworkSuite.issue-285-fail java.util.concurrent.TimeoutException: test timed out after 5 milliseconds |beforeEach - issue-285-ok |afterEach - issue-285-ok | + issue-285-ok-1 |afterAll |""".stripMargin, - tags = Set( - // Skipped on JS/Native because we don't support - // `PlatformCompat.setTimeout` on Native and the test has stack traces - // on JS which fails the assertion (even if the behavior works as - // expected) - OnlyJVM - ), format = StdoutFormat ) diff --git a/tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala b/tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala index 9c336b3d..b98bfd57 100644 --- a/tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala +++ b/tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala @@ -157,13 +157,13 @@ object SkippedFrameworkStdoutJVMSuite |==> i munit.SkippedFrameworkSuite.pending.empty.ignored.comment PENDING comment ignored |==> i munit.SkippedFrameworkSuite.pending.successful.ignored PENDING ignored |==> i munit.SkippedFrameworkSuite.pending.successful.ignored.comment PENDING comment ignored - |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:38 assertion failed + |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:38 assertion failed |37: test("pending.failed.not-ignored".pending) { |38: assert(false) |39: } | at munit.FunSuite.assert(FunSuite.scala:11) | at munit.SkippedFrameworkSuite.$anonfun$new$21(SkippedFrameworkSuite.scala:38) - |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored.comment munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:41 assertion failed + |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored.comment munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:41 assertion failed |40: test("pending.failed.not-ignored.comment".pending("comment")) { |41: assert(false) |42: } @@ -199,14 +199,14 @@ object SkippedFrameworkStdoutJVMVerboseSuite |munit.SkippedFrameworkSuite.pending.successful.ignored.comment started |==> i munit.SkippedFrameworkSuite.pending.successful.ignored.comment PENDING comment ignored |munit.SkippedFrameworkSuite.pending.failed.not-ignored started - |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:38 assertion failed + |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:38 assertion failed |37: test("pending.failed.not-ignored".pending) { |38: assert(false) |39: } | at munit.FunSuite.assert(FunSuite.scala:11) | at munit.SkippedFrameworkSuite.$anonfun$new$21(SkippedFrameworkSuite.scala:38) |munit.SkippedFrameworkSuite.pending.failed.not-ignored.comment started - |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored.comment munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:41 assertion failed + |==> X munit.SkippedFrameworkSuite.pending.failed.not-ignored.comment munit.FailException: tests/shared/src/main/scala/munit/SkippedFrameworkSuite.scala:41 assertion failed |40: test("pending.failed.not-ignored.comment".pending("comment")) { |41: assert(false) |42: }