summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2018-04-08 12:55:30 -0700
committerLi Haoyi <haoyi.sg@gmail.com>2018-04-08 12:55:30 -0700
commit62a18754179d74252668879c42e1c5d6a45fbdce (patch)
tree99a820224293a2367c116272283fd2a3426957ae
parentdbcad35c05f1726f26b8033524e8fdd3d68b2de9 (diff)
downloadmill-62a18754179d74252668879c42e1c5d6a45fbdce.tar.gz
mill-62a18754179d74252668879c42e1c5d6a45fbdce.tar.bz2
mill-62a18754179d74252668879c42e1c5d6a45fbdce.zip
Enable JUnit testing, via sbt-test-interface, for `JavaModule`s
-rw-r--r--integration/test/resources/upickle/build.sc2
-rw-r--r--scalajslib/src/mill/scalajslib/ScalaJSModule.scala4
-rw-r--r--scalalib/src/mill/scalalib/JavaModule.scala71
-rw-r--r--scalalib/src/mill/scalalib/Lib.scala117
-rw-r--r--scalalib/src/mill/scalalib/ScalaModule.scala63
-rw-r--r--scalalib/src/mill/scalalib/ScalaWorkerApi.scala6
-rw-r--r--scalalib/test/resources/hello-java/app/src/hello/Main.java10
-rw-r--r--scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java18
-rw-r--r--scalalib/test/resources/hello-java/core/src/hello/Core.java1
-rw-r--r--scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java15
-rw-r--r--scalalib/test/resources/hello-java/main/src/hello/Main.java7
-rw-r--r--scalalib/test/src/mill/scalalib/HelloJavaTests.scala43
-rw-r--r--scalaworker/src/mill/scalaworker/ScalaWorker.scala123
13 files changed, 271 insertions, 209 deletions
diff --git a/integration/test/resources/upickle/build.sc b/integration/test/resources/upickle/build.sc
index e1383771..2ea7668d 100644
--- a/integration/test/resources/upickle/build.sc
+++ b/integration/test/resources/upickle/build.sc
@@ -90,7 +90,7 @@ trait UpickleModule extends CrossSbtModule with PublishModule{
def platformSegment: String
}
-trait UpickleTestModule extends TestModule{
+trait UpickleTestModule extends TestModule with ScalaModule{
def platformSegment: String
def ivyDeps = Agg(
diff --git a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
index dbf693ff..1596cdb6 100644
--- a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
+++ b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
@@ -190,9 +190,7 @@ trait TestScalaJSModule extends ScalaJSModule with TestModule {
fastOptTest().path.toIO
)
- val (doneMsg, results) = scalaWorker
- .worker()
- .runTests(
+ val (doneMsg, results) = Lib.runTests(
_ => Seq(framework),
runClasspath().map(_.path),
Agg(compile().classes.path),
diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala
index b8ae0fd4..ea56b22e 100644
--- a/scalalib/src/mill/scalalib/JavaModule.scala
+++ b/scalalib/src/mill/scalalib/JavaModule.scala
@@ -17,7 +17,11 @@ import mill.util.Loose.Agg
* Core configuration required to compile a single Scala compilation target
*/
trait JavaModule extends mill.Module with TaskModule { outer =>
-
+ trait Tests extends TestModule{
+ override def moduleDeps = Seq(outer)
+ override def repositories = outer.repositories
+ override def javacOptions = outer.javacOptions
+ }
def defaultCommandName() = "run"
def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{
@@ -281,4 +285,67 @@ trait JavaModule extends mill.Module with TaskModule { outer =>
def artifactName: T[String] = millModuleSegments.parts.mkString("-")
def artifactId: T[String] = artifactName()
-} \ No newline at end of file
+}
+
+trait TestModule extends JavaModule with TaskModule {
+ override def defaultCommandName() = "test"
+ def testFrameworks: T[Seq[String]]
+
+ def forkWorkingDir = ammonite.ops.pwd
+
+ def test(args: String*) = T.command{
+ val outputPath = T.ctx().dest/"out.json"
+
+ Jvm.subprocess(
+ mainClass = "mill.scalaworker.ScalaWorker",
+ classPath = ScalaWorkerModule.classpath(),
+ jvmArgs = forkArgs(),
+ envArgs = forkEnv(),
+ mainArgs =
+ Seq(testFrameworks().length.toString) ++
+ testFrameworks() ++
+ Seq(runClasspath().length.toString) ++
+ runClasspath().map(_.path.toString) ++
+ Seq(args.length.toString) ++
+ args ++
+ Seq(outputPath.toString, T.ctx().log.colored.toString, compile().classes.path.toString, T.ctx().home.toString),
+ workingDir = forkWorkingDir
+ )
+
+ val jsonOutput = ujson.read(outputPath.toIO)
+ val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput)
+ TestModule.handleResults(doneMsg, results)
+
+ }
+ def testLocal(args: String*) = T.command{
+ val outputPath = T.ctx().dest/"out.json"
+
+ Lib.runTests(
+ TestRunner.frameworks(testFrameworks()),
+ runClasspath().map(_.path),
+ Agg(compile().classes.path),
+ args
+ )
+
+ val jsonOutput = ujson.read(outputPath.toIO)
+ val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput)
+ TestModule.handleResults(doneMsg, results)
+
+ }
+}
+
+object TestModule{
+ def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = {
+
+ val badTests = results.filter(x => Set("Error", "Failure").contains(x.status))
+ if (badTests.isEmpty) Result.Success((doneMsg, results))
+ else {
+ val suffix = if (badTests.length == 1) "" else " and " + (badTests.length-1) + " more"
+
+ Result.Failure(
+ badTests.head.fullyQualifiedName + " " + badTests.head.selector + suffix,
+ Some((doneMsg, results))
+ )
+ }
+ }
+}
diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala
index 7b4b5bdb..3eb2defd 100644
--- a/scalalib/src/mill/scalalib/Lib.scala
+++ b/scalalib/src/mill/scalalib/Lib.scala
@@ -1,14 +1,22 @@
package mill
package scalalib
-import java.io.File
+import java.io.{File, FileInputStream}
+import java.lang.annotation.Annotation
+import java.util.zip.ZipInputStream
import javax.tools.ToolProvider
import ammonite.ops._
import ammonite.util.Util
import coursier.{Cache, Dependency, Fetch, Repository, Resolution}
+import mill.Agg
import mill.eval.{PathRef, Result}
-import mill.util.Loose.Agg
+import mill.modules.Jvm
+
+import mill.util.Ctx
+import sbt.testing._
+
+import scala.collection.mutable
object CompilationResult {
implicit val jsonFormatter: upickle.default.ReadWriter[CompilationResult] = upickle.default.macroRW
@@ -194,4 +202,109 @@ object Lib{
force = false
)
+ def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework],
+ entireClasspath: Agg[Path],
+ testClassfilePath: Agg[Path],
+ args: Seq[String])
+ (implicit ctx: Ctx.Log with Ctx.Home): (String, Seq[mill.scalalib.TestRunner.Result]) = {
+ Jvm.inprocess(entireClasspath, classLoaderOverrideSbtTesting = true, cl => {
+ val frameworks = frameworkInstances(cl)
+
+ val events = mutable.Buffer.empty[Event]
+
+ val doneMessages = frameworks.map { framework =>
+ val runner = framework.runner(args.toArray, args.toArray, cl)
+
+ val testClasses = discoverTests(cl, framework, testClassfilePath)
+
+ val tasks = runner.tasks(
+ for ((cls, fingerprint) <- testClasses.toArray)
+ yield new TaskDef(cls.getName.stripSuffix("$"), fingerprint, true, Array(new SuiteSelector))
+ )
+
+ for (t <- tasks) {
+ t.execute(
+ new EventHandler {
+ def handle(event: Event) = events.append(event)
+ },
+ Array(
+ new Logger {
+ def debug(msg: String) = ctx.log.outputStream.println(msg)
+
+ def error(msg: String) = ctx.log.outputStream.println(msg)
+
+ def ansiCodesSupported() = true
+
+ def warn(msg: String) = ctx.log.outputStream.println(msg)
+
+ def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream)
+
+ def info(msg: String) = ctx.log.outputStream.println(msg)
+ })
+ )
+ }
+ ctx.log.outputStream.println(runner.done())
+ }
+
+ val results = for(e <- events) yield {
+ val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None
+ mill.scalalib.TestRunner.Result(
+ e.fullyQualifiedName(),
+ e.selector() match{
+ case s: NestedSuiteSelector => s.suiteId()
+ case s: NestedTestSelector => s.suiteId() + "." + s.testName()
+ case s: SuiteSelector => s.toString
+ case s: TestSelector => s.testName()
+ case s: TestWildcardSelector => s.testWildcard()
+ },
+ e.duration(),
+ e.status().toString,
+ ex.map(_.getClass.getName),
+ ex.map(_.getMessage),
+ ex.map(_.getStackTrace)
+ )
+ }
+
+ (doneMessages.mkString("\n"), results)
+ })
+ }
+
+ def listClassFiles(base: Path): Iterator[String] = {
+ if (base.isDir) ls.rec(base).toIterator.filter(_.ext == "class").map(_.relativeTo(base).toString)
+ else {
+ val zip = new ZipInputStream(new FileInputStream(base.toIO))
+ Iterator.continually(zip.getNextEntry).takeWhile(_ != null).map(_.getName).filter(_.endsWith(".class"))
+ }
+ }
+
+ def discoverTests(cl: ClassLoader, framework: Framework, classpath: Agg[Path]) = {
+
+ val fingerprints = framework.fingerprints()
+
+ val testClasses = classpath.flatMap { base =>
+ // Don't blow up if there are no classfiles representing
+ // the tests to run Instead just don't run anything
+ if (!exists(base)) Nil
+ else listClassFiles(base).flatMap { path =>
+ val cls = cl.loadClass(path.stripSuffix(".class").replace('/', '.'))
+ fingerprints.find {
+ case f: SubclassFingerprint =>
+ !cls.isInterface &&
+ (f.isModule == cls.getName.endsWith("$")) &&
+ cl.loadClass(f.superclassName()).isAssignableFrom(cls)
+ case f: AnnotatedFingerprint =>
+ val annotationCls = cl.loadClass(f.annotationName()).asInstanceOf[Class[Annotation]]
+ (f.isModule == cls.getName.endsWith("$")) &&
+ (
+ cls.isAnnotationPresent(annotationCls) ||
+ cls.getDeclaredMethods.exists(_.isAnnotationPresent(annotationCls))
+ )
+
+ }.map { f => (cls, f) }
+ }
+ }
+
+ testClasses
+ }
+
}
diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala
index b98f248e..a2ca09c5 100644
--- a/scalalib/src/mill/scalalib/ScalaModule.scala
+++ b/scalalib/src/mill/scalalib/ScalaModule.scala
@@ -18,7 +18,7 @@ import mill.util.DummyInputStream
trait ScalaModule extends JavaModule { outer =>
def scalaWorker: ScalaWorkerModule = mill.scalalib.ScalaWorkerModule
- trait Tests extends TestModule{
+ trait Tests extends TestModule with ScalaModule{
def scalaVersion = outer.scalaVersion()
override def repositories = outer.repositories
override def scalacPluginIvyDeps = outer.scalacPluginIvyDeps
@@ -195,64 +195,3 @@ trait ScalaModule extends JavaModule { outer =>
}
-object TestModule{
- def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = {
-
- val badTests = results.filter(x => Set("Error", "Failure").contains(x.status))
- if (badTests.isEmpty) Result.Success((doneMsg, results))
- else {
- val suffix = if (badTests.length == 1) "" else " and " + (badTests.length-1) + " more"
-
- Result.Failure(
- badTests.head.fullyQualifiedName + " " + badTests.head.selector + suffix,
- Some((doneMsg, results))
- )
- }
- }
-}
-trait TestModule extends ScalaModule with TaskModule {
- override def defaultCommandName() = "test"
- def testFrameworks: T[Seq[String]]
-
- def forkWorkingDir = ammonite.ops.pwd
-
- def test(args: String*) = T.command{
- val outputPath = T.ctx().dest/"out.json"
-
- Jvm.subprocess(
- mainClass = "mill.scalaworker.ScalaWorker",
- classPath = scalaWorker.classpath(),
- jvmArgs = forkArgs(),
- envArgs = forkEnv(),
- mainArgs =
- Seq(testFrameworks().length.toString) ++
- testFrameworks() ++
- Seq(runClasspath().length.toString) ++
- runClasspath().map(_.path.toString) ++
- Seq(args.length.toString) ++
- args ++
- Seq(outputPath.toString, T.ctx().log.colored.toString, compile().classes.path.toString, T.ctx().home.toString),
- workingDir = forkWorkingDir
- )
-
- val jsonOutput = ujson.read(outputPath.toIO)
- val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput)
- TestModule.handleResults(doneMsg, results)
-
- }
- def testLocal(args: String*) = T.command{
- val outputPath = T.ctx().dest/"out.json"
-
- scalaWorker.worker().runTests(
- TestRunner.frameworks(testFrameworks()),
- runClasspath().map(_.path),
- Agg(compile().classes.path),
- args
- )
-
- val jsonOutput = ujson.read(outputPath.toIO)
- val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput)
- TestModule.handleResults(doneMsg, results)
-
- }
-}
diff --git a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala
index f6500ae8..0b6a931b 100644
--- a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala
+++ b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala
@@ -5,7 +5,6 @@ import ammonite.ops.Path
import coursier.Cache
import coursier.maven.MavenRepository
import mill.Agg
-import mill.scalalib.TestRunner.Result
import mill.T
import mill.define.{Discover, Worker}
import mill.scalalib.Lib.resolveDependencies
@@ -68,11 +67,6 @@ trait ScalaWorkerApi {
upstreamCompileOutput: Seq[CompilationResult])
(implicit ctx: mill.util.Ctx): mill.eval.Result[CompilationResult]
- def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework],
- entireClasspath: Agg[Path],
- testClassfilePath: Agg[Path],
- args: Seq[String])
- (implicit ctx: mill.util.Ctx.Log with mill.util.Ctx.Home): (String, Seq[Result])
def discoverMainClasses(compilationResult: CompilationResult)
(implicit ctx: mill.util.Ctx): Seq[String]
diff --git a/scalalib/test/resources/hello-java/app/src/hello/Main.java b/scalalib/test/resources/hello-java/app/src/hello/Main.java
new file mode 100644
index 00000000..23ddd679
--- /dev/null
+++ b/scalalib/test/resources/hello-java/app/src/hello/Main.java
@@ -0,0 +1,10 @@
+package hello;
+
+public class Main{
+ public static String getMessage(String[] args){
+ return Core.msg() + " " + args[0];
+ }
+ public static void main(String[] args){
+ System.out.println(getMessage(args));
+ }
+} \ No newline at end of file
diff --git a/scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java b/scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java
new file mode 100644
index 00000000..df0d0351
--- /dev/null
+++ b/scalalib/test/resources/hello-java/app/test/src/hello/MyAppTests.java
@@ -0,0 +1,18 @@
+package hello;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class MyAppTests {
+
+ @Test
+ public void coreTest() {
+ assertEquals(Core.msg(), "Hello World");
+ }
+
+ @Test
+ public void appTest() {
+ assertEquals(Main.getMessage(new String[]{"lols"}), "Hello World lols");
+ }
+
+} \ No newline at end of file
diff --git a/scalalib/test/resources/hello-java/core/src/hello/Core.java b/scalalib/test/resources/hello-java/core/src/hello/Core.java
index bef1a5a0..3ecb1f61 100644
--- a/scalalib/test/resources/hello-java/core/src/hello/Core.java
+++ b/scalalib/test/resources/hello-java/core/src/hello/Core.java
@@ -4,5 +4,4 @@ public class Core{
public static String msg(){
return "Hello World";
}
-
} \ No newline at end of file
diff --git a/scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java b/scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java
new file mode 100644
index 00000000..38bebaeb
--- /dev/null
+++ b/scalalib/test/resources/hello-java/core/test/src/hello/MyCoreTests.java
@@ -0,0 +1,15 @@
+package hello;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class MyCoreTests {
+ @Test
+ public void msgTest() {
+ assertEquals(Core.msg(), "Hello World!!");
+ }
+ @Test
+ public void lengthTest() {
+ assertEquals(Core.msg().length(), 11);
+ }
+} \ No newline at end of file
diff --git a/scalalib/test/resources/hello-java/main/src/hello/Main.java b/scalalib/test/resources/hello-java/main/src/hello/Main.java
deleted file mode 100644
index 44b927bd..00000000
--- a/scalalib/test/resources/hello-java/main/src/hello/Main.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package hello;
-
-public class Main{
- public static void main(String[] args){
- System.out.println(Core.msg() + " " + args[0]);
- }
-} \ No newline at end of file
diff --git a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala
index 7794d5a5..bcfdd19f 100644
--- a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala
+++ b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala
@@ -1,8 +1,10 @@
-package mill.scalalib
+package mill
+package scalalib
import ammonite.ops.{%, %%, cp, ls, mkdir, pwd, rm, up}
import ammonite.ops.ImplicitWd._
+import mill.eval.Result
import mill.util.{TestEvaluator, TestUtil}
import utest._
import utest.framework.TestPath
@@ -12,9 +14,17 @@ object HelloJavaTests extends TestSuite {
object HelloJava extends TestUtil.BaseModule{
def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.')
- object core extends JavaModule
- object main extends JavaModule{
+ trait JUnitTests extends TestModule{
+ def testFrameworks = Seq("com.novocode.junit.JUnitFramework")
+ def ivyDeps = Agg(ivy"com.novocode:junit-interface:0.11")
+ }
+
+ object core extends JavaModule{
+ object test extends Tests with JUnitTests
+ }
+ object app extends JavaModule{
def moduleDeps = Seq(core)
+ object test extends Tests with JUnitTests
}
}
val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-java"
@@ -28,12 +38,12 @@ object HelloJavaTests extends TestSuite {
eval
}
def tests: Tests = Tests {
- 'scalaVersion - {
+ 'compile - {
val eval = init()
val Right((res1, n1)) = eval.apply(HelloJava.core.compile)
val Right((res2, 0)) = eval.apply(HelloJava.core.compile)
- val Right((res3, n2)) = eval.apply(HelloJava.main.compile)
+ val Right((res3, n2)) = eval.apply(HelloJava.app.compile)
assert(
res1 == res2,
@@ -49,12 +59,33 @@ object HelloJavaTests extends TestSuite {
val eval = init()
val Right((ref1, _)) = eval.apply(HelloJava.core.docJar)
- val Right((ref2, _)) = eval.apply(HelloJava.main.docJar)
+ val Right((ref2, _)) = eval.apply(HelloJava.app.docJar)
assert(
%%("jar", "tf", ref1.path).out.lines.contains("hello/Core.html"),
%%("jar", "tf", ref2.path).out.lines.contains("hello/Main.html")
)
}
+ 'test - {
+ val eval = init()
+
+ val Left(Result.Failure(ref1, Some(v1))) = eval.apply(HelloJava.core.test.test())
+
+ assert(
+ v1._2(0).fullyQualifiedName == "hello.MyCoreTests.lengthTest",
+ v1._2(0).status == "Success",
+ v1._2(1).fullyQualifiedName == "hello.MyCoreTests.msgTest",
+ v1._2(1).status == "Failure"
+ )
+
+ val Right((v2, _)) = eval.apply(HelloJava.app.test.test())
+
+ assert(
+ v2._2(0).fullyQualifiedName == "hello.MyAppTests.appTest",
+ v2._2(0).status == "Success",
+ v2._2(1).fullyQualifiedName == "hello.MyAppTests.coreTest",
+ v2._2(1).status == "Success"
+ )
+ }
}
}
diff --git a/scalaworker/src/mill/scalaworker/ScalaWorker.scala b/scalaworker/src/mill/scalaworker/ScalaWorker.scala
index 9a4c317c..08a31fc6 100644
--- a/scalaworker/src/mill/scalaworker/ScalaWorker.scala
+++ b/scalaworker/src/mill/scalaworker/ScalaWorker.scala
@@ -1,34 +1,19 @@
package mill.scalaworker
-import java.io.{File, FileInputStream}
-import java.lang.annotation.Annotation
-import java.net.URLClassLoader
+import java.io.File
import java.util.Optional
-import java.util.zip.ZipInputStream
-import javax.tools.ToolProvider
-import ammonite.ops.{Path, exists, ls, mkdir, rm, up}
+import ammonite.ops.{Path, exists, ls, mkdir}
import ammonite.util.Colors
import mill.Agg
-import mill.define.Worker
import mill.eval.PathRef
-import mill.modules.Jvm
import mill.scalalib.{CompilationResult, Lib, TestRunner}
import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _}
import mill.scalalib.Lib.grepJar
-import mill.scalalib.TestRunner.Result
import mill.util.{Ctx, PrintLogger}
import sbt.internal.inc._
-import sbt.internal.inc.classfile.Analyze
-import sbt.internal.inc.classpath.ClasspathUtilities
-import sbt.internal.inc.javac.JavaCompiler
import sbt.internal.util.{ConsoleOut, MainAppender}
-import sbt.io.PathFinder
-import sbt.testing._
-import sbt.util.{InterfaceUtil, LogExchange}
-import xsbti.AnalysisCallback
-
-import scala.collection.mutable
+import sbt.util.LogExchange
case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup {
override def analysis(classpathEntry: File): Optional[CompileAnalysis] =
@@ -68,7 +53,7 @@ object ScalaWorker{
)
val home = Path(homeStr)
}
- val result = new ScalaWorker(null, null).runTests(
+ val result = Lib.runTests(
frameworkInstances = TestRunner.frameworks(frameworks),
entireClasspath = Agg.from(classpath.map(Path(_))),
testClassfilePath = Agg(Path(testCp)),
@@ -262,104 +247,4 @@ class ScalaWorker(ctx0: mill.util.Ctx,
mill.eval.Result.Success(CompilationResult(zincFile, PathRef(classesDir)))
}catch{case e: CompileFailed => mill.eval.Result.Failure(e.toString)}
}
-
- def runTests(frameworkInstances: ClassLoader => Seq[sbt.testing.Framework],
- entireClasspath: Agg[Path],
- testClassfilePath: Agg[Path],
- args: Seq[String])
- (implicit ctx: Ctx.Log with Ctx.Home): (String, Seq[Result]) = {
- Jvm.inprocess(entireClasspath, classLoaderOverrideSbtTesting = true, cl => {
- val frameworks = frameworkInstances(cl)
-
- val events = mutable.Buffer.empty[Event]
-
- val doneMessages = frameworks.map { framework =>
- val runner = framework.runner(args.toArray, args.toArray, cl)
-
- val testClasses = discoverTests(cl, framework, testClassfilePath)
-
- val tasks = runner.tasks(
- for ((cls, fingerprint) <- testClasses.toArray)
- yield new TaskDef(cls.getName.stripSuffix("$"), fingerprint, true, Array(new SuiteSelector))
- )
-
- for (t <- tasks) {
- t.execute(
- new EventHandler {
- def handle(event: Event) = events.append(event)
- },
- Array(
- new Logger {
- def debug(msg: String) = ctx.log.outputStream.println(msg)
-
- def error(msg: String) = ctx.log.outputStream.println(msg)
-
- def ansiCodesSupported() = true
-
- def warn(msg: String) = ctx.log.outputStream.println(msg)
-
- def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream)
-
- def info(msg: String) = ctx.log.outputStream.println(msg)
- })
- )
- }
- ctx.log.outputStream.println(runner.done())
- }
-
- val results = for(e <- events) yield {
- val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None
- Result(
- e.fullyQualifiedName(),
- e.selector() match{
- case s: NestedSuiteSelector => s.suiteId()
- case s: NestedTestSelector => s.suiteId() + "." + s.testName()
- case s: SuiteSelector => s.toString
- case s: TestSelector => s.testName()
- case s: TestWildcardSelector => s.testWildcard()
- },
- e.duration(),
- e.status().toString,
- ex.map(_.getClass.getName),
- ex.map(_.getMessage),
- ex.map(_.getStackTrace)
- )
- }
-
- (doneMessages.mkString("\n"), results)
- })
-
- }
- def listClassFiles(base: Path): Iterator[String] = {
- if (base.isDir) ls.rec(base).toIterator.filter(_.ext == "class").map(_.relativeTo(base).toString)
- else {
- val zip = new ZipInputStream(new FileInputStream(base.toIO))
- Iterator.continually(zip.getNextEntry).takeWhile(_ != null).map(_.getName).filter(_.endsWith(".class"))
- }
- }
- def discoverTests(cl: ClassLoader, framework: Framework, classpath: Agg[Path]) = {
-
-
- val fingerprints = framework.fingerprints()
- val testClasses = classpath.flatMap { base =>
- // Don't blow up if there are no classfiles representing
- // the tests to run Instead just don't run anything
- if (!exists(base)) Nil
- else listClassFiles(base).flatMap { path =>
- val cls = cl.loadClass(path.stripSuffix(".class").replace('/', '.'))
- fingerprints.find {
- case f: SubclassFingerprint =>
- !cls.isInterface &&
- (f.isModule == cls.getName.endsWith("$")) &&
- cl.loadClass(f.superclassName()).isAssignableFrom(cls)
- case f: AnnotatedFingerprint =>
- (f.isModule == cls.getName.endsWith("$")) &&
- cls.isAnnotationPresent(
- cl.loadClass(f.annotationName()).asInstanceOf[Class[Annotation]]
- )
- }.map { f => (cls, f) }
- }
- }
- testClasses
- }
}