summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-11-25 09:25:28 -0800
committerLi Haoyi <haoyi.sg@gmail.com>2017-11-25 09:27:16 -0800
commit60d7b68ed6c1d0a86d05668f040f73ee619ddde8 (patch)
treeffe6f0cd26b2603e2603ee38287ea0ebf84c9be5 /core/src
parentff852a7a7602030b57af6f7fa499742b5c76d771 (diff)
downloadmill-60d7b68ed6c1d0a86d05668f040f73ee619ddde8.tar.gz
mill-60d7b68ed6c1d0a86d05668f040f73ee619ddde8.tar.bz2
mill-60d7b68ed6c1d0a86d05668f040f73ee619ddde8.zip
Unit tests for `Main.resolve`
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/scala/mill/Main.scala82
-rw-r--r--core/src/test/scala/mill/main/MainTests.scala49
2 files changed, 95 insertions, 36 deletions
diff --git a/core/src/main/scala/mill/Main.scala b/core/src/main/scala/mill/Main.scala
index a8c51a5b..a3cde4eb 100644
--- a/core/src/main/scala/mill/Main.scala
+++ b/core/src/main/scala/mill/Main.scala
@@ -24,66 +24,76 @@ object Main {
query.parse(input)
}
- def parseArgs(args: Seq[String]): Either[String, List[scala.util.Either[String,Seq[String]]]] = {
- import fastparse.all.Parsed
-
- val Seq(selectorString, rest @_*) = args
+ def renderSelector(selector: List[Either[String, Seq[String]]]) = {
+ val Left(head) :: rest = selector
+ head + rest.map{case Left(s) => "." + s case Right(vs) => "[" + vs.mkString(",") + "]"}.mkString
+ }
- parseSelector(selectorString) match {
+ def parseArgs(selectorString: String): Either[String, List[scala.util.Either[String,Seq[String]]]] = {
+ import fastparse.all.Parsed
+ if (selectorString.isEmpty) Left("Selector cannot be empty")
+ else parseSelector(selectorString) match {
case f: Parsed.Failure => Left(s"Parsing exception ${f.msg}")
case Parsed.Success(selector, _) => Right(selector)
}
}
- def resolve[T, V](selector: List[Either[String, Seq[String]]],
- hierarchy: Mirror[T, V])(implicit
+ def resolve[T, V](remainingSelector: List[Either[String, Seq[String]]],
+ hierarchy: Mirror[T, V],
obj: T,
rest: Seq[String],
- crossSelectors: List[List[String]]): Either[String, Task[Any]] = {
+ remainingCrossSelectors: List[List[String]],
+ revSelectorsSoFar: List[Either[String, Seq[String]]]): Either[String, Task[Any]] = {
- selector match{
- case Right(_) :: Nil => Left("No target or command selected")
+ remainingSelector match{
+ case Right(_) :: Nil => Left("Selector cannot start with a [cross] segment")
case Left(last) :: Nil =>
def target: Option[Task[Any]] =
- hierarchy.targets.find(_.label == last)
- .map{x => x.run(hierarchy.node(obj, crossSelectors))}
+ hierarchy.targets
+ .find(_.label == last)
+ .map{x => x.run(hierarchy.node(obj, remainingCrossSelectors))}
def targetModule: Seq[Task[Any]] = for{
(label, child) <- hierarchy.children
if label == last
- node <- child.node(obj, crossSelectors) match{
+ node <- child.node(obj, remainingCrossSelectors) match{
case x: TaskModule => Some(x)
case _ => None
}
- } yield node.self
-
- def command: Either[String, Task[Any]] =
- hierarchy.commands.find(_.name == last).fold[Either[String, Task[Any]]](
- Left(s"Command not found $last")
- ){ x =>
- Option(hierarchy.node(obj, crossSelectors)).fold[Either[String, Task[Any]]](
- Left(s"Instance not found for calling $last")
- ){ inst =>
- (x.invoke(inst, ammonite.main.Scripts.groupArgs(rest.toList)) match {
- case Router.Result.Success(v) => Right(v)
- case _ => Left(s"Method not found $last")
- })
- }
+ } yield node.self()
+
+ def command =
+ for(x <- hierarchy.commands.find(_.name == last))
+ yield x.invoke(
+ hierarchy.node(obj, remainingCrossSelectors),
+ ammonite.main.Scripts.groupArgs(rest.toList)
+ ) match {
+ case Router.Result.Success(v) => Right(v)
+ case _ => Left(s"Command failed $last")
}
- target.map(Right(_)) orElse targetModule.headOption.map(Right(_)) getOrElse command
+ command orElse target.map(Right(_)) orElse targetModule.headOption.map(Right(_)) match{
+ case None => Left("Cannot resolve task " + renderSelector((Left(last) :: revSelectorsSoFar).reverse))
+ case Some(either) => either
+ }
+
+
case head :: tail =>
+ val newRevSelectorsSoFar = head :: revSelectorsSoFar
head match{
case Left(singleLabel) =>
hierarchy.children.collectFirst{
- case (label, child) if label == singleLabel =>
- resolve(tail, child)
- }.getOrElse( Left(s"Single label not found $singleLabel") )
+ case (label, child) if label == singleLabel => child
+ } match{
+ case Some(child) => resolve(tail, child, obj, rest, remainingCrossSelectors, newRevSelectorsSoFar)
+ case None => Left("Cannot resolve module " + renderSelector(newRevSelectorsSoFar))
+ }
+
case Right(cross) =>
- resolve(tail, hierarchy.crossChildren.get._2)
+ resolve(tail, hierarchy.crossChildren.get._2, obj, rest, remainingCrossSelectors, newRevSelectorsSoFar)
}
- case Nil => Left("Nothing to run")
+ case Nil => Left("Selector cannot be empty")
}
}
@@ -134,14 +144,14 @@ object Main {
val log = new Logger(coloredOutput)
- val Seq(_, rest @_*) = args
+ val Seq(selectorString, rest @_*) = args
val res =
for {
- sel <- parseArgs(args)
+ sel <- parseArgs(selectorString)
disc <- discoverMirror(obj)
val crossSelectors = sel.collect{case Right(x) => x.toList}
- target <- resolve(sel, disc.mirror)(obj, rest, crossSelectors)
+ target <- resolve(sel, disc.mirror, obj, rest, crossSelectors, Nil)
val mapping = Discovered.mapping(obj)(disc)
val workspacePath = pwd / 'out
val evaluator = new Evaluator(workspacePath, mapping, log.info)
diff --git a/core/src/test/scala/mill/main/MainTests.scala b/core/src/test/scala/mill/main/MainTests.scala
new file mode 100644
index 00000000..de7cfd7d
--- /dev/null
+++ b/core/src/test/scala/mill/main/MainTests.scala
@@ -0,0 +1,49 @@
+package mill.main
+
+import mill.Module
+import mill.define.Task
+import mill.discover.Discovered
+import mill.util.TestUtil.test
+import utest._
+
+object MainTests extends TestSuite{
+ def check[T: Discovered](obj: T,
+ selectorString: String,
+ expected: Either[String, Task[_]]) = {
+ val resolved = for{
+ args <- mill.Main.parseArgs(selectorString)
+ task <- mill.Main.resolve(args, implicitly[Discovered[T]].mirror, obj, Nil, Nil, Nil)
+ } yield task
+ assert(resolved == expected)
+ }
+ val tests = Tests{
+ val graphs = new mill.util.TestGraphs()
+ import graphs._
+ 'single - {
+ 'pos - check(singleton, "single", Right(singleton.single))
+ 'neg1 - check(singleton, "doesntExist", Left("Cannot resolve task doesntExist"))
+ 'neg2 - check(singleton, "single.doesntExist", Left("Cannot resolve module single"))
+ 'neg3 - check(singleton, "", Left("Selector cannot be empty"))
+ }
+ 'nested - {
+ class CanNest extends Module{
+ val single = test()
+ }
+ object outer {
+ val single = test()
+ object nested extends Module{
+ val single = test()
+ }
+ val classInstance = new CanNest
+
+ }
+ 'pos1 - check(outer, "single", Right(outer.single))
+ 'pos2 - check(outer, "nested.single", Right(outer.nested.single))
+ 'pos3 - check(outer, "classInstance.single", Right(outer.classInstance.single))
+ 'neg1 - check(outer, "doesntExist", Left("Cannot resolve task doesntExist"))
+ 'neg2 - check(outer, "single.doesntExist", Left("Cannot resolve module single"))
+ 'neg3 - check(outer, "nested.doesntExist", Left("Cannot resolve task nested.doesntExist"))
+ 'neg4 - check(outer, "classInstance.doesntExist", Left("Cannot resolve task classInstance.doesntExist"))
+ }
+ }
+}