From 9b47664a85089847090fe12804bfb5dc1ba6e376 Mon Sep 17 00:00:00 2001 From: Nikolay Tatarinov <5min4eq.unity@gmail.com> Date: Tue, 28 Nov 2017 07:18:29 +0300 Subject: fix #8 simple 'hello world' scala module with tests on it; extract test evaluator (#30) --- .../resource/hello-world/src/main/scala/Main.scala | 10 + .../hello-world/src/main/scala/Result.scala | 7 + .../test/scala/mill/scalaplugin/AcyclicTests.scala | 23 +-- .../scala/mill/scalaplugin/HelloWorldTests.scala | 221 +++++++++++++++++++++ .../scala/mill/scalaplugin/TestEvaluator.scala | 38 ++++ 5 files changed, 279 insertions(+), 20 deletions(-) create mode 100644 scalaplugin/src/test/resource/hello-world/src/main/scala/Main.scala create mode 100644 scalaplugin/src/test/resource/hello-world/src/main/scala/Result.scala create mode 100644 scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala create mode 100644 scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala (limited to 'scalaplugin/src') diff --git a/scalaplugin/src/test/resource/hello-world/src/main/scala/Main.scala b/scalaplugin/src/test/resource/hello-world/src/main/scala/Main.scala new file mode 100644 index 00000000..e288a17a --- /dev/null +++ b/scalaplugin/src/test/resource/hello-world/src/main/scala/Main.scala @@ -0,0 +1,10 @@ +import scala.collection._ // unused import to check unused imports warning +import java.nio.file.{Files, Paths} + +object Main extends App { + val person = Person.fromString("rockjam:25") + val greeting = s"hello ${person.name}, your age is: ${person.age}" + println(greeting) + val resultPath = Paths.get("target", "workspace", "hello-world", "hello-mill") + Files.write(resultPath, greeting.getBytes) +} diff --git a/scalaplugin/src/test/resource/hello-world/src/main/scala/Result.scala b/scalaplugin/src/test/resource/hello-world/src/main/scala/Result.scala new file mode 100644 index 00000000..d7d29a51 --- /dev/null +++ b/scalaplugin/src/test/resource/hello-world/src/main/scala/Result.scala @@ -0,0 +1,7 @@ +object Person { + def fromString(s: String): Person = { + val Array(name, age) = s.split(":") + Person(name, age.toInt) + } +} +case class Person(name: String, age: Int) diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala index 1f0bbd7e..1a11a759 100644 --- a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala +++ b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala @@ -2,12 +2,9 @@ package mill.scalaplugin import ammonite.ops.ImplicitWd._ import ammonite.ops._ -import mill.define.{Cross, Target, Task} +import mill.define.{Cross,Task} import mill.discover.Discovered -import mill.eval.{Evaluator, PathRef, Result} -import mill.modules.Jvm.jarUp -import mill.{Module, T} -import mill.util.OSet +import mill.eval.Result import utest._ import mill.util.JsonFormatters._ object AcyclicBuild{ @@ -43,22 +40,8 @@ object AcyclicTests extends TestSuite{ mkdir(workspacePath/up) cp(srcPath, workspacePath) val mapping = Discovered.mapping(AcyclicBuild) - def eval[T](t: Task[T]): Either[Result.Failing, (T, Int)] = { - val evaluator = new Evaluator(workspacePath, mapping, _ => ()) - val evaluated = evaluator.evaluate(OSet(t)) + def eval[T](t: Task[T]) = TestEvaluator.eval(mapping, workspacePath)(t) - if (evaluated.failing.keyCount == 0){ - Right(Tuple2( - evaluated.rawValues(0).asInstanceOf[Result.Success[T]].value, - evaluated.evaluated.collect{ - case t: Target[_] if mapping.contains(t) => t - case t: mill.define.Command[_] => t - }.size - )) - }else{ - Left(evaluated.failing.lookupKey(evaluated.failing.keys().next).items.next()) - } - } val packageScala = workspacePath/'src/'main/'scala/'acyclic/"package.scala" 'acyclic - { diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala new file mode 100644 index 00000000..03b9b285 --- /dev/null +++ b/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala @@ -0,0 +1,221 @@ +package mill.scalaplugin + +import ammonite.ops._ +import ammonite.ops.ImplicitWd._ +import mill._ +import mill.define.{Target, Task} +import mill.discover.Discovered +import mill.discover.Mirror.LabelledTarget +import mill.eval.Result +import sbt.internal.inc.CompileFailed +import utest._ + +trait HelloWorldModule extends ScalaModule { + def scalaVersion = "2.12.4" + def basePath = HelloWorldTests.workspacePath +} + +object HelloWorld extends HelloWorldModule + +object HelloWorldWarnUnused extends HelloWorldModule { + override def scalacOptions = T(Seq("-Ywarn-unused")) +} + +object HelloWorldFatalWarnings extends HelloWorldModule { + override def scalacOptions = T(Seq("-Ywarn-unused", "-Xfatal-warnings")) +} + +object HelloWorldTests extends TestSuite { + + val srcPath = pwd / 'scalaplugin / 'src / 'test / 'resource / "hello-world" + val workspacePath = pwd / 'target / 'workspace / "hello-world" + val outputPath = workspacePath / 'out + val mainObject = workspacePath / 'src / 'main / 'scala / "Main.scala" + + def eval[T](t: Task[T], mapping: Map[Target[_], LabelledTarget[_]]) = + TestEvaluator.eval(mapping, outputPath)(t) + + val helloWorldMapping = Discovered.mapping(HelloWorld) + + def tests: Tests = Tests { + prepareWorkspace() + 'scalaVersion - { + 'fromBuild - { + val Right((result, evalCount)) = + eval(HelloWorld.scalaVersion, helloWorldMapping) + + assert( + result == "2.12.4", + evalCount > 0 + ) + } + 'override - { + object HelloWorldScalaOverride extends HelloWorldModule { + override def scalaVersion: Target[String] = "2.11.11" + } + + val Right((result, evalCount)) = + eval(HelloWorldScalaOverride.scalaVersion, + Discovered.mapping(HelloWorldScalaOverride)) + + assert( + result == "2.11.11", + evalCount > 0 + ) + } + } + 'scalacOptions - { + 'emptyByDefault - { + val Right((result, evalCount)) = + eval(HelloWorld.scalacOptions, helloWorldMapping) + + assert( + result.isEmpty, + evalCount > 0 + ) + } + 'override - { + val Right((result, evalCount)) = + eval(HelloWorldFatalWarnings.scalacOptions, + Discovered.mapping(HelloWorldFatalWarnings)) + + assert( + result == Seq("-Ywarn-unused", "-Xfatal-warnings"), + evalCount > 0 + ) + } + } + 'compile - { + 'fromScratch - { + val Right((result, evalCount)) = + eval(HelloWorld.compile, helloWorldMapping) + + val outPath = result.path + val outputFiles = ls.rec(outPath) + val expectedClassfiles = compileClassfiles(outputPath / 'compile / 'classes) + assert( + outPath == outputPath / 'compile / 'classes, + outputFiles.nonEmpty, + outputFiles.forall(expectedClassfiles.contains), + evalCount > 0 + ) + + // don't recompile if nothing changed + val Right((_, unchangedEvalCount)) = + eval(HelloWorld.compile, helloWorldMapping) + assert(unchangedEvalCount == 0) + } + 'recompileOnChange - { + val Right((_, freshCount)) = + eval(HelloWorld.compile, helloWorldMapping) + assert(freshCount > 0) + + write.append(mainObject, "\n") + + val Right((_, incCompileCount)) = + eval(HelloWorld.compile, helloWorldMapping) + assert(incCompileCount == 1) + } + 'failOnError - { + write.append(mainObject, "val x: ") + + val Left(Result.Exception(err)) = + eval(HelloWorld.compile, helloWorldMapping) + + assert(err.isInstanceOf[CompileFailed]) + + val (compilePath, compileMetadataPath) = + TestEvaluator.resolveDestPaths(outputPath)( + helloWorldMapping(HelloWorld.compile)) + + assert( + ls.rec(compilePath / 'classes).isEmpty, + !exists(compileMetadataPath) + ) + } + 'passScalacOptions - { + // compilation fails because of "-Xfatal-warnings" flag + val Left(Result.Exception(err)) = + eval(HelloWorldFatalWarnings.compile, + Discovered.mapping(HelloWorldFatalWarnings)) + + assert(err.isInstanceOf[CompileFailed]) + } + } + 'run - { + 'runMainObject - { + val Right((_, evalCount)) = + eval(HelloWorld.run("Main"), helloWorldMapping) + + assert(evalCount > 0) + + val runResult = workspacePath / "hello-mill" + assert( + exists(runResult), + read(runResult) == "hello rockjam, your age is: 25" + ) + } + 'notRunInvalidMainObject - { + val Left(Result.Exception(err)) = + eval(HelloWorld.run("Invalid"), helloWorldMapping) + + assert( + err.isInstanceOf[InteractiveShelloutException] + ) + } + 'notRunWhenComplileFailed - { + write.append(mainObject, "val x: ") + + val Left(Result.Exception(err)) = + eval(HelloWorld.run("Main"), helloWorldMapping) + + assert( + err.isInstanceOf[CompileFailed] + ) + } + } + 'jar - { + 'nonEmpty - { + val Right((result, evalCount)) = + eval(HelloWorld.jar, helloWorldMapping) + + assert( + exists(result.path), + evalCount > 0 + ) + + val unJarPath = outputPath / 'unjar + mkdir(unJarPath) + %("tar", "xf", result.path, "-C", unJarPath) + + val manifestFiles = Seq( + unJarPath / "META-INF", + unJarPath / "META-INF" / "MANIFEST.MF", + ) + val expectedFiles = compileClassfiles(unJarPath) ++ manifestFiles + + val jarFiles = ls.rec(unJarPath) + assert( + jarFiles.nonEmpty, + jarFiles.forall(expectedFiles.contains) + ) + } + // TODO: check that we can `java -jar` produced jar + } + } + + def compileClassfiles(parentDir: Path) = Seq( + parentDir / "Main.class", + parentDir / "Main$.class", + parentDir / "Main$delayedInit$body.class", + parentDir / "Person.class", + parentDir / "Person$.class" + ) + + def prepareWorkspace(): Unit = { + rm(workspacePath) + mkdir(workspacePath / up) + cp(srcPath, workspacePath) + } + +} diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala b/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala new file mode 100644 index 00000000..e54480c9 --- /dev/null +++ b/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala @@ -0,0 +1,38 @@ +package mill.scalaplugin + +import ammonite.ops.Path +import mill.define.{Target, Task} +import mill.discover.Mirror +import mill.eval.{Evaluator, Result} +import mill.util.OSet + +object TestEvaluator { + + private val noopLogger: String => Unit = _ => () + + def resolveDestPaths(workspacePath: Path)(t: Mirror.LabelledTarget[_]): (Path, Path) = { + new Evaluator(workspacePath, Map.empty, noopLogger).resolveDestPaths(t) + } + + def eval[T]( + mapping: Map[Target[_], Mirror.LabelledTarget[_]], + workspacePath: Path)(t: Task[T]): Either[Result.Failing, (T, Int)] = { + val evaluator = new Evaluator(workspacePath, mapping, noopLogger) + val evaluated = evaluator.evaluate(OSet(t)) + + if (evaluated.failing.keyCount == 0) { + Right( + Tuple2( + evaluated.rawValues.head.asInstanceOf[Result.Success[T]].value, + evaluated.evaluated.collect { + case t: Target[_] if mapping.contains(t) => t + case t: mill.define.Command[_] => t + }.size + )) + } else { + Left( + evaluated.failing.lookupKey(evaluated.failing.keys().next).items.next()) + } + } + +} -- cgit v1.2.3