mirror of https://github.com/sbt/sbt.git
* Disable parallel execution until scripted tests support it
* Remove scripted tests, since it was moved into a plugin * ignore test currently fails (scripted test plugin problem) git-svn-id: https://simple-build-tool.googlecode.com/svn/trunk@850 d89573ee-9141-11dd-94d4-bdf5e562f29c
This commit is contained in:
parent
7008061c64
commit
40bf046677
|
|
@ -1,7 +1,7 @@
|
||||||
#Project properties
|
#Project properties
|
||||||
#Thu Jun 25 20:59:30 EDT 2009
|
#Sat Jul 04 12:43:34 EDT 2009
|
||||||
project.organization=sbt
|
project.organization=sbt
|
||||||
project.name=Simple Build Tool Parent
|
project.name=Simple Build Tool Parent
|
||||||
sbt.version=0.5
|
sbt.version=0.5.1
|
||||||
project.version=0.5.2-SNAPSHOT
|
project.version=0.5.2-p1
|
||||||
scala.version=2.7.2
|
scala.version=2.7.2
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ class SbtProject(info: ProjectInfo) extends ParentProject(info)
|
||||||
override def shouldCheckOutputDirectories = false
|
override def shouldCheckOutputDirectories = false
|
||||||
override def baseUpdateOptions = QuietUpdate :: Nil
|
override def baseUpdateOptions = QuietUpdate :: Nil
|
||||||
|
|
||||||
override def parallelExecution = true
|
//override def parallelExecution = true
|
||||||
override def deliverLocalAction = noAction
|
override def deliverLocalAction = noAction
|
||||||
private def noAction = task { None }
|
private def noAction = task { None }
|
||||||
override def publishLocalAction = noAction
|
override def publishLocalAction = noAction
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class MainProject(val info: ProjectInfo) extends CrossCompileProject
|
protected class MainProject(val info: ProjectInfo) extends CrossCompileProject with test.ScalaScripted
|
||||||
{
|
{
|
||||||
override def mainSources =
|
override def mainSources =
|
||||||
if(ScalaVersion.currentString.startsWith("2.8")) // cannot compile against test libraries currently
|
if(ScalaVersion.currentString.startsWith("2.8")) // cannot compile against test libraries currently
|
||||||
|
|
@ -37,38 +37,5 @@ protected class MainProject(val info: ProjectInfo) extends CrossCompileProject
|
||||||
override def mainResources = super.mainResources +++ extraResources
|
override def mainResources = super.mainResources +++ extraResources
|
||||||
override def mainClass = Some("sbt.Main")
|
override def mainClass = Some("sbt.Main")
|
||||||
override def testOptions = ExcludeTests("sbt.ReflectiveSpecification" :: "sbt.ProcessSpecification" :: Nil) :: super.testOptions.toList
|
override def testOptions = ExcludeTests("sbt.ReflectiveSpecification" :: "sbt.ProcessSpecification" :: Nil) :: super.testOptions.toList
|
||||||
|
override def scriptedDependencies = testCompile :: `package` :: Nil
|
||||||
// ======== Scripted testing ==========
|
|
||||||
|
|
||||||
def sbtTestResources = testResourcesPath / "sbt-test-resources"
|
|
||||||
|
|
||||||
lazy val testNoScripted = super.testAction
|
|
||||||
override def testAction = testNoScripted dependsOn(scripted)
|
|
||||||
lazy val scripted = scriptedTask dependsOn(testCompile, `package`)
|
|
||||||
def scriptedTask =
|
|
||||||
task
|
|
||||||
{
|
|
||||||
log.info("Running scripted tests...")
|
|
||||||
log.info("")
|
|
||||||
// load ScriptedTests using a ClassLoader that loads from the project classpath so that the version
|
|
||||||
// of sbt being built is tested, not the one doing the building.
|
|
||||||
val loader = ScriptedLoader(scriptedClasspath.toArray)
|
|
||||||
val scriptedClass = Class.forName(ScriptedClassName, true, loader)
|
|
||||||
val scriptedConstructor = scriptedClass.getConstructor(classOf[File], classOf[Function2[String, String, Boolean]])
|
|
||||||
val runner = scriptedConstructor.newInstance(sbtTestResources.asFile, filter)
|
|
||||||
runner.asInstanceOf[{def scriptedTests(log: Logger): Option[String]}].scriptedTests(log)
|
|
||||||
}
|
|
||||||
/** The classpath to use for scripted tests. This ensures that the version of sbt being built it the one used for testing.*/
|
|
||||||
private def scriptedClasspath =
|
|
||||||
{
|
|
||||||
val buildClasspath = classOf[SbtProject]. getProtectionDomain.getCodeSource.getLocation.toURI.toURL
|
|
||||||
val scalacJar = FileUtilities.scalaCompilerJar.toURI.toURL
|
|
||||||
val ivy = runClasspath.get.filter(_.asFile.getName.startsWith("ivy-")).map(_.asURL).toList
|
|
||||||
val builtSbtJar = (outputPath / defaultJarName).asURL
|
|
||||||
builtSbtJar :: buildClasspath :: scalacJar :: ivy
|
|
||||||
}
|
|
||||||
|
|
||||||
val ScriptedClassName = "scripted.ScriptedTests"
|
|
||||||
|
|
||||||
val filter = (group: String, name: String) => true
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/* sbt -- Simple Build Tool
|
|
||||||
* Copyright 2008, 2009 Mark Harrah
|
|
||||||
*/
|
|
||||||
import sbt._
|
|
||||||
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
package sbt { // need access to LoaderBase, which is private in package sbt
|
|
||||||
object ScriptedLoader
|
|
||||||
{
|
|
||||||
def apply(paths: Array[URL]): ClassLoader = new ScriptedLoader(paths)
|
|
||||||
}
|
|
||||||
private class ScriptedLoader(paths: Array[URL]) extends LoaderBase(paths, classOf[ScriptedLoader].getClassLoader)
|
|
||||||
{
|
|
||||||
private val delegateFor = List("sbt.Logger", "sbt.LogEvent", "sbt.SetLevel", "sbt.Success", "sbt.Log", "sbt.SetTrace", "sbt.Trace", "sbt.ControlEvent")
|
|
||||||
def doLoadClass(className: String): Class[_] =
|
|
||||||
{
|
|
||||||
// Logger needs to be loaded from the version of sbt building the project because we need to pass
|
|
||||||
// a Logger from that loader into ScriptedTests.
|
|
||||||
// All other sbt classes should be loaded from the project classpath so that we test those classes with 'scripted'
|
|
||||||
if(!shouldDelegate(className) && (className.startsWith("sbt.") || className.startsWith("scripted.") || className.startsWith("scala.tools.")))
|
|
||||||
findClass(className)
|
|
||||||
else
|
|
||||||
selfLoadClass(className)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def shouldDelegate(className: String) = delegateFor.exists(check => isNestedOrSelf(className, check))
|
|
||||||
private def isNestedOrSelf(className: String, checkAgainst: String) =
|
|
||||||
className == checkAgainst || className.startsWith(checkAgainst + "$")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
/* sbt -- Simple Build Tool
|
|
||||||
* Copyright 2009 Mark Harrah
|
|
||||||
*/
|
|
||||||
|
|
||||||
package scripted
|
|
||||||
|
|
||||||
import sbt._
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
trait ScriptedTestFilter extends NotNull
|
|
||||||
{
|
|
||||||
def accept(group: String, name: String): Boolean
|
|
||||||
}
|
|
||||||
class BasicFilter(f: (String, String) => Boolean) extends ScriptedTestFilter
|
|
||||||
{
|
|
||||||
def accept(group: String, name: String) = f(group, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
object AcceptAllFilter extends ScriptedTestFilter
|
|
||||||
{
|
|
||||||
def accept(group: String, name: String): Boolean = true
|
|
||||||
}
|
|
||||||
class ScriptedTests(testResources: Resources, filter: ScriptedTestFilter) extends NotNull
|
|
||||||
{
|
|
||||||
def this(resourceBaseDirectory: File, filter: (String, String) => Boolean) = this(new Resources(resourceBaseDirectory), new BasicFilter(filter))
|
|
||||||
def this(resourceBaseDirectory: File, filter: ScriptedTestFilter) = this(new Resources(resourceBaseDirectory), filter)
|
|
||||||
def this(testResources: Resources) = this(testResources, AcceptAllFilter)
|
|
||||||
def this(resourceBaseDirectory: File) = this(new Resources(resourceBaseDirectory))
|
|
||||||
|
|
||||||
val ScriptFilename = "test"
|
|
||||||
import testResources._
|
|
||||||
|
|
||||||
private def includeDirectory(file: File) = file.getName != ".svn"
|
|
||||||
def scriptedTests(log: Logger): Option[String] =
|
|
||||||
{
|
|
||||||
System.setProperty("sbt.scala.version", "")
|
|
||||||
var success = true
|
|
||||||
for(group <- baseDirectory.listFiles(DirectoryFilter) if includeDirectory(group))
|
|
||||||
{
|
|
||||||
log.info("Test group " + group.getName)
|
|
||||||
for(test <- group.listFiles(DirectoryFilter) if includeDirectory(test))
|
|
||||||
{
|
|
||||||
val testName = test.getName
|
|
||||||
if(!filter.accept(group.getName, testName))
|
|
||||||
log.warn(" Test " + testName + " skipped.")
|
|
||||||
else
|
|
||||||
scriptedTest(test, log) match
|
|
||||||
{
|
|
||||||
case Some(err) =>
|
|
||||||
log.error(" Test " + testName + " failed: " + err)
|
|
||||||
success = false
|
|
||||||
case None => log.info(" Test " + testName + " succeeded.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(success)
|
|
||||||
None
|
|
||||||
else
|
|
||||||
Some("One or more tests failed.")
|
|
||||||
}
|
|
||||||
|
|
||||||
def scriptedTest(group: String, name: String, log: Logger): Option[String] =
|
|
||||||
readOnlyResourceDirectory(group, name).fold(err => Some(err), testDirectory => scriptedTest(testDirectory, log))
|
|
||||||
def scriptedTest(testDirectory: File, log: Logger): Option[String] =
|
|
||||||
{
|
|
||||||
(for(script <- (new TestScriptParser(testDirectory, log)).parse(new File(testDirectory, ScriptFilename)).right;
|
|
||||||
u <- withProject(testDirectory, log)(script).right )
|
|
||||||
yield u).left.toOption
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
/* sbt -- Simple Build Tool
|
|
||||||
* Copyright 2009 Mark Harrah
|
|
||||||
*/
|
|
||||||
|
|
||||||
package scripted
|
|
||||||
|
|
||||||
import sbt._
|
|
||||||
import java.io.{BufferedReader, File, InputStreamReader}
|
|
||||||
|
|
||||||
/*
|
|
||||||
statement*
|
|
||||||
statement ::= ('$' | '>') word+ '[' word ']'
|
|
||||||
word ::= [^ \[\]]+
|
|
||||||
comment ::= '#' [^ \n\r]* ('\n' | '\r' | eof)
|
|
||||||
*/
|
|
||||||
import scala.util.parsing.combinator._
|
|
||||||
import scala.util.parsing.input.Positional
|
|
||||||
|
|
||||||
import TestScriptParser._
|
|
||||||
private class TestScriptParser(baseDirectory: File, log: Logger) extends RegexParsers with NotNull
|
|
||||||
{
|
|
||||||
type Statement = Project => Either[String, ReloadProject]
|
|
||||||
type PStatement = Statement with Positional
|
|
||||||
|
|
||||||
private def evaluateList(list: List[PStatement])(p: Project): WithProjectResult[Unit] =
|
|
||||||
list match
|
|
||||||
{
|
|
||||||
case Nil => ValueResult(())
|
|
||||||
case head :: tail =>
|
|
||||||
head(p) match
|
|
||||||
{
|
|
||||||
case Left(msg) => new ErrorResult(msg)
|
|
||||||
case Right(reload) => ContinueResult(p =>evaluateList(tail)(p), reload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def script: Parser[Project => WithProjectResult[Unit]] = rep1(space ~> statement <~ space) ^^ evaluateList
|
|
||||||
def statement: Parser[PStatement] =
|
|
||||||
positioned
|
|
||||||
{
|
|
||||||
(StartRegex ~! rep1(word) ~! "[" ~! word ~! "]") ^^
|
|
||||||
{
|
|
||||||
case start ~ command ~ open ~ result ~ close =>
|
|
||||||
val successExpected = result.toLowerCase == SuccessLiteral.toLowerCase
|
|
||||||
new Statement with Positional
|
|
||||||
{ selfPositional =>
|
|
||||||
def apply(p: Project) =
|
|
||||||
{
|
|
||||||
val result =
|
|
||||||
try
|
|
||||||
{
|
|
||||||
start match
|
|
||||||
{
|
|
||||||
case CommandStart => evaluateCommand(command, successExpected, selfPositional)(p)
|
|
||||||
case ActionStart => evaluateAction(command, successExpected)(p).toLeft(NoReload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
case e: Exception =>
|
|
||||||
log.trace(e)
|
|
||||||
Left(e.toString)
|
|
||||||
}
|
|
||||||
result.left.map(message => linePrefix(this) + message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private def linePrefix(p: Positional) = "{line " + p.pos.line + "} "
|
|
||||||
def space = """(\s+|(\#[^\n\r]*))*""".r
|
|
||||||
def word: Parser[String] = ("\'" ~> "[^'\n\r]*".r <~ "\'") | ("\"" ~> "[^\"\n\r]*".r <~ "\"") | WordRegex
|
|
||||||
def parse(scriptFile: File): Either[String, Project => WithProjectResult[Unit]] =
|
|
||||||
{
|
|
||||||
def parseReader(reader: java.io.Reader) =
|
|
||||||
parseAll(script, reader) match
|
|
||||||
{
|
|
||||||
case Success(result, next) => Right(result)
|
|
||||||
case err: NoSuccess =>
|
|
||||||
{
|
|
||||||
val pos = err.next.pos
|
|
||||||
Left("Could not parse test script '" + scriptFile.getCanonicalPath +
|
|
||||||
"' (" + pos.line + "," + pos.column + "): " + err.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FileUtilities.readValue(scriptFile, log)(parseReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def scriptError(message: String): Some[String] = Some("Test script error: " + message)
|
|
||||||
private def wrongArguments(commandName: String, args: List[String]): Some[String] =
|
|
||||||
scriptError("Command '" + commandName + "' does not accept arguments (found '" + spacedString(args) + "').")
|
|
||||||
private def wrongArguments(commandName: String, requiredArgs: String, args: List[String]): Some[String] =
|
|
||||||
scriptError("Wrong number of arguments to " + commandName + " command. " + requiredArgs + " required, found: '" + spacedString(args) + "'.")
|
|
||||||
private def evaluateCommand(command: List[String], successExpected: Boolean, position: Positional)(project: Project): Either[String, ReloadProject] =
|
|
||||||
{
|
|
||||||
command match
|
|
||||||
{
|
|
||||||
case "reload" :: Nil => Right(if(successExpected) new ReloadSuccessExpected(linePrefix(position)) else ReloadErrorExpected)
|
|
||||||
case x => evaluateCommandNoReload(x, successExpected)(project).toLeft(NoReload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private def evaluateCommandNoReload(command: List[String], successExpected: Boolean)(project: Project): Option[String] =
|
|
||||||
{
|
|
||||||
evaluate(successExpected, "Command '" + command.firstOption.getOrElse("") + "'", project)
|
|
||||||
{
|
|
||||||
command match
|
|
||||||
{
|
|
||||||
case Nil => scriptError("No command specified.")
|
|
||||||
case "touch" :: paths => touch(paths, project)
|
|
||||||
case "delete" :: paths => delete(paths, project)
|
|
||||||
case "mkdir" :: paths => makeDirectories(paths, project)
|
|
||||||
case "copy-file" :: from :: to :: Nil => copyFile(from, to, project)
|
|
||||||
case "copy-file" :: args => wrongArguments("copy-file", "Two paths", args)
|
|
||||||
case "sync" :: from :: to :: Nil => sync(from, to, project)
|
|
||||||
case "sync" :: args => wrongArguments("sync", "Two directory paths", args)
|
|
||||||
case "copy" :: paths => copy(paths, project)
|
|
||||||
case "exists" :: paths => exists(paths, project)
|
|
||||||
case "absent" :: paths => absent(paths, project)
|
|
||||||
case "pause" :: Nil => readLine("Press enter to continue. "); println(); None
|
|
||||||
case "pause" :: args => wrongArguments("pause", args)
|
|
||||||
case "newer" :: a :: b :: Nil => newer(a, b, project)
|
|
||||||
case "newer" :: args => wrongArguments("newer", "Two paths", args)
|
|
||||||
case "sleep" :: time :: Nil => trap("Error while sleeping:") { Thread.sleep(time.toLong) }
|
|
||||||
case "sleep" :: args => wrongArguments("sleep", "Time in milliseconds", args)
|
|
||||||
case "exec" :: command :: args => execute(command, args, project)
|
|
||||||
case "exec" :: other => wrongArguments("exec", "Command and arguments", other)
|
|
||||||
case "reload" :: args => wrongArguments("reload", args)
|
|
||||||
case unknown :: arguments => scriptError("Unknown command " + unknown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private def foreachBufferedLogger(project: Project)(f: BufferedLogger => Unit)
|
|
||||||
{
|
|
||||||
project.topologicalSort.foreach(p => p.log match { case buffered: BufferedLogger => f(buffered); case _ => () })
|
|
||||||
}
|
|
||||||
private def evaluate(successExpected: Boolean, label: String, project: Project)(body: => Option[String]): Option[String] =
|
|
||||||
{
|
|
||||||
def startRecordingLog() { foreachBufferedLogger(project)(_.startRecording()) }
|
|
||||||
def playLog() { foreachBufferedLogger(project)(_.playAll()) }
|
|
||||||
def stopLog() { foreachBufferedLogger(project)(_.stop()) }
|
|
||||||
|
|
||||||
startRecordingLog()
|
|
||||||
val result =
|
|
||||||
body match
|
|
||||||
{
|
|
||||||
case None =>
|
|
||||||
if(successExpected) None
|
|
||||||
else
|
|
||||||
{
|
|
||||||
playLog()
|
|
||||||
Some(label + " succeeded (expected failure).")
|
|
||||||
}
|
|
||||||
case Some(failure) =>
|
|
||||||
if(successExpected)
|
|
||||||
{
|
|
||||||
playLog()
|
|
||||||
Some(label + " failed (expected success): " + failure)
|
|
||||||
}
|
|
||||||
else None
|
|
||||||
}
|
|
||||||
stopLog()
|
|
||||||
result
|
|
||||||
}
|
|
||||||
private def evaluateAction(action: List[String], successExpected: Boolean)(project: Project): Option[String] =
|
|
||||||
{
|
|
||||||
def actionToString = action.mkString(" ")
|
|
||||||
action match
|
|
||||||
{
|
|
||||||
case Nil => scriptError("No action specified.")
|
|
||||||
case head :: Nil if project.taskNames.toSeq.contains(head)=>
|
|
||||||
evaluate(successExpected, "Action '" + actionToString + "'", project)(project.act(head))
|
|
||||||
case head :: tail =>
|
|
||||||
evaluate(successExpected, "Method '" + actionToString + "'", project)(project.call(head, tail.toArray))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private def spacedString[T](l: Seq[T]) = l.mkString(" ")
|
|
||||||
private def wrap(result: Option[String]) = result.flatMap(scriptError)
|
|
||||||
private def trap(errorPrefix: String)(action: => Unit) = wrap( Control.trapUnit(errorPrefix, log) { action; None } )
|
|
||||||
|
|
||||||
private def fromStrings(paths: List[String], project: Project) = paths.map(path => fromString(path, project))
|
|
||||||
private def fromString(path: String, project: Project) = Path.fromString(project.info.projectPath, path)
|
|
||||||
private def touch(paths: List[String], project: Project) =
|
|
||||||
if(paths.isEmpty)
|
|
||||||
scriptError("No paths specified for touch command.")
|
|
||||||
else
|
|
||||||
wrap(lazyFold(paths) { path => FileUtilities.touch(fromString(path, project), log) })
|
|
||||||
|
|
||||||
private def delete(paths: List[String], project: Project) =
|
|
||||||
if(paths.isEmpty)
|
|
||||||
scriptError("No paths specified for delete command.")
|
|
||||||
else
|
|
||||||
wrap(FileUtilities.clean(fromStrings(paths, project), true, log))
|
|
||||||
private def sync(from: String, to: String, project: Project) =
|
|
||||||
wrap(FileUtilities.sync(fromString(from, project), fromString(to, project), log))
|
|
||||||
private def copyFile(from: String, to: String, project: Project) =
|
|
||||||
wrap(FileUtilities.copyFile(fromString(from, project), fromString(to, project), log))
|
|
||||||
private def copy(paths: List[String], project: Project) =
|
|
||||||
paths match
|
|
||||||
{
|
|
||||||
case Nil => scriptError("No paths specified for copy command.")
|
|
||||||
case path :: Nil => scriptError("No destination specified for copy command.")
|
|
||||||
case _ =>
|
|
||||||
val mapped = fromStrings(paths, project).toArray
|
|
||||||
val last = mapped.length - 1
|
|
||||||
wrap(FileUtilities.copy(mapped.take(last), mapped(last), log).left.toOption)
|
|
||||||
}
|
|
||||||
private def makeDirectories(paths: List[String], project: Project) =
|
|
||||||
fromStrings(paths, project) match
|
|
||||||
{
|
|
||||||
case Nil => scriptError("No paths specified for mkdir command.")
|
|
||||||
case p => FileUtilities.createDirectories(p, project.log)
|
|
||||||
}
|
|
||||||
private def newer(a: String, b: String, project: Project) =
|
|
||||||
trap("Error testing if '" + a + "' is newer than '" + b + "'")
|
|
||||||
{
|
|
||||||
val pathA = fromString(a, project)
|
|
||||||
val pathB = fromString(b, project)
|
|
||||||
pathA.exists && (!pathB.exists || pathA.lastModified > pathB.lastModified)
|
|
||||||
}
|
|
||||||
private def exists(paths: List[String], project: Project) =
|
|
||||||
fromStrings(paths, project).filter(!_.exists) match
|
|
||||||
{
|
|
||||||
case Nil => None
|
|
||||||
case x => Some("File(s) did not exist: " + x.mkString("[ ", " , ", " ]"))
|
|
||||||
}
|
|
||||||
private def absent(paths: List[String], project: Project) =
|
|
||||||
fromStrings(paths, project).filter(_.exists) match
|
|
||||||
{
|
|
||||||
case Nil => None
|
|
||||||
case x => Some("File(s) existed: " + x.mkString("[ ", " , ", " ]"))
|
|
||||||
}
|
|
||||||
private def execute(command: String, args: List[String], project: Project) =
|
|
||||||
{
|
|
||||||
if(command.trim.isEmpty)
|
|
||||||
Some("Command was empty.")
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Control.trapUnit("Error running command: ", project.log)
|
|
||||||
{
|
|
||||||
val builder = new java.lang.ProcessBuilder((command :: args).toArray : _*).directory(project.info.projectDirectory)
|
|
||||||
val exitValue = Process(builder) ! log
|
|
||||||
if(exitValue == 0)
|
|
||||||
None
|
|
||||||
else
|
|
||||||
Some("Nonzero exit value (" + exitValue + ")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private object TestScriptParser
|
|
||||||
{
|
|
||||||
val SuccessLiteral = "success"
|
|
||||||
val Failure = "error"
|
|
||||||
val CommandStart = "$"
|
|
||||||
val ActionStart = ">"
|
|
||||||
val WordRegex = """[^ \[\]\s'\"][^ \[\]\s]*""".r
|
|
||||||
val StartRegex = ("[" + CommandStart + ActionStart + "]").r
|
|
||||||
|
|
||||||
final def lazyFold[T](list: List[T])(f: T => Option[String]): Option[String] =
|
|
||||||
list match
|
|
||||||
{
|
|
||||||
case Nil => None
|
|
||||||
case head :: tail =>
|
|
||||||
f(head) match
|
|
||||||
{
|
|
||||||
case None => lazyFold(tail)(f)
|
|
||||||
case x => x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import sbt._
|
||||||
|
|
||||||
|
class Plugins(info: ProjectInfo) extends PluginDefinition(info)
|
||||||
|
{
|
||||||
|
val scripted = "org.scala-tools.sbt" % "test" % "0.5.1"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue