From 0da3f059280dea14c6c18fe8c8d61aba29571ffa Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Sun, 28 Jan 2024 15:12:21 +0100 Subject: [PATCH] Print `done` message from test framework to stdout We are returning the doneMessage from the test framework as value of the test task, but we are not printing it. Moreover, the output is shortcircuited when a task fails, so it's not passed to the `test` command. This PR uses `ctx.log.outputStream.println(doneMessage)` to print the message before returning it so users can clearly see it in case of both success and failure. This was discovered because of the way weaver works on Scala.js Since the `TestRunnerTests` are on `scalalib` and can't use Scala.js code, I implemented two dummy `sbt.testing.Framework`s which just return a fixed `done` message. --- build.sc | 1 + .../src/DoneMessageFailureFramework.scala | 42 +++++++++++++++++++ .../src/DoneMessageSuccessFramework.scala | 18 ++++++++ .../src/mill/scalalib/TestRunnerTests.scala | 38 ++++++++++++++++- .../src/mill/testrunner/TestRunnerUtils.scala | 2 + 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 scalalib/test/resources/testrunner/doneMessageFailure/src/DoneMessageFailureFramework.scala create mode 100644 scalalib/test/resources/testrunner/doneMessageSuccess/src/DoneMessageSuccessFramework.scala diff --git a/build.sc b/build.sc index 079c07f40c3..898b5561903 100644 --- a/build.sc +++ b/build.sc @@ -397,6 +397,7 @@ trait MillBaseTestsModule extends MillJavaModule with TestModule { s"-DTEST_SCALANATIVE_VERSION=${Deps.Scalanative_0_4.scalanativeVersion}", s"-DTEST_UTEST_VERSION=${Deps.utest.dep.version}", s"-DTEST_SCALATEST_VERSION=${Deps.TestDeps.scalaTest.dep.version}", + s"-DTEST_TEST_INTERFACE_VERSION=${Deps.sbtTestInterface.dep.version}", s"-DTEST_ZIOTEST_VERSION=${Deps.TestDeps.zioTest.dep.version}", s"-DTEST_ZINC_VERSION=${Deps.zinc.dep.version}" ) diff --git a/scalalib/test/resources/testrunner/doneMessageFailure/src/DoneMessageFailureFramework.scala b/scalalib/test/resources/testrunner/doneMessageFailure/src/DoneMessageFailureFramework.scala new file mode 100644 index 00000000000..d28504e70f9 --- /dev/null +++ b/scalalib/test/resources/testrunner/doneMessageFailure/src/DoneMessageFailureFramework.scala @@ -0,0 +1,42 @@ +package mill.scalalib + +import sbt.testing._ + +class DoneMessageFailureFramework extends Framework { + def fingerprints() = Array.empty + def name() = "DoneMessageFailureFramework" + def runner( + args: Array[String], + remoteArgs: Array[String], + testClassLoader: ClassLoader + ): Runner = new Runner { + def args() = Array.empty + def done() = "test failure done message" + def remoteArgs() = Array.empty + def tasks(taskDefs: Array[TaskDef]) = Array(new Task { + def taskDef(): TaskDef = null + def execute( + eventHandler: EventHandler, + loggers: Array[Logger] + ): Array[Task] = { + eventHandler.handle(new Event { + + override def fullyQualifiedName(): String = "foo.bar" + + override def fingerprint(): Fingerprint = new Fingerprint {} + + override def selector(): Selector = new TestSelector("foo.bar") + + override def status(): Status = Status.Failure + + override def throwable(): OptionalThrowable = new OptionalThrowable() + + override def duration(): Long = 0L + + }) + Array.empty + } + def tags = Array.empty + }) + } +} diff --git a/scalalib/test/resources/testrunner/doneMessageSuccess/src/DoneMessageSuccessFramework.scala b/scalalib/test/resources/testrunner/doneMessageSuccess/src/DoneMessageSuccessFramework.scala new file mode 100644 index 00000000000..575bdf6367e --- /dev/null +++ b/scalalib/test/resources/testrunner/doneMessageSuccess/src/DoneMessageSuccessFramework.scala @@ -0,0 +1,18 @@ +package mill.scalalib + +import sbt.testing._ + +class DoneMessageSuccessFramework extends Framework { + def fingerprints() = Array.empty + def name() = "DoneMessageSuccessFramework" + def runner( + args: Array[String], + remoteArgs: Array[String], + testClassLoader: ClassLoader + ): Runner = new Runner { + def args() = Array.empty + def done() = "test success done message" + def remoteArgs() = Array.empty + def tasks(taskDefs: Array[TaskDef]) = Array.empty + } +} diff --git a/scalalib/test/src/mill/scalalib/TestRunnerTests.scala b/scalalib/test/src/mill/scalalib/TestRunnerTests.scala index 16e9e0682eb..d5349d8f405 100644 --- a/scalalib/test/src/mill/scalalib/TestRunnerTests.scala +++ b/scalalib/test/src/mill/scalalib/TestRunnerTests.scala @@ -2,10 +2,13 @@ package mill.scalalib import mill.{Agg, T} +import mill.api.Result import mill.util.{TestEvaluator, TestUtil} import utest._ import utest.framework.TestPath +import java.io.{ByteArrayOutputStream, PrintStream} + object TestRunnerTests extends TestSuite { object testrunner extends TestUtil.BaseModule with ScalaModule { override def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.') @@ -28,6 +31,20 @@ object TestRunnerTests extends TestSuite { } } + trait DoneMessage extends ScalaTests { + override def ivyDeps = T { + super.ivyDeps() ++ Agg( + ivy"org.scala-sbt:test-interface:${sys.props.getOrElse("TEST_TEST_INTERFACE_VERSION", ???)}" + ) + } + } + object doneMessageSuccess extends DoneMessage { + def testFramework = "mill.scalalib.DoneMessageSuccessFramework" + } + object doneMessageFailure extends DoneMessage { + def testFramework = "mill.scalalib.DoneMessageFailureFramework" + } + object ziotest extends ScalaTests with TestModule.ZioTest { override def ivyDeps = T { super.ivyDeps() ++ Agg( @@ -42,11 +59,12 @@ object TestRunnerTests extends TestSuite { def workspaceTest[T]( m: TestUtil.BaseModule, + outStream: PrintStream = System.out, resourcePath: os.Path = resourcePath )(t: TestEvaluator => T)( implicit tp: TestPath ): T = { - val eval = new TestEvaluator(m) + val eval = new TestEvaluator(m, outStream = outStream) os.remove.all(m.millSourcePath) os.remove.all(eval.outPath) os.makeDir.all(m.millSourcePath / os.up) @@ -88,6 +106,24 @@ object TestRunnerTests extends TestSuite { } } + "doneMessage" - { + test("failure") { + val outStream = new ByteArrayOutputStream() + workspaceTest(testrunner, outStream = new PrintStream(outStream, true)) { eval => + val Left(Result.Failure(msg, _)) = eval(testrunner.doneMessageFailure.test()) + val stdout = new String(outStream.toByteArray) + assert(stdout.contains("test failure done message")) + } + } + test("success") { + val outStream = new ByteArrayOutputStream() + workspaceTest(testrunner, outStream = new PrintStream(outStream, true)) { eval => + val Right(_) = eval(testrunner.doneMessageSuccess.test()) + val stdout = new String(outStream.toByteArray) + assert(stdout.contains("test success done message")) + } + } + } "ScalaTest" - { test("scalatest.test") { workspaceTest(testrunner) { eval => diff --git a/testrunner/src/mill/testrunner/TestRunnerUtils.scala b/testrunner/src/mill/testrunner/TestRunnerUtils.scala index 10f32e466de..7f2712c7984 100644 --- a/testrunner/src/mill/testrunner/TestRunnerUtils.scala +++ b/testrunner/src/mill/testrunner/TestRunnerUtils.scala @@ -156,6 +156,8 @@ import scala.jdk.CollectionConverters.IteratorHasAsScala runner.done() } + ctx.log.outputStream.println(doneMessage) + val results = for (e <- events.iterator().asScala) yield { val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None