Skip to content

Commit

Permalink
fix #8 simple 'hello world' scala module with tests on it; extract te…
Browse files Browse the repository at this point in the history
…st evaluator (#30)
  • Loading branch information
rockjam authored and lihaoyi committed Nov 28, 2017
1 parent c6d95ab commit 9b47664
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 20 deletions.
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)
}
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)
23 changes: 3 additions & 20 deletions scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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 - {
Expand Down
221 changes: 221 additions & 0 deletions scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala
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 scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala
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())
}
}

}

0 comments on commit 9b47664

Please sign in to comment.