-
-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix #8 simple 'hello world' scala module with tests on it; extract te…
…st evaluator (#30)
- Loading branch information
Showing
5 changed files
with
279 additions
and
20 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
scalaplugin/src/test/resource/hello-world/src/main/scala/Main.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
7 changes: 7 additions & 0 deletions
7
scalaplugin/src/test/resource/hello-world/src/main/scala/Result.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
221 changes: 221 additions & 0 deletions
221
scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
|
||
} |
38 changes: 38 additions & 0 deletions
38
scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) | ||
} | ||
} | ||
|
||
} |