10

I want to hava a SBT task which takes comma-separated list of test classes given by their fully qualified name as input from command line. Now that I run the task with hard-coded value but I want to get it from command line. Can someone help me in writing a task like this.

 lazy val runTask = inputKey[Unit]("custom run")

 runTask := {
    val one = (runMain in Compile).fullInput(" org.scalatest.tools.Runner -P1 -C reporter.TestReporter -o -s testcase.GetAccountInfo -s testcase.GetProfileInfo").evaluated
 }

Something like this,

 sbt runTask testcase.GetProfileInfo,testcase.GetAccountInfo

Thanks in advance.

lpiepiora
  • 13,659
  • 1
  • 35
  • 47
Sarath
  • 1,438
  • 4
  • 24
  • 40

1 Answers1

16

You have to have a Parser, which will parse the input given to the task. Once you have the input, you can convert (runMain in Compile).toTask, and feed the input to the task.

TL;DR; build.sbt

import sbt.complete._

import complete.DefaultParsers._

lazy val myRunTask = inputKey[Unit]("Runs actual tests")

lazy val FullQualifiedClassName = 
  (charClass(c => isScalaIDChar(c) || (c == '.'), "class name")).+.string

def commaDelimited(display: String) =
  token(Space) ~> repsep(token(FullQualifiedClassName, display), token(","))

lazy val testClassArgs: Parser[Seq[String]] = 
  commaDelimited("<full qualified test class name>").map { 
    xs: Seq[String] => xs.map(e => s" -s $e ")
  }

myRunTask := Def.inputTaskDyn {
  val classes = testClassArgs.parsed
  runMainInCompile(classes)
}.evaluated

def runMainInCompile(classes: Seq[String]) = Def.taskDyn {
  (runMain in Compile).toTask(s" org.scalatest.tools.Runner -P1 -C reporter.TestReporter -o ${classes.mkString}")
}

Parser

Let's start with a parser. The parser must take a space, followed by your classes separated by comma.

Let's first defined a parser, which parses full qualified class name:

lazy val FullQualifiedClassName = 
      (charClass(c => isScalaIDChar(c) || (c == '.'), "class name")).+.string

Once we have the parser, we can combine it together with another parser. We need to create a parser, which takes comma separated full qualified class names:

def commaDelimited(display: String) = 
      token(Space) ~> repsep(token(FullQualifiedClassName, display), token(","))

The ~> operator means that the input on the left of it will be discarded. The value returned from the parser is a Seq[String] of the full qualified class names.

Judging from your question, you want your classes to be prefixed with -s. You could do it later, but just to show one more feature of parsers, I'll just do it here.

You can take an output of a parser and convert it to another output, using map.

lazy val testClassArgs: Parser[Seq[String]] = 
  commaDelimited("<full qualified test class name>").map { 
    xs: Seq[String] => xs.map(e => s" -s $e ")
  }

OK, we're almost there.

Run InputTask with arguments combined with a static string

We can define a new input task key. I'll chose myRunTask, because otherwise it will collide with runTask, which already exists.

Let's define a method which takes a sequence of classes (already prefixed with -s) as an argument, and which returns a Task obtained from an InputTask.

def runMainInCompile(classes: Seq[String]) = Def.taskDyn {
  (runMain in Compile).toTask(s" org.scalatest.tools.Runner -P1 -C reporter.TestReporter -o ${classes.mkString}")
} 

Now let's combine all elements in one task:

myRunTask := Def.inputTaskDyn {
  val classes = testClassArgs.parsed
  runMainInCompile(classes)
}.evaluated
lpiepiora
  • 13,659
  • 1
  • 35
  • 47
  • Hi Ipiepiora, Thanks for answering. I'm getting the following import errors. \build.sbt:52: error: not found: value charClass (charClass(c => isScalaIDChar(c) || (c == '.'), "class name")).+.string ^ D:\Workspaces\KeplerWS_2\cross-browser-test\build.sbt:57: error: not found: type Parser lazy val testClassArgs: Parser[Seq[String]] = \build.sbt:55: error: not found: value token token(Space) ~> repsep(token(FullQualifiedClassName, display), token(",")) sbt.compiler.EvalException: Type error in expression – Sarath Jun 05 '14 at 05:55
  • Am I missing something ? I'm new to SBT. Kindly guide me. – Sarath Jun 05 '14 at 05:59
  • @Sarath are you running it on sbt 0.13.5 ? Have you tried with just creating a `build.sbt` file, with only my content from the first code snippet – lpiepiora Jun 05 '14 at 06:27
  • Hi lpiepiora, I'm using sbt 0.13. I created a build.sbt file with the content you posted. Getting some imports problem. – Sarath Jun 05 '14 at 11:44
  • Can you try with sbt 0.13.5 ? Also remember that new lines are important, do not concatenate lines, which are not concatenated in my snippet. – lpiepiora Jun 05 '14 at 11:46
  • Now that i included all the imports with the methods. For eg., complete.DefaultParsers.isScalaIDChar, complete.DefaultParsers.repsep, complete.Parser.token, complete.Parser. And I changed that .+.string to toString No errors now. How am I supposed to run with the args. – Sarath Jun 05 '14 at 12:42
  • You should be able to run it just like you've stated in your OP. However changing `.+.string` to `toString` is not a good idea. I am certain that my code should work in new project on sbt 0.13.5, just as I've typed it in above. – lpiepiora Jun 05 '14 at 13:10
  • @Sarath you can check working example in my github repository https://github.com/lpiepiora/stack-overflow/tree/master/solutions/q-23952566 – lpiepiora Jun 06 '14 at 19:11
  • The `FullQualifiedClassName` is pretty dense. How did you figure that out? Did you use a built-in from sbt `complete` as a startign point? – WestCoastProjects Jul 26 '16 at 18:03