mirror of https://github.com/sbt/sbt.git
* Integrate Josh's test arguments patch
* Use ScalaTest arguments example to test test-interface
This commit is contained in:
parent
328d862f70
commit
d06df9a2fe
|
|
@ -11,7 +11,7 @@ final class SbtHandler(directory: File, log: Logger, server: IPC.Server) extends
|
|||
def initialState = newRemote
|
||||
def apply(command: String, arguments: List[String], p: Process): Process =
|
||||
{
|
||||
send((command :: arguments).mkString(" "))
|
||||
send((command :: arguments.map(escape)).mkString(" "))
|
||||
receive(command + " failed")
|
||||
p
|
||||
}
|
||||
|
|
@ -38,5 +38,9 @@ final class SbtHandler(directory: File, log: Logger, server: IPC.Server) extends
|
|||
catch { case e: java.net.SocketException => error("Remote sbt initialization failed") }
|
||||
p
|
||||
}
|
||||
// if the argument contains spaces, enclose it in quotes, quoting backslashes and quotes
|
||||
def escape(argument: String) =
|
||||
if(argument.contains(" ")) "\"" + argument.replaceAll(q("""\"""), """\\""").replaceAll(q("\""), "\\\"") + "\"" else argument
|
||||
def q(s: String) = java.util.regex.Pattern.quote(s)
|
||||
// Process("java" :: "-classpath" :: classpath.map(_.getAbsolutePath).mkString(File.pathSeparator) :: "xsbt.boot.Boot" :: ( "<" + server.port) :: Nil) run log
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,16 +267,16 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
protected def docAction = scaladocTask(mainLabel, mainSources, mainDocPath, docClasspath, documentOptions).dependsOn(compile) describedAs DocDescription
|
||||
protected def docTestAction = scaladocTask(testLabel, testSources, testDocPath, docClasspath, documentOptions).dependsOn(testCompile) describedAs TestDocDescription
|
||||
|
||||
protected def testAction = defaultTestTask(Nil, testOptions)
|
||||
protected def testOnlyAction = testQuickMethod(testCompileConditional.analysis, testOptions)((testArgs, options) => {
|
||||
defaultTestTask(testArgs, options)
|
||||
}) describedAs(TestOnlyDescription)
|
||||
protected def testQuickAction = defaultTestQuickMethod(false) describedAs(TestQuickDescription)
|
||||
protected def testFailedAction = defaultTestQuickMethod(true) describedAs(TestFailedDescription)
|
||||
protected def defaultTestQuickMethod(failedOnly: Boolean) =
|
||||
testQuickMethod(testCompileConditional.analysis, testOptions)((args, options) => defaultTestTask(args, quickOptions(failedOnly) ::: options.toList))
|
||||
protected def defaultTestTask(testArgs: Seq[String], testOptions: => Seq[TestOption]) =
|
||||
testTask(testFrameworks, testClasspath, testCompileConditional.analysis, testArgs, testOptions).dependsOn(testCompile, copyResources, copyTestResources) describedAs TestDescription
|
||||
protected def testAction = defaultTestTask(testOptions)
|
||||
protected def testOnlyAction = testQuickMethod(testCompileConditional.analysis, testOptions)((options) => {
|
||||
defaultTestTask(options)
|
||||
}) describedAs (TestOnlyDescription)
|
||||
protected def testQuickAction = defaultTestQuickMethod(false) describedAs (TestQuickDescription)
|
||||
protected def testFailedAction = defaultTestQuickMethod(true) describedAs (TestFailedDescription)
|
||||
protected def defaultTestQuickMethod(failedOnly: Boolean) =
|
||||
testQuickMethod(testCompileConditional.analysis, testOptions)(options => defaultTestTask(quickOptions(failedOnly) ::: options.toList))
|
||||
protected def defaultTestTask(testOptions: => Seq[TestOption]) =
|
||||
testTask(testFrameworks, testClasspath, testCompileConditional.analysis, testOptions).dependsOn(testCompile, copyResources, copyTestResources) describedAs TestDescription
|
||||
|
||||
override def packageToPublishActions: Seq[ManagedTask] = `package` :: Nil
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ trait IntegrationTesting extends NotNull
|
|||
trait ScalaIntegrationTesting extends IntegrationTesting
|
||||
{ self: ScalaProject =>
|
||||
|
||||
protected def integrationTestTask(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, testArgs: Seq[String], options: => Seq[TestOption]) =
|
||||
testTask(frameworks, classpath, analysis, testArgs, options)
|
||||
protected def integrationTestTask(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, options: => Seq[TestOption]) =
|
||||
testTask(frameworks, classpath, analysis, options)
|
||||
}
|
||||
|
||||
trait BasicScalaIntegrationTesting extends BasicIntegrationTesting with MavenStyleIntegrationTestPaths
|
||||
|
|
@ -34,7 +34,7 @@ trait BasicIntegrationTesting extends ScalaIntegrationTesting with IntegrationTe
|
|||
|
||||
val integrationTestCompileConditional = new CompileConditional(integrationTestCompileConfiguration, buildCompiler)
|
||||
|
||||
protected def integrationTestAction = integrationTestTask(integrationTestFrameworks, integrationTestClasspath, integrationTestCompileConditional.analysis, Nil, integrationTestOptions) dependsOn integrationTestCompile describedAs IntegrationTestCompileDescription
|
||||
protected def integrationTestAction = integrationTestTask(integrationTestFrameworks, integrationTestClasspath, integrationTestCompileConditional.analysis, integrationTestOptions) dependsOn integrationTestCompile describedAs IntegrationTestCompileDescription
|
||||
protected def integrationTestCompileAction = integrationTestCompileTask() dependsOn compile describedAs IntegrationTestDescription
|
||||
|
||||
protected def integrationTestCompileTask() = task{ integrationTestCompileConditional.run }
|
||||
|
|
|
|||
|
|
@ -67,7 +67,14 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
case class ExcludeTests(tests: Iterable[String]) extends TestOption
|
||||
case class TestListeners(listeners: Iterable[TestReportListener]) extends TestOption
|
||||
case class TestFilter(filterTest: String => Boolean) extends TestOption
|
||||
case class TestFrameworkArgument(arg: String) extends TestOption
|
||||
|
||||
// args for all frameworks
|
||||
def TestArgument(args: String*): TestArgument = TestArgument(None, args.toList)
|
||||
// args for a particular test framework
|
||||
def TestArgument(tf: TestFramework, args: String*): TestArgument = TestArgument(Some(tf), args.toList)
|
||||
|
||||
// None means apply to all, Some(tf) means apply to a particular framework only.
|
||||
case class TestArgument(framework: Option[TestFramework], args: List[String]) extends TestOption
|
||||
|
||||
case class JarManifest(m: Manifest) extends PackageOption
|
||||
{
|
||||
|
|
@ -150,14 +157,13 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
def copyTask(sources: PathFinder, destinationDirectory: Path): Task =
|
||||
task { FileUtilities.copy(sources.get, destinationDirectory, log).left.toOption }
|
||||
|
||||
def testTask(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, testArgs: Seq[String], options: TestOption*): Task =
|
||||
testTask(frameworks, classpath, analysis, testArgs, options)
|
||||
def testTask(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis,
|
||||
testArgs: Seq[String], options: => Seq[TestOption]): Task =
|
||||
def testTask(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, options: TestOption*): Task =
|
||||
testTask(frameworks, classpath, analysis, options)
|
||||
def testTask(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, options: => Seq[TestOption]): Task =
|
||||
{
|
||||
def work =
|
||||
{
|
||||
val (begin, work, end) = testTasks(frameworks, classpath, analysis, testArgs, options)
|
||||
val (begin, work, end) = testTasks(frameworks, classpath, analysis, options)
|
||||
val beginTasks = begin.map(toTask).toSeq // test setup tasks
|
||||
val workTasks = work.map(w => toTask(w) dependsOn(beginTasks : _*)) // the actual tests
|
||||
val endTasks = end.map(toTask).toSeq // tasks that perform test cleanup and are run regardless of success of tests
|
||||
|
|
@ -251,43 +257,58 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
}
|
||||
}
|
||||
protected def incrementImpl(v: BasicVersion): Version = v.incrementMicro
|
||||
protected def testTasks(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, testArgs: Seq[String],
|
||||
options: => Seq[TestOption]) = {
|
||||
protected def testTasks(frameworks: Seq[TestFramework], classpath: PathFinder, analysis: CompileAnalysis, options: => Seq[TestOption]) =
|
||||
{
|
||||
import scala.collection.mutable.HashSet
|
||||
import scala.collection.mutable.Map
|
||||
|
||||
val testFilters = new ListBuffer[String => Boolean]
|
||||
val excludeTestsSet = new HashSet[String]
|
||||
val setup, cleanup = new ListBuffer[() => Option[String]]
|
||||
val testListeners = new ListBuffer[TestReportListener]
|
||||
val testFilters = new ListBuffer[String => Boolean]
|
||||
val excludeTestsSet = new HashSet[String]
|
||||
val setup, cleanup = new ListBuffer[() => Option[String]]
|
||||
val testListeners = new ListBuffer[TestReportListener]
|
||||
val testArgsByFramework = Map[TestFramework, ListBuffer[String]]()
|
||||
def frameworkArgs(framework: TestFramework): ListBuffer[String] =
|
||||
testArgsByFramework.getOrElseUpdate(framework, new ListBuffer[String])
|
||||
|
||||
for(option <- options)
|
||||
for(option <- options)
|
||||
{
|
||||
option match
|
||||
{
|
||||
option match
|
||||
{
|
||||
case TestFilter(include) => testFilters += include
|
||||
case ExcludeTests(exclude) => excludeTestsSet ++= exclude
|
||||
case TestListeners(listeners) => testListeners ++= listeners
|
||||
case TestSetup(setupFunction) => setup += setupFunction
|
||||
case TestCleanup(cleanupFunction) => cleanup += cleanupFunction
|
||||
}
|
||||
case TestFilter(include) => testFilters += include
|
||||
case ExcludeTests(exclude) => excludeTestsSet ++= exclude
|
||||
case TestListeners(listeners) => testListeners ++= listeners
|
||||
case TestSetup(setupFunction) => setup += setupFunction
|
||||
case TestCleanup(cleanupFunction) => cleanup += cleanupFunction
|
||||
/**
|
||||
* There are two cases here.
|
||||
* The first handles TestArguments in the project file, which
|
||||
* might have a TestFramework specified.
|
||||
* The second handles arguments to be applied to all test frameworks.
|
||||
* -- arguments from the project file that didnt have a framework specified
|
||||
* -- command line arguments (ex: test-only someClass -- someArg)
|
||||
* (currently, command line args must be passed to all frameworks)
|
||||
*/
|
||||
case TestArgument(Some(framework), args) => frameworkArgs(framework) ++= args
|
||||
case TestArgument(None, args) => frameworks.foreach { framework => frameworkArgs(framework) ++= args.toList }
|
||||
}
|
||||
}
|
||||
|
||||
if(excludeTestsSet.size > 0 && log.atLevel(Level.Debug))
|
||||
{
|
||||
log.debug("Excluding tests: ")
|
||||
excludeTestsSet.foreach(test => log.debug("\t" + test))
|
||||
}
|
||||
def includeTest(test: TestDefinition) = !excludeTestsSet.contains(test.testClassName) && testFilters.forall(filter => filter(test.testClassName))
|
||||
val tests = HashSet.empty[TestDefinition] ++ analysis.allTests.filter(includeTest)
|
||||
TestFramework.testTasks(frameworks, classpath.get, buildScalaInstance.loader, tests.toSeq, log, testListeners.readOnly, false, setup.readOnly, cleanup.readOnly, testArgs)
|
||||
if(excludeTestsSet.size > 0 && log.atLevel(Level.Debug))
|
||||
{
|
||||
log.debug("Excluding tests: ")
|
||||
excludeTestsSet.foreach(test => log.debug("\t" + test))
|
||||
}
|
||||
def includeTest(test: TestDefinition) = !excludeTestsSet.contains(test.testClassName) && testFilters.forall(filter => filter(test.testClassName))
|
||||
val tests = HashSet.empty[TestDefinition] ++ analysis.allTests.filter(includeTest)
|
||||
TestFramework.testTasks(frameworks, classpath.get, buildScalaInstance.loader, tests.toSeq, log,
|
||||
testListeners.readOnly, false, setup.readOnly, cleanup.readOnly, testArgsByFramework)
|
||||
}
|
||||
private def flatten[T](i: Iterable[Iterable[T]]) = i.flatMap(x => x)
|
||||
|
||||
protected def testQuickMethod(testAnalysis: CompileAnalysis, options: => Seq[TestOption])(toRun: (Seq[String],Seq[TestOption]) => Task) = {
|
||||
protected def testQuickMethod(testAnalysis: CompileAnalysis, options: => Seq[TestOption])(toRun: (Seq[TestOption]) => Task) = {
|
||||
val analysis = testAnalysis.allTests.map(_.testClassName).toList
|
||||
multiTask(analysis) { (args,includeFunction) => {
|
||||
toRun(args, TestFilter(includeFunction) :: options.toList)
|
||||
}
|
||||
multiTask(analysis) { (args, includeFunction) =>
|
||||
toRun(TestArgument(args:_*) :: TestFilter(includeFunction) :: options.toList)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +372,7 @@ object ScalaProject
|
|||
}
|
||||
trait MultiTaskProject extends Project
|
||||
{
|
||||
def multiTask(allTests: => List[String])(run: (List[String], (String => Boolean)) => Task): MethodTask = {
|
||||
def multiTask(allTests: => List[String])(run: (Seq[String], String => Boolean) => Task): MethodTask = {
|
||||
|
||||
task { tests =>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq
|
|||
private[this] val delegate = framework.testRunner(loader, listeners.flatMap(_.contentLogger).toArray)
|
||||
final def run(testDefinition: TestDefinition, args: Seq[String]): Result.Value =
|
||||
{
|
||||
log.debug("Running " + testDefinition + " with arguments " + args.mkString(", "))
|
||||
val testClass = testDefinition.testClassName
|
||||
def runTest() =
|
||||
{
|
||||
|
|
@ -87,28 +88,34 @@ object TestFramework
|
|||
private[sbt] def safeForeach[T](it: Iterable[T], log: Logger)(f: T => Unit): Unit =
|
||||
it.foreach(i => Control.trapAndLog(log){ f(i) } )
|
||||
|
||||
import scala.collection.{Map, Set}
|
||||
|
||||
import scala.collection.{immutable, Map, Set}
|
||||
|
||||
def testTasks(frameworks: Seq[TestFramework],
|
||||
classpath: Iterable[Path],
|
||||
scalaLoader: ClassLoader,
|
||||
tests: Seq[TestDefinition],
|
||||
log: Logger,
|
||||
listeners: Seq[TestReportListener],
|
||||
endErrorsEnabled: Boolean,
|
||||
setup: Iterable[() => Option[String]],
|
||||
cleanup: Iterable[() => Option[String]],
|
||||
testArgs: Seq[String]): (Iterable[NamedTestTask], Iterable[NamedTestTask], Iterable[NamedTestTask]) =
|
||||
classpath: Iterable[Path],
|
||||
scalaLoader: ClassLoader,
|
||||
tests: Seq[TestDefinition],
|
||||
log: Logger,
|
||||
listeners: Seq[TestReportListener],
|
||||
endErrorsEnabled: Boolean,
|
||||
setup: Iterable[() => Option[String]],
|
||||
cleanup: Iterable[() => Option[String]],
|
||||
testArgsByFramework: Map[TestFramework, Seq[String]]):
|
||||
(Iterable[NamedTestTask], Iterable[NamedTestTask], Iterable[NamedTestTask]) =
|
||||
{
|
||||
val loader = createTestLoader(classpath, scalaLoader)
|
||||
val rawFrameworks = frameworks.flatMap(_.create(loader, log))
|
||||
val mappedTests = testMap(rawFrameworks, tests)
|
||||
val arguments = immutable.Map() ++
|
||||
( for(framework <- frameworks; created <- framework.create(loader, log)) yield
|
||||
(created, testArgsByFramework.getOrElse(framework, Nil)) )
|
||||
|
||||
val mappedTests = testMap(arguments.keys.toList, tests, arguments)
|
||||
if(mappedTests.isEmpty)
|
||||
(new NamedTestTask(TestStartName, None) :: Nil, Nil, new NamedTestTask(TestFinishName, { log.info("No tests to run."); None }) :: Nil )
|
||||
else
|
||||
createTestTasks(loader, mappedTests, log, listeners, endErrorsEnabled, setup, cleanup, testArgs)
|
||||
createTestTasks(loader, mappedTests, log, listeners, endErrorsEnabled, setup, cleanup)
|
||||
}
|
||||
private def testMap(frameworks: Seq[Framework], tests: Seq[TestDefinition]): Map[Framework, Set[TestDefinition]] =
|
||||
|
||||
private def testMap(frameworks: Seq[Framework], tests: Seq[TestDefinition], args: Map[Framework, Seq[String]]):
|
||||
immutable.Map[Framework, (Set[TestDefinition], Seq[String])] =
|
||||
{
|
||||
import scala.collection.mutable.{HashMap, HashSet, Set}
|
||||
val map = new HashMap[Framework, Set[TestDefinition]]
|
||||
|
|
@ -127,14 +134,14 @@ object TestFramework
|
|||
}
|
||||
if(!frameworks.isEmpty)
|
||||
assignTests()
|
||||
wrap.Wrappers.readOnly(map)
|
||||
(immutable.Map() ++ map) transform { (framework, tests) => (tests, args(framework)) }
|
||||
}
|
||||
private def createTasks(work: Iterable[() => Option[String]], baseName: String) =
|
||||
work.toList.zipWithIndex.map{ case (work, index) => new NamedTestTask(baseName + " " + (index+1), work()) }
|
||||
|
||||
private def createTestTasks(loader: ClassLoader, tests: Map[Framework, Set[TestDefinition]], log: Logger,
|
||||
private def createTestTasks(loader: ClassLoader, tests: Map[Framework, (Set[TestDefinition], Seq[String])], log: Logger,
|
||||
listeners: Seq[TestReportListener], endErrorsEnabled: Boolean, setup: Iterable[() => Option[String]],
|
||||
cleanup: Iterable[() => Option[String]], testArgs: Seq[String]) =
|
||||
cleanup: Iterable[() => Option[String]]) =
|
||||
{
|
||||
val testsListeners = listeners.filter(_.isInstanceOf[TestsListener]).map(_.asInstanceOf[TestsListener])
|
||||
def foreachListenerSafe(f: TestsListener => Unit): Unit = safeForeach(testsListeners, log)(f)
|
||||
|
|
@ -148,7 +155,7 @@ object TestFramework
|
|||
}
|
||||
val startTask = new NamedTestTask(TestStartName, {foreachListenerSafe(_.doInit); None}) :: createTasks(setup, "Test setup")
|
||||
val testTasks =
|
||||
tests flatMap { case (framework, testDefinitions) =>
|
||||
tests flatMap { case (framework, (testDefinitions, testArgs)) =>
|
||||
|
||||
val runner = new TestRunner(framework, loader, listeners, log)
|
||||
for(testDefinition <- testDefinitions) yield
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
project.name=Test Arguments Test
|
||||
project.version=1.0
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import sbt._
|
||||
|
||||
class ArgumentTest(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
val snap = ScalaToolsSnapshots
|
||||
val st = "org.scalatest" % "scalatest" % "1.0.1-for-scala-2.8.0.Beta1-RC7-with-test-interfaces-0.3-SNAPSHOT"
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
import org.scalatest.fixture.FixtureFunSuite
|
||||
import org.scalatest.Tag
|
||||
|
||||
class ArgumentTest extends FixtureFunSuite{
|
||||
type FixtureParam = Map[String,Any]
|
||||
override def withFixture(test: OneArgTest) {
|
||||
test(test.configMap)
|
||||
}
|
||||
test("1", Tag("test1")){ conf => error("error #1") }
|
||||
test("2", Tag("test2")){ conf => () }
|
||||
test("3", Tag("test3")){ conf => () }
|
||||
test("4", Tag("test4")){ conf => error("error #4") }
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
> ++2.8.0.Beta1-RC7
|
||||
> update
|
||||
|
||||
# should fail because it should run all tests, some of which are expected to fail (1 and 4)
|
||||
-> test-only ArgumentTest
|
||||
|
||||
# should fail because it should run the test tagged 'test1', which should fail
|
||||
-> test-only ArgumentTest -- -n test1
|
||||
|
||||
# should succeed because it should only run the test tagged 'test2', which should succeed
|
||||
> test-only ArgumentTest -- -n test2
|
||||
|
||||
# should succeed because it should only run the test tagged 'test3', which should succeed
|
||||
> test-only ArgumentTest -- -n test3
|
||||
|
||||
# should fail because it should run the test tagged 'test4', which should fail
|
||||
-> test-only ArgumentTest -- -n test4
|
||||
|
||||
# should succeed because it should only run the tests tagged 'test2' or 'test3', both of which should succeed
|
||||
> test-only ArgumentTest -- -n "test2 test3"
|
||||
|
||||
# these should fail because they run at least one failed test
|
||||
-> test-only ArgumentTest -- -n "test2 test4"
|
||||
-> test-only ArgumentTest -- -n "test1 test2 test3"
|
||||
-> test-only ArgumentTest -- -n "test2 test3 test4"
|
||||
-> test-only ArgumentTest -- -n "test1 test2 test3 test4"
|
||||
-> test-only ArgumentTest -- -n "test1 test3"
|
||||
Loading…
Reference in New Issue