mirror of https://github.com/sbt/sbt.git
Merge remote-tracking branch 'scalatest/new-framework-api-12' into 0.13
This commit is contained in:
commit
f7ce8334c3
|
|
@ -0,0 +1,7 @@
|
|||
# Set default behaviour, in case users don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files we want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.scala text
|
||||
*.java text
|
||||
|
|
@ -4,14 +4,14 @@
|
|||
package sbt
|
||||
|
||||
import scala.collection.mutable
|
||||
import org.scalatools.testing._
|
||||
import testing._
|
||||
import java.net.ServerSocket
|
||||
import java.io._
|
||||
import Tests.{Output => TestOutput, _}
|
||||
import ForkMain._
|
||||
|
||||
private[sbt] object ForkTests {
|
||||
def apply(frameworks: Seq[TestFramework], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = {
|
||||
def apply(runners: Map[TestFramework, Runner], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = {
|
||||
val opts = config.options.toList
|
||||
val listeners = opts flatMap {
|
||||
case Listeners(ls) => ls
|
||||
|
|
@ -25,19 +25,13 @@ private[sbt] object ForkTests {
|
|||
case Filter(f) => Some(f)
|
||||
case _ => None
|
||||
}
|
||||
val argMap = frameworks.map {
|
||||
f => f.implClassName -> opts.flatMap {
|
||||
case Argument(None | Some(`f`), args) => args
|
||||
case _ => Nil
|
||||
}
|
||||
}.toMap
|
||||
|
||||
std.TaskExtra.task {
|
||||
if (!tests.isEmpty) {
|
||||
val server = new ServerSocket(0)
|
||||
object Acceptor extends Runnable {
|
||||
val resultsAcc = mutable.Map.empty[String, TestResult.Value]
|
||||
lazy val result = (overall(resultsAcc.values), resultsAcc.toMap)
|
||||
val resultsAcc = mutable.Map.empty[String, SuiteResult]
|
||||
lazy val result = TestOutput(overall(resultsAcc.values.map(_.result)), resultsAcc.toMap, Iterable.empty)
|
||||
def run: Unit = {
|
||||
val socket =
|
||||
try {
|
||||
|
|
@ -56,10 +50,12 @@ private[sbt] object ForkTests {
|
|||
}.toArray
|
||||
os.writeObject(testsFiltered)
|
||||
|
||||
os.writeInt(frameworks.size)
|
||||
for ((clazz, args) <- argMap) {
|
||||
os.writeObject(clazz)
|
||||
os.writeObject(args.toArray)
|
||||
os.writeInt(runners.size)
|
||||
for ((testFramework, mainRunner) <- runners) {
|
||||
val remoteArgs = mainRunner.remoteArgs()
|
||||
os.writeObject(testFramework.implClassNames.toArray)
|
||||
os.writeObject(mainRunner.args)
|
||||
os.writeObject(remoteArgs)
|
||||
}
|
||||
os.flush()
|
||||
|
||||
|
|
@ -72,27 +68,32 @@ private[sbt] object ForkTests {
|
|||
|
||||
try {
|
||||
testListeners.foreach(_.doInit())
|
||||
new Thread(Acceptor).start()
|
||||
val acceptorThread = new Thread(Acceptor)
|
||||
acceptorThread.start()
|
||||
|
||||
val fullCp = classpath ++: Seq(IO.classLocationFile[ForkMain], IO.classLocationFile[Framework])
|
||||
val options = Seq("-classpath", fullCp mkString File.pathSeparator, classOf[ForkMain].getCanonicalName, server.getLocalPort.toString)
|
||||
val ec = Fork.java(fork, options)
|
||||
val result =
|
||||
if (ec != 0)
|
||||
(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> TestResult.Error))
|
||||
else
|
||||
TestOutput(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error), Iterable.empty)
|
||||
else {
|
||||
// Need to wait acceptor thread to finish its business
|
||||
acceptorThread.join()
|
||||
Acceptor.result
|
||||
testListeners.foreach(_.doComplete(result._1))
|
||||
}
|
||||
|
||||
testListeners.foreach(_.doComplete(result.overall))
|
||||
result
|
||||
} finally {
|
||||
server.close()
|
||||
}
|
||||
} else
|
||||
(TestResult.Passed, Map.empty[String, TestResult.Value])
|
||||
TestOutput(TestResult.Passed, Map.empty[String, SuiteResult], Iterable.empty)
|
||||
} tagw (config.tags: _*)
|
||||
}
|
||||
}
|
||||
private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Logger, listeners: Seq[TestReportListener], results: mutable.Map[String, TestResult.Value])
|
||||
private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Logger, listeners: Seq[TestReportListener], results: mutable.Map[String, SuiteResult])
|
||||
{
|
||||
import ForkTags._
|
||||
@annotation.tailrec def react(): Unit = is.readObject match {
|
||||
|
|
@ -106,9 +107,9 @@ private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Lo
|
|||
listeners.foreach(_ startGroup group)
|
||||
val event = TestEvent(tEvents)
|
||||
listeners.foreach(_ testEvent event)
|
||||
val result = event.result getOrElse TestResult.Passed
|
||||
results += group -> result
|
||||
listeners.foreach(_ endGroup (group, result))
|
||||
val suiteResult = SuiteResult(tEvents)
|
||||
results += group -> suiteResult
|
||||
listeners.foreach(_ endGroup (group, suiteResult.result))
|
||||
react()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ package sbt
|
|||
import xsbti.api.Definition
|
||||
import ConcurrentRestrictions.Tag
|
||||
|
||||
import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint}
|
||||
import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner, Task => TestTask}
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -19,7 +20,8 @@ sealed trait TestOption
|
|||
object Tests
|
||||
{
|
||||
// (overall result, individual results)
|
||||
type Output = (TestResult.Value, Map[String,TestResult.Value])
|
||||
final case class Output(overall: TestResult.Value, events: Map[String,SuiteResult], summaries: Iterable[Summary])
|
||||
final case class Summary(name: String, summaryText: String)
|
||||
|
||||
final case class Setup(setup: ClassLoader => Unit) extends TestOption
|
||||
def Setup(setup: () => Unit) = new Setup(_ => setup())
|
||||
|
|
@ -43,7 +45,7 @@ object Tests
|
|||
|
||||
final case class Execution(options: Seq[TestOption], parallel: Boolean, tags: Seq[(Tag, Int)])
|
||||
|
||||
def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, discovered: Seq[TestDefinition], config: Execution, log: Logger): Task[Output] =
|
||||
def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, runners: Map[TestFramework, Runner], discovered: Seq[TestDefinition], config: Execution, log: Logger): Task[Output] =
|
||||
{
|
||||
import collection.mutable.{HashSet, ListBuffer, Map, Set}
|
||||
val testFilters = new ListBuffer[String => Boolean]
|
||||
|
|
@ -58,7 +60,7 @@ object Tests
|
|||
def frameworkArguments(framework: TestFramework, args: Seq[String]): Unit =
|
||||
(frameworks get framework) match {
|
||||
case Some(f) => frameworkArgs(f, args)
|
||||
case None => undefinedFrameworks += framework.implClassName
|
||||
case None => undefinedFrameworks ++= framework.implClassNames
|
||||
}
|
||||
|
||||
for(option <- config.options)
|
||||
|
|
@ -94,10 +96,10 @@ object Tests
|
|||
val filtered0 = discovered.filter(includeTest).toList.distinct
|
||||
val tests = if(orderedFilters.isEmpty) filtered0 else orderedFilters.flatMap(f => filtered0.filter(d => f(d.name))).toList.distinct
|
||||
val arguments = testArgsByFramework.map { case (k,v) => (k, v.toList) } toMap;
|
||||
testTask(frameworks.values.toSeq, testLoader, tests, setup.readOnly, cleanup.readOnly, log, testListeners.readOnly, arguments, config)
|
||||
testTask(testLoader, frameworks, runners, tests, setup.readOnly, cleanup.readOnly, log, testListeners.readOnly, arguments, config)
|
||||
}
|
||||
|
||||
def testTask(frameworks: Seq[Framework], loader: ClassLoader, tests: Seq[TestDefinition],
|
||||
def testTask(loader: ClassLoader, frameworks: Map[TestFramework, Framework], runners: Map[TestFramework, Runner], tests: Seq[TestDefinition],
|
||||
userSetup: Iterable[ClassLoader => Unit], userCleanup: Iterable[ClassLoader => Unit],
|
||||
log: Logger, testListeners: Seq[TestReportListener], arguments: Map[Framework, Seq[String]], config: Execution): Task[Output] =
|
||||
{
|
||||
|
|
@ -105,32 +107,68 @@ object Tests
|
|||
def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map {a => () => a(loader) }
|
||||
|
||||
val (frameworkSetup, runnables, frameworkCleanup) =
|
||||
TestFramework.testTasks(frameworks, loader, tests, log, testListeners, arguments)
|
||||
TestFramework.testTasks(frameworks, runners, loader, tests, log, testListeners, arguments)
|
||||
|
||||
val setupTasks = fj(partApp(userSetup) :+ frameworkSetup)
|
||||
val mainTasks =
|
||||
if(config.parallel)
|
||||
makeParallel(runnables, setupTasks, config.tags).toSeq.join
|
||||
makeParallel(loader, runnables, setupTasks, config.tags)//.toSeq.join
|
||||
else
|
||||
makeSerial(runnables, setupTasks, config.tags)
|
||||
makeSerial(loader, runnables, setupTasks, config.tags)
|
||||
val taggedMainTasks = mainTasks.tagw(config.tags : _*)
|
||||
taggedMainTasks map processResults flatMap { results =>
|
||||
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results._1))
|
||||
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results.overall))
|
||||
cleanupTasks map { _ => results }
|
||||
}
|
||||
}
|
||||
type TestRunnable = (String, () => TestResult.Value)
|
||||
def makeParallel(runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]) =
|
||||
runnables map { case (name, test) => task { (name, test()) } tagw(tags : _*) dependsOn setupTasks named name }
|
||||
def makeSerial(runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]) =
|
||||
task { runnables map { case (name, test) => (name, test()) } } dependsOn(setupTasks)
|
||||
type TestRunnable = (String, TestFunction)
|
||||
|
||||
private def createNestedRunnables(name: String, loader: ClassLoader, testFun: TestFunction, nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] =
|
||||
nestedTasks.view.zipWithIndex map { case (nt, idx) =>
|
||||
(name, TestFramework.createTestFunction(loader, new TestDefinition(testFun.testDefinition.name + "-" + idx, testFun.testDefinition.fingerprint), testFun.runner, nt))
|
||||
}
|
||||
|
||||
def processResults(results: Iterable[(String, TestResult.Value)]): (TestResult.Value, Map[String, TestResult.Value]) =
|
||||
(overall(results.map(_._2)), results.toMap)
|
||||
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
|
||||
def makeParallel(loader: ClassLoader, runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]): Task[Map[String,SuiteResult]] =
|
||||
toTasks(loader, runnables.toSeq, tags).dependsOn(setupTasks)
|
||||
|
||||
def toTasks(loader: ClassLoader, runnables: Seq[TestRunnable], tags: Seq[(Tag,Int)]): Task[Map[String, SuiteResult]] = {
|
||||
val tasks = runnables.map { case (name, test) => toTask(loader, name, test, tags) }
|
||||
tasks.join.map( _.foldLeft(Map.empty[String, SuiteResult]) { case (sum, e) =>
|
||||
sum ++ e
|
||||
} )
|
||||
}
|
||||
|
||||
def toTask(loader: ClassLoader, name: String, fun: TestFunction, tags: Seq[(Tag,Int)]): Task[Map[String, SuiteResult]] = {
|
||||
val base = task { (name, fun.apply()) }
|
||||
val taggedBase = base.tagw(tags : _*).tag(fun.tags.map(ConcurrentRestrictions.Tag(_)) : _*)
|
||||
taggedBase flatMap { case (name, (result, nested)) =>
|
||||
val nestedRunnables = createNestedRunnables(fun.testDefinition.name, loader, fun, nested)
|
||||
toTasks(loader, nestedRunnables, tags).map( _.updated(name, result) )
|
||||
}
|
||||
}
|
||||
|
||||
def makeSerial(loader: ClassLoader, runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]): Task[List[(String, SuiteResult)]] =
|
||||
{
|
||||
@tailrec
|
||||
def processRunnable(runnableList: List[TestRunnable], acc: List[(String, SuiteResult)]): List[(String, SuiteResult)] =
|
||||
runnableList match {
|
||||
case hd :: rst =>
|
||||
val testFun = hd._2
|
||||
val (result, nestedTasks) = testFun.apply()
|
||||
val nestedRunnables = createNestedRunnables(testFun.testDefinition.name, loader, testFun, nestedTasks)
|
||||
processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc)
|
||||
case Nil => acc
|
||||
}
|
||||
|
||||
task { processRunnable(runnables.toList, List.empty) } dependsOn(setupTasks)
|
||||
}
|
||||
|
||||
def processResults(results: Iterable[(String, SuiteResult)]): Output =
|
||||
Output(overall(results.map(_._2.result)), results.toMap, Iterable.empty)
|
||||
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
|
||||
if (parallel)
|
||||
reduced(results.toIndexedSeq, {
|
||||
case ((v1, m1), (v2, m2)) => (if (v1.id < v2.id) v2 else v1, m1 ++ m2)
|
||||
case (Output(v1, m1, _), Output(v2, m2, _)) => Output(if (v1.id < v2.id) v2 else v1, m1 ++ m2, Iterable.empty)
|
||||
})
|
||||
else {
|
||||
def sequence(tasks: List[Task[Output]], acc: List[Output]): Task[List[Output]] = tasks match {
|
||||
|
|
@ -138,19 +176,19 @@ object Tests
|
|||
case hd::tl => hd flatMap { out => sequence(tl, out::acc) }
|
||||
}
|
||||
sequence(results.toList, List()) map { ress =>
|
||||
val (rs, ms) = ress.unzip
|
||||
(overall(rs), ms reduce (_ ++ _))
|
||||
val (rs, ms) = ress.unzip { e => (e.overall, e.events) }
|
||||
Output(overall(rs), ms reduce (_ ++ _), Iterable.empty)
|
||||
}
|
||||
}
|
||||
def overall(results: Iterable[TestResult.Value]): TestResult.Value =
|
||||
(TestResult.Passed /: results) { (acc, result) => if(acc.id < result.id) result else acc }
|
||||
def discover(frameworks: Seq[Framework], analysis: Analysis, log: Logger): (Seq[TestDefinition], Set[String]) =
|
||||
discover(frameworks flatMap TestFramework.getTests, allDefs(analysis), log)
|
||||
discover(frameworks flatMap TestFramework.getFingerprints, allDefs(analysis), log)
|
||||
|
||||
def allDefs(analysis: Analysis) = analysis.apis.internal.values.flatMap(_.api.definitions).toSeq
|
||||
def discover(fingerprints: Seq[Fingerprint], definitions: Seq[Definition], log: Logger): (Seq[TestDefinition], Set[String]) =
|
||||
{
|
||||
val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superClassName, sub.isModule, sub) };
|
||||
val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superclassName, sub.isModule, sub) };
|
||||
val annotations = fingerprints collect { case ann: AnnotatedFingerprint => (ann.annotationName, ann.isModule, ann) };
|
||||
log.debug("Subclass fingerprints: " + subclasses)
|
||||
log.debug("Annotation fingerprints: " + annotations)
|
||||
|
|
@ -169,14 +207,45 @@ object Tests
|
|||
(tests, mains.toSet)
|
||||
}
|
||||
|
||||
def showResults(log: Logger, results: (TestResult.Value, Map[String, TestResult.Value]), noTestsMessage: =>String): Unit =
|
||||
def showResults(log: Logger, results: Output, noTestsMessage: =>String): Unit =
|
||||
{
|
||||
if (results._2.isEmpty)
|
||||
val multipleFrameworks = results.summaries.size > 1
|
||||
def printSummary(name: String, message: String)
|
||||
{
|
||||
if (multipleFrameworks)
|
||||
log.info(name)
|
||||
if (message.size > 0)
|
||||
log.info(message)
|
||||
else
|
||||
log.info("Summary for " + name + " not available.")
|
||||
}
|
||||
|
||||
for (Summary(name, messages) <- results.summaries)
|
||||
printSummary(name, messages)
|
||||
val noSummary = results.summaries.headOption.forall(_.summaryText.size == 0)
|
||||
val printStandard = multipleFrameworks || noSummary
|
||||
// Print the standard one-liner statistic if no framework summary is defined, or when > 1 framework is in used.
|
||||
if (printStandard)
|
||||
{
|
||||
val (skippedCount, errorsCount, passedCount, failuresCount) =
|
||||
results.events.foldLeft((0, 0, 0, 0)) { case (acc, (name, testEvent)) =>
|
||||
(acc._1 + testEvent.skippedCount, acc._2 + testEvent.errorCount, acc._3 + testEvent.passedCount, acc._4 + testEvent.failureCount)
|
||||
}
|
||||
val totalCount = failuresCount + errorsCount + skippedCount + passedCount
|
||||
val postfix = "Total " + totalCount + ", Failed " + failuresCount + ", Errors " + errorsCount + ", Passed " + passedCount + ", Skipped " + skippedCount
|
||||
results.overall match {
|
||||
case TestResult.Error => log.error("Error: " + postfix)
|
||||
case TestResult.Passed => log.info("Passed: " + postfix)
|
||||
case TestResult.Failed => log.error("Failed: " + postfix)
|
||||
}
|
||||
}
|
||||
// Let's always print out Failed tests for now
|
||||
if (results.events.isEmpty)
|
||||
log.info(noTestsMessage)
|
||||
else {
|
||||
import TestResult.{Error, Failed, Passed}
|
||||
|
||||
def select(Tpe: TestResult.Value) = results._2 collect { case (name, Tpe) => name }
|
||||
def select(Tpe: TestResult.Value) = results.events collect { case (name, Tpe) => name }
|
||||
|
||||
val failures = select(Failed)
|
||||
val errors = select(Error)
|
||||
|
|
@ -184,17 +253,19 @@ object Tests
|
|||
|
||||
def show(label: String, level: Level.Value, tests: Iterable[String]): Unit =
|
||||
if(!tests.isEmpty)
|
||||
{
|
||||
log.log(level, label)
|
||||
log.log(level, tests.mkString("\t", "\n\t", ""))
|
||||
}
|
||||
{
|
||||
log.log(level, label)
|
||||
log.log(level, tests.mkString("\t", "\n\t", ""))
|
||||
}
|
||||
|
||||
show("Passed tests:", Level.Debug, passed )
|
||||
show("Failed tests:", Level.Error, failures)
|
||||
show("Error during tests:", Level.Error, errors)
|
||||
}
|
||||
|
||||
if(!failures.isEmpty || !errors.isEmpty)
|
||||
throw new TestsFailedException
|
||||
results.overall match {
|
||||
case TestResult.Error | TestResult.Failed => throw new TestsFailedException
|
||||
case TestResult.Passed =>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ package sbt
|
|||
import complete._
|
||||
import std.TaskExtra._
|
||||
import inc.{FileValueCache, Locate}
|
||||
import org.scalatools.testing.{Framework, AnnotatedFingerprint, SubclassFingerprint}
|
||||
import testing.{Framework, Runner, AnnotatedFingerprint, SubclassFingerprint}
|
||||
|
||||
import sys.error
|
||||
import scala.xml.NodeSeq
|
||||
|
|
@ -446,25 +446,46 @@ object Defaults extends BuildCommon
|
|||
implicit val display = Project.showContextKey(state.value)
|
||||
val modifiedOpts = Tests.Filters(filter(selected)) +: Tests.Argument(frameworkOptions : _*) +: config.options
|
||||
val newConfig = config.copy(options = modifiedOpts)
|
||||
val groupsTask = allTestGroupsTask(s, loadedTestFrameworks.value, testLoader.value, testGrouping.value, newConfig, fullClasspath.value, javaHome.value)
|
||||
val output = allTestGroupsTask(s, loadedTestFrameworks.value, testLoader.value, testGrouping.value, newConfig, fullClasspath.value, javaHome.value)
|
||||
val processed =
|
||||
for(out <- groupsTask) yield
|
||||
for(out <- output) yield
|
||||
Tests.showResults(s.log, out, noTestsMessage(resolvedScoped.value))
|
||||
Def.value(processed)
|
||||
}
|
||||
}
|
||||
|
||||
def createTestRunners(frameworks: Map[TestFramework,Framework], loader: ClassLoader, config: Tests.Execution) = {
|
||||
import Tests.Argument
|
||||
val opts = config.options.toList
|
||||
frameworks.map { case (tf, f) =>
|
||||
val args = opts.flatMap {
|
||||
case Argument(None | Some(`tf`), args) => args
|
||||
case _ => Nil
|
||||
}
|
||||
val mainRunner = f.runner(args.toArray, Array.empty[String], loader)
|
||||
tf -> mainRunner
|
||||
}
|
||||
}
|
||||
|
||||
def allTestGroupsTask(s: TaskStreams, frameworks: Map[TestFramework,Framework], loader: ClassLoader, groups: Seq[Tests.Group], config: Tests.Execution, cp: Classpath, javaHome: Option[File]): Task[Tests.Output] = {
|
||||
val runners = createTestRunners(frameworks, loader, config)
|
||||
val groupTasks = groups map {
|
||||
case Tests.Group(name, tests, runPolicy) =>
|
||||
runPolicy match {
|
||||
case Tests.SubProcess(opts) =>
|
||||
ForkTests(frameworks.keys.toSeq, tests.toList, config, cp.files, opts, s.log) tag Tags.ForkedTestGroup
|
||||
ForkTests(runners, tests.toList, config, cp.files, opts, s.log) tag Tags.ForkedTestGroup
|
||||
case Tests.InProcess =>
|
||||
Tests(frameworks, loader, tests, config, s.log)
|
||||
Tests(frameworks, loader, runners, tests, config, s.log)
|
||||
}
|
||||
}
|
||||
Tests.foldTasks(groupTasks, config.parallel)
|
||||
val output = Tests.foldTasks(groupTasks, config.parallel)
|
||||
output map { out =>
|
||||
val summaries =
|
||||
runners map { case (tf, r) =>
|
||||
Tests.Summary(frameworks(tf).name, r.done())
|
||||
}
|
||||
out.copy(summaries = summaries)
|
||||
}
|
||||
}
|
||||
|
||||
def selectedFilter(args: Seq[String]): Seq[String => Boolean] =
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ package sbt
|
|||
import scala.xml.{Node => XNode, NodeSeq}
|
||||
import org.apache.ivy.core.module.{descriptor, id}
|
||||
import descriptor.ModuleDescriptor, id.ModuleRevisionId
|
||||
import org.scalatools.testing.Framework
|
||||
import testing.Framework
|
||||
import Configurations.CompilerPlugin
|
||||
import Types.Id
|
||||
import KeyRanks._
|
||||
|
|
|
|||
|
|
@ -79,10 +79,10 @@ object Sbt extends Build
|
|||
// Apache Ivy integration
|
||||
lazy val ivySub = baseProject(file("ivy"), "Ivy") dependsOn(interfaceSub, launchInterfaceSub, crossSub, logSub % "compile;test->test", ioSub % "compile;test->test", launchSub % "test->test") settings(ivy, jsch, httpclient, testExclusive)
|
||||
// Runner for uniform test interface
|
||||
lazy val testingSub = baseProject(file("testing"), "Testing") dependsOn(ioSub, classpathSub, logSub, launchInterfaceSub, testAgentSub) settings(libraryDependencies += "org.scala-tools.testing" % "test-interface" % "0.5")
|
||||
lazy val testingSub = baseProject(file("testing"), "Testing") dependsOn(ioSub, classpathSub, logSub, launchInterfaceSub, testAgentSub) settings(libraryDependencies += "org.scalatest" % "test-interface" % "1.0-SNAP3")
|
||||
// Testing agent for running tests in a separate process.
|
||||
lazy val testAgentSub = project(file("testing/agent"), "Test Agent") settings(
|
||||
libraryDependencies += "org.scala-tools.testing" % "test-interface" % "0.5"
|
||||
libraryDependencies += "org.scalatest" % "test-interface" % "1.0-SNAP3"
|
||||
)
|
||||
|
||||
// Basic task engine
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
-> f
|
||||
> project sub
|
||||
> f
|
||||
> reload
|
||||
> f
|
||||
$ copy-file changes/Changed.scala project/TestProject.scala
|
||||
> reload
|
||||
-> f
|
||||
> project {external}root2
|
||||
> g
|
||||
# The current URI should be kept
|
||||
> reload
|
||||
-> f
|
||||
> project sub
|
||||
> f
|
||||
> reload
|
||||
> f
|
||||
$ copy-file changes/Changed.scala project/TestProject.scala
|
||||
> reload
|
||||
-> f
|
||||
> project {external}root2
|
||||
> g
|
||||
# The current URI should be kept
|
||||
> reload
|
||||
> g
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package testCase;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface JFoo {
|
||||
|
||||
}
|
||||
package testCase;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface JFoo {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
import annotation.target.field
|
||||
|
||||
|
||||
package object testCase {
|
||||
type Foo = JFoo @field;
|
||||
}
|
||||
|
||||
import annotation.target.field
|
||||
|
||||
|
||||
package object testCase {
|
||||
type Foo = JFoo @field;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
import sbt._
|
||||
import Keys._
|
||||
|
||||
object TestProject extends Build
|
||||
{
|
||||
lazy val root = Project("root", file(".")) settings(
|
||||
ivyPaths <<= (baseDirectory, target)( (dir, t) => new IvyPaths(dir, Some(t / "ivy-cache"))),
|
||||
libraryDependencies <++= baseDirectory (libraryDeps),
|
||||
TaskKey[Unit]("check-forced") <<= check("1.2.14"),
|
||||
TaskKey[Unit]("check-depend") <<= check("1.2.13")
|
||||
)
|
||||
|
||||
def libraryDeps(base: File) = {
|
||||
val slf4j = Seq("org.slf4j" % "slf4j-log4j12" % "1.1.0") // Uses log4j 1.2.13
|
||||
if ((base / "force").exists) slf4j :+ ("log4j" % "log4j" % "1.2.14" force()) else slf4j
|
||||
}
|
||||
|
||||
def check(ver: String) =
|
||||
(dependencyClasspath in Compile) map { jars =>
|
||||
val log4j = jars map (_.data) collect {
|
||||
case f if f.getName contains "log4j-" => f.getName
|
||||
}
|
||||
if (log4j.size != 1 || !log4j.head.contains(ver))
|
||||
error("Did not download the correct jar.")
|
||||
}
|
||||
}
|
||||
import sbt._
|
||||
import Keys._
|
||||
|
||||
object TestProject extends Build
|
||||
{
|
||||
lazy val root = Project("root", file(".")) settings(
|
||||
ivyPaths <<= (baseDirectory, target)( (dir, t) => new IvyPaths(dir, Some(t / "ivy-cache"))),
|
||||
libraryDependencies <++= baseDirectory (libraryDeps),
|
||||
TaskKey[Unit]("check-forced") <<= check("1.2.14"),
|
||||
TaskKey[Unit]("check-depend") <<= check("1.2.13")
|
||||
)
|
||||
|
||||
def libraryDeps(base: File) = {
|
||||
val slf4j = Seq("org.slf4j" % "slf4j-log4j12" % "1.1.0") // Uses log4j 1.2.13
|
||||
if ((base / "force").exists) slf4j :+ ("log4j" % "log4j" % "1.2.14" force()) else slf4j
|
||||
}
|
||||
|
||||
def check(ver: String) =
|
||||
(dependencyClasspath in Compile) map { jars =>
|
||||
val log4j = jars map (_.data) collect {
|
||||
case f if f.getName contains "log4j-" => f.getName
|
||||
}
|
||||
if (log4j.size != 1 || !log4j.head.contains(ver))
|
||||
error("Did not download the correct jar.")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
$ touch force
|
||||
|
||||
> reload
|
||||
|
||||
> check-forced
|
||||
-> check-depend
|
||||
|
||||
$ delete force
|
||||
> reload
|
||||
|
||||
-> check-forced
|
||||
$ touch force
|
||||
|
||||
> reload
|
||||
|
||||
> check-forced
|
||||
-> check-depend
|
||||
|
||||
$ delete force
|
||||
> reload
|
||||
|
||||
-> check-forced
|
||||
> check-depend
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
k1 := {error("k1")}
|
||||
|
||||
k2 <<= k1 map identity
|
||||
|
||||
k1 := {error("k1")}
|
||||
|
||||
k2 <<= k1 map identity
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
k1 := {}
|
||||
|
||||
k2 <<= k1 map identity
|
||||
|
||||
k1 := {}
|
||||
|
||||
k2 <<= k1 map identity
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
k1 := {}
|
||||
|
||||
k2 := {}
|
||||
|
||||
k1 <<= k1 map {_ => error("k1")}
|
||||
|
||||
k1 := {}
|
||||
|
||||
k2 := {}
|
||||
|
||||
k1 <<= k1 map {_ => error("k1")}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
k1 := {
|
||||
}
|
||||
|
||||
k2 := {
|
||||
}
|
||||
|
||||
k1 := {
|
||||
}
|
||||
|
||||
k2 := {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import sbt._
|
||||
|
||||
object TestBuild extends Build {
|
||||
val k1 = TaskKey[Unit]("k1")
|
||||
val k2 = TaskKey[Unit]("k2")
|
||||
|
||||
lazy val root = Project("root", file("."))
|
||||
}
|
||||
import sbt._
|
||||
|
||||
object TestBuild extends Build {
|
||||
val k1 = TaskKey[Unit]("k1")
|
||||
val k2 = TaskKey[Unit]("k2")
|
||||
|
||||
lazy val root = Project("root", file("."))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
> set k1 := {error("k1")}
|
||||
> session save
|
||||
> reload
|
||||
-> k1
|
||||
|
||||
> set k2 <<= k1 map identity
|
||||
> session save
|
||||
> reload
|
||||
-> k2
|
||||
$ must-mirror build.sbt build.check.1
|
||||
|
||||
> set k1 := {}
|
||||
> session save
|
||||
> reload
|
||||
> k1
|
||||
> k2
|
||||
$ must-mirror build.sbt build.check.2
|
||||
|
||||
> set k1 <<= k1 map {_ => error("k1")}
|
||||
> set k2 := {}
|
||||
> session save
|
||||
> reload
|
||||
-> k1
|
||||
> k2
|
||||
> set k1 := {error("k1")}
|
||||
> session save
|
||||
> reload
|
||||
-> k1
|
||||
|
||||
> set k2 <<= k1 map identity
|
||||
> session save
|
||||
> reload
|
||||
-> k2
|
||||
$ must-mirror build.sbt build.check.1
|
||||
|
||||
> set k1 := {}
|
||||
> session save
|
||||
> reload
|
||||
> k1
|
||||
> k2
|
||||
$ must-mirror build.sbt build.check.2
|
||||
|
||||
> set k1 <<= k1 map {_ => error("k1")}
|
||||
> set k2 := {}
|
||||
> session save
|
||||
> reload
|
||||
-> k1
|
||||
> k2
|
||||
$ must-mirror build.sbt build.check.3
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName)
|
||||
case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName)
|
||||
case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName)
|
||||
case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
@DoNotDiscover
|
||||
class TestSpec2 extends Spec {
|
||||
|
||||
def `TestSpec2-test-1 ` {}
|
||||
|
||||
def `TestSpec2-test-2 ` {}
|
||||
|
||||
def `TestSpec2-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#Test the ScalaTest framework to exclude suite annotated with @DoNotDiscover (TestSpec2) properly.
|
||||
#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/,
|
||||
#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec-2
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec2
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec2-2
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec2
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-3-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-1-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-1-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-2-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-2-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-3-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-3-2
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends ResourcefulReporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case runCompleted: RunCompleted => writeFile("target/RunCompleted", "RunCompleted")
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
def dispose() {
|
||||
val file = new File("target/dispose")
|
||||
val filePath =
|
||||
if (file.exists)
|
||||
"target/dispose2"
|
||||
else
|
||||
"target/dispose"
|
||||
writeFile(filePath, "dispose")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec2 extends Spec {
|
||||
|
||||
def `TestSpec2-test-1 ` {}
|
||||
|
||||
def `TestSpec2-test-2 ` {}
|
||||
|
||||
def `TestSpec2-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#Test the framework will call Runner.done once.
|
||||
#Because ScalaTest's runner will report RunCompleted when the run completed, a CustomReporter is
|
||||
#used to report expected ScalaTest's RunCompleted event by writing out to target/, it is then
|
||||
#used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
#ResourcefulReporter's dispose method will be called in Runner.done also, and it should be called
|
||||
#once only.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/RunCompleted
|
||||
|
||||
$ absent target/RunCompleted-2
|
||||
|
||||
$ exists target/dispose
|
||||
|
||||
$ absent target/dispose2
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
||||
parallelExecution in Test := true
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName)
|
||||
case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName)
|
||||
case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName)
|
||||
case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
class NestedSpecs extends Suites (
|
||||
new TestSpec
|
||||
)
|
||||
|
||||
@DoNotDiscover
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#This test that the framework will execute ScalaTest nested suites as parallel nested task (InProcess) properly.
|
||||
#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/,
|
||||
#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/SuiteStarting-NestedSpecs
|
||||
|
||||
$ absent target/SuiteStarting-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteCompleted-NestedSpecs
|
||||
|
||||
$ absent target/SuiteCompleted-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-3-2
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
||||
parallelExecution in Test := false
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName)
|
||||
case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName)
|
||||
case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName)
|
||||
case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
class NestedSpecs extends Suites (
|
||||
new TestSpec
|
||||
)
|
||||
|
||||
@DoNotDiscover
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#This test that the framework will execute ScalaTest nested suites as sequential nested task (InProcess) properly.
|
||||
#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/,
|
||||
#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/SuiteStarting-NestedSpecs
|
||||
|
||||
$ absent target/SuiteStarting-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteCompleted-NestedSpecs
|
||||
|
||||
$ absent target/SuiteCompleted-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-3-2
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
||||
fork := true
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName)
|
||||
case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName)
|
||||
case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName)
|
||||
case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
class NestedSpecs extends Suites (
|
||||
new TestSpec
|
||||
)
|
||||
|
||||
@DoNotDiscover
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#This test that the framework will execute ScalaTest nested suites as sequential nested task (SubProcess) properly.
|
||||
#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/,
|
||||
#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/SuiteStarting-NestedSpecs
|
||||
|
||||
$ absent target/SuiteStarting-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteCompleted-NestedSpecs
|
||||
|
||||
$ absent target/SuiteCompleted-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-3-2
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case runStarting: RunStarting => writeFile("target/RunStarting", "RunStarting")
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec2 extends Spec {
|
||||
|
||||
def `TestSpec2-test-1 ` {}
|
||||
|
||||
def `TestSpec2-test-2 ` {}
|
||||
|
||||
def `TestSpec2-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#This test that the framework will only use a single runner instance to execute all tests.
|
||||
#Because ScalaTest's runner will report RunStarting when the run start, a CustomReporter is
|
||||
#used to report expected ScalaTest's RunStarting event by writing out to target/, it is then
|
||||
#used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/RunStarting
|
||||
|
||||
$ absent target/RunStarting-2
|
||||
|
|
@ -14,10 +14,10 @@ object Ticket543Test extends Build {
|
|||
fork := true,
|
||||
testListeners += new TestReportListener {
|
||||
def testEvent(event: TestEvent) {
|
||||
for (e <- event.detail.filter(_.result == org.scalatools.testing.Result.Failure)) {
|
||||
if (e.error ne null) {
|
||||
for (e <- event.detail.filter(_.status == sbt.testing.Status.Failure)) {
|
||||
if (e.throwable ne null) {
|
||||
val caw = new CharArrayWriter
|
||||
e.error.printStackTrace(new PrintWriter(caw))
|
||||
e.throwable.printStackTrace(new PrintWriter(caw))
|
||||
if (caw.toString.contains("Test.scala:"))
|
||||
marker.createNewFile()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName)
|
||||
case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName)
|
||||
case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName)
|
||||
case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest.Spec
|
||||
|
||||
class TestSpec2 extends Spec {
|
||||
|
||||
def `TestSpec2-test-1 ` {}
|
||||
|
||||
def `TestSpec2-test-2 ` {}
|
||||
|
||||
def `TestSpec2-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#This test that the framework will execute ScalaTest suites as task properly.
|
||||
#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/,
|
||||
#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec-2
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec2
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec2-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec2
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec2-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec2-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec2-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec2-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec2-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec2-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec2-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec2-test-3-2
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
case class A(b: B) {
|
||||
def foo = b.foo
|
||||
// A comment added should trigger recompilation.
|
||||
case class A(b: B) {
|
||||
def foo = b.foo
|
||||
// A comment added should trigger recompilation.
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
class B {
|
||||
def foo = 1
|
||||
// API-level change
|
||||
def bar(a: A) = 2
|
||||
}
|
||||
class B {
|
||||
def foo = 1
|
||||
// API-level change
|
||||
def bar(a: A) = 2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import java.io.File
|
||||
|
||||
trait Base {
|
||||
val marker = new File("marker")
|
||||
// Test compilation group change.
|
||||
val baz = ""
|
||||
}
|
||||
import java.io.File
|
||||
|
||||
trait Base {
|
||||
val marker = new File("marker")
|
||||
// Test compilation group change.
|
||||
val baz = ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// A, B are referring to each other, OK in the same compilation group.
|
||||
case class A(b: B) {
|
||||
def foo = b.foo
|
||||
// A, B are referring to each other, OK in the same compilation group.
|
||||
case class A(b: B) {
|
||||
def foo = b.foo
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
class B {
|
||||
def foo = 1
|
||||
def bar(a: A) {}
|
||||
}
|
||||
class B {
|
||||
def foo = 1
|
||||
def bar(a: A) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import java.io.File
|
||||
|
||||
trait Base {
|
||||
val marker = new File("marker")
|
||||
}
|
||||
import java.io.File
|
||||
|
||||
trait Base {
|
||||
val marker = new File("marker")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class Create extends FlatSpec with ShouldMatchers with Base {
|
||||
"a file" should "not exist" in {
|
||||
A(new B).foo
|
||||
marker.exists should equal(false)
|
||||
marker.createNewFile() should equal (true)
|
||||
}
|
||||
|
||||
}
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class Create extends FlatSpec with ShouldMatchers with Base {
|
||||
"a file" should "not exist" in {
|
||||
A(new B).foo
|
||||
marker.exists should equal(false)
|
||||
marker.createNewFile() should equal (true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class Delete extends FlatSpec with ShouldMatchers with Base {
|
||||
"a file" should "exist" in {
|
||||
marker.exists should equal(true)
|
||||
marker.delete()
|
||||
}
|
||||
|
||||
}
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class Delete extends FlatSpec with ShouldMatchers with Base {
|
||||
"a file" should "exist" in {
|
||||
marker.exists should equal(true)
|
||||
marker.delete()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
> test-quick Create
|
||||
# Create not re-run, Delete deletes the file.
|
||||
> test-quick
|
||||
# Re-create the file.
|
||||
> test-only Create
|
||||
|
||||
# Non-API change
|
||||
$ copy-file changed/A.scala src/main/scala/A.scala
|
||||
> compile
|
||||
$ sleep 2000
|
||||
# Create is run. Delete is not since it doesn't have src/main dependency.
|
||||
-> test-quick
|
||||
> test-only Delete
|
||||
# Previous run of Create failed, re-run.
|
||||
> test-quick Create
|
||||
# No-op.
|
||||
> test-quick Create
|
||||
# API change.
|
||||
|
||||
$ copy-file changed/B.scala src/main/scala/B.scala
|
||||
> compile
|
||||
$ sleep 2000
|
||||
-> test-quick Create
|
||||
> test-only Delete
|
||||
# Previous run of Create failed, re-run.
|
||||
> test-quick Create
|
||||
# src/test compilation group change.
|
||||
|
||||
$ copy-file changed/Base.scala src/test/scala/Base.scala
|
||||
> test:compile
|
||||
$ sleep 2000
|
||||
-> test-quick Create
|
||||
> test-quick Delete
|
||||
> test-quick Create
|
||||
# Create not re-run, Delete deletes the file.
|
||||
> test-quick
|
||||
# Re-create the file.
|
||||
> test-only Create
|
||||
|
||||
# Non-API change
|
||||
$ copy-file changed/A.scala src/main/scala/A.scala
|
||||
> compile
|
||||
$ sleep 2000
|
||||
# Create is run. Delete is not since it doesn't have src/main dependency.
|
||||
-> test-quick
|
||||
> test-only Delete
|
||||
# Previous run of Create failed, re-run.
|
||||
> test-quick Create
|
||||
# No-op.
|
||||
> test-quick Create
|
||||
# API change.
|
||||
|
||||
$ copy-file changed/B.scala src/main/scala/B.scala
|
||||
> compile
|
||||
$ sleep 2000
|
||||
-> test-quick Create
|
||||
> test-only Delete
|
||||
# Previous run of Create failed, re-run.
|
||||
> test-quick Create
|
||||
# src/test compilation group change.
|
||||
|
||||
$ copy-file changed/Base.scala src/test/scala/Base.scala
|
||||
> test:compile
|
||||
$ sleep 2000
|
||||
-> test-quick Create
|
||||
> test-quick Delete
|
||||
> test-quick Create
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package sbt;
|
||||
|
||||
import org.scalatools.testing.*;
|
||||
import sbt.testing.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
|
@ -13,17 +13,21 @@ import java.net.Socket;
|
|||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ForkMain {
|
||||
static class SubclassFingerscan implements TestFingerprint, Serializable {
|
||||
static class SubclassFingerscan implements SubclassFingerprint, Serializable {
|
||||
private boolean isModule;
|
||||
private String superClassName;
|
||||
private String superclassName;
|
||||
private boolean requireNoArgConstructor;
|
||||
SubclassFingerscan(SubclassFingerprint print) {
|
||||
isModule = print.isModule();
|
||||
superClassName = print.superClassName();
|
||||
superclassName = print.superclassName();
|
||||
requireNoArgConstructor = print.requireNoArgConstructor();
|
||||
}
|
||||
public boolean isModule() { return isModule; }
|
||||
public String superClassName() { return superClassName; }
|
||||
public String superclassName() { return superclassName; }
|
||||
public boolean requireNoArgConstructor() { return requireNoArgConstructor; }
|
||||
}
|
||||
static class AnnotatedFingerscan implements AnnotatedFingerprint, Serializable {
|
||||
private boolean isModule;
|
||||
|
|
@ -59,21 +63,69 @@ public class ForkMain {
|
|||
public String getMessage() { return originalMessage; }
|
||||
public Exception getCause() { return cause; }
|
||||
}
|
||||
static class ForkEvent implements Event, Serializable {
|
||||
static class ForkSelector extends Selector implements Serializable {}
|
||||
static class ForkSuiteSelector extends ForkSelector {}
|
||||
static class ForkTestSelector extends ForkSelector {
|
||||
private String testName;
|
||||
private String description;
|
||||
private Result result;
|
||||
private Throwable error;
|
||||
ForkEvent(Event e) {
|
||||
testName = e.testName();
|
||||
description = e.description();
|
||||
result = e.result();
|
||||
if (e.error() != null) error = new ForkError(e.error());
|
||||
ForkTestSelector(TestSelector testSelector) {
|
||||
this.testName = testSelector.getTestName();
|
||||
}
|
||||
public String getTestName() {
|
||||
return testName;
|
||||
}
|
||||
}
|
||||
static class ForkNestedSuiteSelector extends ForkSelector {
|
||||
private String suiteId;
|
||||
ForkNestedSuiteSelector(NestedSuiteSelector nestedSuiteSelector) {
|
||||
this.suiteId = nestedSuiteSelector.getSuiteId();
|
||||
}
|
||||
public String getSuiteId() {
|
||||
return suiteId;
|
||||
}
|
||||
}
|
||||
static class ForkNestedTestSelector extends ForkSelector {
|
||||
private String suiteId;
|
||||
private String testName;
|
||||
ForkNestedTestSelector(NestedTestSelector nestedTestSelector) {
|
||||
this.suiteId = nestedTestSelector.getSuiteId();
|
||||
this.testName = nestedTestSelector.getTestName();
|
||||
}
|
||||
public String getSuiteId() {
|
||||
return suiteId;
|
||||
}
|
||||
public String getTestName() {
|
||||
return testName;
|
||||
}
|
||||
}
|
||||
|
||||
static class ForkEvent implements Event, Serializable {
|
||||
private String fullyQualifiedName;
|
||||
private boolean isModule;
|
||||
private ForkSelector selector;
|
||||
private Status status;
|
||||
private Throwable throwable;
|
||||
ForkEvent(Event e) {
|
||||
fullyQualifiedName = e.fullyQualifiedName();
|
||||
isModule = e.isModule();
|
||||
selector = forkSelector(e.selector());
|
||||
status = e.status();
|
||||
if (e.throwable() != null) throwable = new ForkError(e.throwable());
|
||||
}
|
||||
public String fullyQualifiedName() { return fullyQualifiedName; }
|
||||
public boolean isModule() { return isModule; }
|
||||
public Selector selector() { return selector; }
|
||||
public Status status() { return status; }
|
||||
public Throwable throwable() { return throwable; }
|
||||
protected ForkSelector forkSelector(Selector selector) {
|
||||
if (selector instanceof SuiteSelector)
|
||||
return new ForkSuiteSelector();
|
||||
else if (selector instanceof TestSelector)
|
||||
return new ForkTestSelector((TestSelector) selector);
|
||||
else if (selector instanceof NestedSuiteSelector)
|
||||
return new ForkNestedSuiteSelector((NestedSuiteSelector) selector);
|
||||
else
|
||||
return new ForkNestedTestSelector((NestedTestSelector) selector);
|
||||
}
|
||||
public String testName() { return testName; }
|
||||
public String description() { return description; }
|
||||
public Result result() { return result; }
|
||||
public Throwable error() { return error; }
|
||||
}
|
||||
public static void main(String[] args) throws Exception {
|
||||
Socket socket = new Socket(InetAddress.getByName(null), Integer.valueOf(args[0]));
|
||||
|
|
@ -95,7 +147,7 @@ public class ForkMain {
|
|||
if (f1 instanceof SubclassFingerprint && f2 instanceof SubclassFingerprint) {
|
||||
final SubclassFingerprint sf1 = (SubclassFingerprint) f1;
|
||||
final SubclassFingerprint sf2 = (SubclassFingerprint) f2;
|
||||
return sf1.isModule() == sf2.isModule() && sf1.superClassName().equals(sf2.superClassName());
|
||||
return sf1.isModule() == sf2.isModule() && sf1.superclassName().equals(sf2.superclassName());
|
||||
} else if (f1 instanceof AnnotatedFingerprint && f2 instanceof AnnotatedFingerprint) {
|
||||
AnnotatedFingerprint af1 = (AnnotatedFingerprint) f1;
|
||||
AnnotatedFingerprint af2 = (AnnotatedFingerprint) f2;
|
||||
|
|
@ -106,7 +158,7 @@ public class ForkMain {
|
|||
class RunAborted extends RuntimeException {
|
||||
RunAborted(Exception e) { super(e); }
|
||||
}
|
||||
void write(ObjectOutputStream os, Object obj) {
|
||||
synchronized void write(ObjectOutputStream os, Object obj) {
|
||||
try {
|
||||
os.writeObject(obj);
|
||||
os.flush();
|
||||
|
|
@ -117,6 +169,9 @@ public class ForkMain {
|
|||
void logError(ObjectOutputStream os, String message) {
|
||||
write(os, new Object[]{ForkTags.Error, message});
|
||||
}
|
||||
void logDebug(ObjectOutputStream os, String message) {
|
||||
write(os, new Object[]{ForkTags.Debug, message});
|
||||
}
|
||||
void writeEvents(ObjectOutputStream os, ForkTestDefinition test, ForkEvent[] events) {
|
||||
write(os, new Object[]{test.name, events});
|
||||
}
|
||||
|
|
@ -136,50 +191,98 @@ public class ForkMain {
|
|||
};
|
||||
|
||||
for (int i = 0; i < nFrameworks; i++) {
|
||||
final String implClassName = (String) is.readObject();
|
||||
final String[] implClassNames = (String[]) is.readObject();
|
||||
final String[] frameworkArgs = (String[]) is.readObject();
|
||||
final String[] remoteFrameworkArgs = (String[]) is.readObject();
|
||||
|
||||
final Framework framework;
|
||||
try {
|
||||
framework = (Framework) Class.forName(implClassName).newInstance();
|
||||
} catch (ClassNotFoundException e) {
|
||||
logError(os, "Framework implementation '" + implClassName + "' not present.");
|
||||
continue;
|
||||
Framework framework = null;
|
||||
for (String implClassName : implClassNames) {
|
||||
try {
|
||||
Object rawFramework = Class.forName(implClassName).newInstance();
|
||||
if (rawFramework instanceof Framework)
|
||||
framework = (Framework) rawFramework;
|
||||
else
|
||||
framework = new FrameworkWrapper((org.scalatools.testing.Framework) rawFramework);
|
||||
break;
|
||||
} catch (ClassNotFoundException e) {
|
||||
logDebug(os, "Framework implementation '" + implClassName + "' not present.");
|
||||
}
|
||||
}
|
||||
|
||||
if (framework == null)
|
||||
continue;
|
||||
|
||||
ArrayList<ForkTestDefinition> filteredTests = new ArrayList<ForkTestDefinition>();
|
||||
for (Fingerprint testFingerprint : framework.tests()) {
|
||||
for (Fingerprint testFingerprint : framework.fingerprints()) {
|
||||
for (ForkTestDefinition test : tests) {
|
||||
if (matches(testFingerprint, test.fingerprint)) filteredTests.add(test);
|
||||
}
|
||||
}
|
||||
final org.scalatools.testing.Runner runner = framework.testRunner(getClass().getClassLoader(), loggers);
|
||||
final Runner runner = framework.runner(frameworkArgs, remoteFrameworkArgs, getClass().getClassLoader());
|
||||
for (ForkTestDefinition test : filteredTests)
|
||||
runTestSafe(test, runner, framework, frameworkArgs, os);
|
||||
runTestSafe(test, runner, loggers, os);
|
||||
runner.done();
|
||||
}
|
||||
write(os, ForkTags.Done);
|
||||
is.readObject();
|
||||
}
|
||||
void runTestSafe(ForkTestDefinition test, org.scalatools.testing.Runner runner, Framework framework, String[] frameworkArgs, ObjectOutputStream os) {
|
||||
ForkEvent[] events;
|
||||
class NestedTask {
|
||||
private String parentName;
|
||||
private Task task;
|
||||
NestedTask(String parentName, Task task) {
|
||||
this.parentName = parentName;
|
||||
this.task = task;
|
||||
}
|
||||
public String getParentName() {
|
||||
return parentName;
|
||||
}
|
||||
public Task getTask() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
void runTestSafe(ForkTestDefinition test, Runner runner, Logger[] loggers, ObjectOutputStream os) {
|
||||
try {
|
||||
events = runTest(test, runner, framework, frameworkArgs, os);
|
||||
// TODO: To pass in correct explicitlySpecified and selectors
|
||||
Task task = runner.task(test.name, test.fingerprint, false, new Selector[] { new SuiteSelector() });
|
||||
|
||||
List<NestedTask> nestedTasks = new ArrayList<NestedTask>();
|
||||
for (Task nt : runTest(test, task, loggers, os))
|
||||
nestedTasks.add(new NestedTask(test.name, nt));
|
||||
while (true) {
|
||||
List<NestedTask> newNestedTasks = new ArrayList<NestedTask>();
|
||||
int nestedTasksLength = nestedTasks.size();
|
||||
for (int i = 0; i < nestedTasksLength; i++) {
|
||||
NestedTask nestedTask = nestedTasks.get(i);
|
||||
String nestedParentName = nestedTask.getParentName() + "-" + i;
|
||||
for (Task nt : runTest(new ForkTestDefinition(nestedParentName, test.fingerprint), nestedTask.getTask(), loggers, os)) {
|
||||
newNestedTasks.add(new NestedTask(nestedParentName, nt));
|
||||
}
|
||||
}
|
||||
if (newNestedTasks.size() == 0)
|
||||
break;
|
||||
else {
|
||||
nestedTasks = newNestedTasks;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
writeEvents(os, test, new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) });
|
||||
}
|
||||
}
|
||||
Task[] runTest(ForkTestDefinition test, Task task, Logger[] loggers, ObjectOutputStream os) {
|
||||
ForkEvent[] events;
|
||||
Task[] nestedTasks;
|
||||
try {
|
||||
final List<ForkEvent> eventList = new ArrayList<ForkEvent>();
|
||||
EventHandler handler = new EventHandler() { public void handle(Event e){ eventList.add(new ForkEvent(e)); } };
|
||||
nestedTasks = task.execute(handler, loggers);
|
||||
events = eventList.toArray(new ForkEvent[eventList.size()]);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
nestedTasks = new Task[0];
|
||||
events = new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) };
|
||||
}
|
||||
writeEvents(os, test, events);
|
||||
}
|
||||
ForkEvent[] runTest(ForkTestDefinition test, org.scalatools.testing.Runner runner, Framework framework, String[] frameworkArgs, ObjectOutputStream os) {
|
||||
final List<ForkEvent> events = new ArrayList<ForkEvent>();
|
||||
EventHandler handler = new EventHandler() { public void handle(Event e){ events.add(new ForkEvent(e)); } };
|
||||
if (runner instanceof Runner2) {
|
||||
((Runner2) runner).run(test.name, test.fingerprint, handler, frameworkArgs);
|
||||
} else if (test.fingerprint instanceof TestFingerprint) {
|
||||
runner.run(test.name, (TestFingerprint) test.fingerprint, handler, frameworkArgs);
|
||||
} else {
|
||||
events.add(testError(os, test, "Framework '" + framework + "' does not support test '" + test.name + "'"));
|
||||
}
|
||||
return events.toArray(new ForkEvent[events.size()]);
|
||||
return nestedTasks;
|
||||
}
|
||||
void run(ObjectInputStream is, ObjectOutputStream os) throws Exception {
|
||||
try {
|
||||
|
|
@ -198,22 +301,23 @@ public class ForkMain {
|
|||
void internalError(Throwable t) {
|
||||
System.err.println("Internal error when running tests: " + t.toString());
|
||||
}
|
||||
ForkEvent testEvent(final String name, final String desc, final Result r, final Throwable err) {
|
||||
ForkEvent testEvent(final String fullyQualifiedName, final Fingerprint fingerprint, final Selector selector, final Status r, final Throwable err) {
|
||||
return new ForkEvent(new Event() {
|
||||
public String testName() { return name; }
|
||||
public String description() { return desc; }
|
||||
public Result result() { return r; }
|
||||
public Throwable error() { return err; }
|
||||
public String fullyQualifiedName() { return fullyQualifiedName; }
|
||||
public boolean isModule() { return fingerprint instanceof SubclassFingerprint ? ((SubclassFingerprint) fingerprint).isModule() : ((AnnotatedFingerprint) fingerprint).isModule(); }
|
||||
public Selector selector() { return selector; }
|
||||
public Status status() { return r; }
|
||||
public Throwable throwable() { return err; }
|
||||
});
|
||||
}
|
||||
ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message) {
|
||||
logError(os, message);
|
||||
return testEvent(test.name, message, Result.Error, null);
|
||||
return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, null);
|
||||
}
|
||||
ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message, Throwable t) {
|
||||
logError(os, message);
|
||||
write(os, t);
|
||||
return testEvent(test.name, message, Result.Error, t);
|
||||
return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
package sbt;
|
||||
|
||||
import sbt.testing.*;
|
||||
|
||||
public class FrameworkWrapper implements Framework {
|
||||
|
||||
private org.scalatools.testing.Framework oldFramework;
|
||||
|
||||
public FrameworkWrapper(org.scalatools.testing.Framework oldFramework) {
|
||||
this.oldFramework = oldFramework;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return oldFramework.name();
|
||||
}
|
||||
|
||||
public Fingerprint[] fingerprints() {
|
||||
org.scalatools.testing.Fingerprint[] oldFingerprints = oldFramework.tests();
|
||||
int length = oldFingerprints.length;
|
||||
Fingerprint[] fingerprints = new Fingerprint[length];
|
||||
for (int i=0; i < length; i++) {
|
||||
org.scalatools.testing.Fingerprint oldFingerprint = oldFingerprints[i];
|
||||
if (oldFingerprint instanceof org.scalatools.testing.TestFingerprint)
|
||||
fingerprints[i] = new TestFingerprintWrapper((org.scalatools.testing.TestFingerprint) oldFingerprint);
|
||||
else if (oldFingerprint instanceof org.scalatools.testing.SubclassFingerprint)
|
||||
fingerprints[i] = new SubclassFingerprintWrapper((org.scalatools.testing.SubclassFingerprint) oldFingerprint);
|
||||
else
|
||||
fingerprints[i] = new AnnotatedFingerprintWrapper((org.scalatools.testing.AnnotatedFingerprint) oldFingerprint);
|
||||
}
|
||||
return fingerprints;
|
||||
}
|
||||
|
||||
public Runner runner(String[] args, String[] remoteArgs, ClassLoader testClassLoader) {
|
||||
return new RunnerWrapper(oldFramework, testClassLoader, args);
|
||||
}
|
||||
}
|
||||
|
||||
class SubclassFingerprintWrapper implements SubclassFingerprint {
|
||||
private String superclassName;
|
||||
private boolean isModule;
|
||||
private boolean requireNoArgConstructor;
|
||||
|
||||
public SubclassFingerprintWrapper(org.scalatools.testing.SubclassFingerprint oldFingerprint) {
|
||||
this.superclassName = oldFingerprint.superClassName();
|
||||
this.isModule = oldFingerprint.isModule();
|
||||
this.requireNoArgConstructor = false; // Old framework SubclassFingerprint does not require no arg constructor
|
||||
}
|
||||
|
||||
public boolean isModule() {
|
||||
return isModule;
|
||||
}
|
||||
|
||||
public String superclassName() {
|
||||
return superclassName;
|
||||
}
|
||||
|
||||
public boolean requireNoArgConstructor() {
|
||||
return requireNoArgConstructor;
|
||||
}
|
||||
}
|
||||
|
||||
class AnnotatedFingerprintWrapper implements AnnotatedFingerprint {
|
||||
private String annotationName;
|
||||
private boolean isModule;
|
||||
|
||||
public AnnotatedFingerprintWrapper(org.scalatools.testing.AnnotatedFingerprint oldFingerprint) {
|
||||
this.annotationName = oldFingerprint.annotationName();
|
||||
this.isModule = oldFingerprint.isModule();
|
||||
}
|
||||
|
||||
public boolean isModule() {
|
||||
return isModule;
|
||||
}
|
||||
|
||||
public String annotationName() {
|
||||
return annotationName;
|
||||
}
|
||||
}
|
||||
|
||||
class TestFingerprintWrapper extends SubclassFingerprintWrapper {
|
||||
|
||||
public TestFingerprintWrapper(org.scalatools.testing.TestFingerprint oldFingerprint) {
|
||||
super(oldFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
class EventHandlerWrapper implements org.scalatools.testing.EventHandler {
|
||||
|
||||
private EventHandler newEventHandler;
|
||||
private String fullyQualifiedName;
|
||||
private boolean isModule;
|
||||
|
||||
public EventHandlerWrapper(EventHandler newEventHandler, String fullyQualifiedName, boolean isModule) {
|
||||
this.newEventHandler = newEventHandler;
|
||||
this.fullyQualifiedName = fullyQualifiedName;
|
||||
this.isModule = isModule;
|
||||
}
|
||||
|
||||
public void handle(org.scalatools.testing.Event oldEvent) {
|
||||
newEventHandler.handle(new EventWrapper(oldEvent, fullyQualifiedName, isModule));
|
||||
}
|
||||
}
|
||||
|
||||
class EventWrapper implements Event {
|
||||
|
||||
private org.scalatools.testing.Event oldEvent;
|
||||
private String className;
|
||||
private boolean classIsModule;
|
||||
|
||||
public EventWrapper(org.scalatools.testing.Event oldEvent, String className, boolean classIsModule) {
|
||||
this.oldEvent = oldEvent;
|
||||
this.className = className;
|
||||
this.classIsModule = classIsModule;
|
||||
}
|
||||
|
||||
public String fullyQualifiedName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public boolean isModule() {
|
||||
return classIsModule;
|
||||
}
|
||||
|
||||
public Selector selector() {
|
||||
return new TestSelector(oldEvent.testName());
|
||||
}
|
||||
|
||||
public Status status() {
|
||||
switch (oldEvent.result()) {
|
||||
case Success:
|
||||
return Status.Success;
|
||||
case Error:
|
||||
return Status.Error;
|
||||
case Failure:
|
||||
return Status.Failure;
|
||||
case Skipped:
|
||||
return Status.Skipped;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid status.");
|
||||
}
|
||||
}
|
||||
|
||||
public Throwable throwable() {
|
||||
return oldEvent.error();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RunnerWrapper implements Runner {
|
||||
|
||||
private org.scalatools.testing.Framework oldFramework;
|
||||
private ClassLoader testClassLoader;
|
||||
private String[] args;
|
||||
|
||||
public RunnerWrapper(org.scalatools.testing.Framework oldFramework, ClassLoader testClassLoader, String[] args) {
|
||||
this.oldFramework = oldFramework;
|
||||
this.testClassLoader = testClassLoader;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public Task task(final String fullyQualifiedName, final Fingerprint fingerprint, boolean explicitlySpecified, Selector[] selectors) {
|
||||
return new Task() {
|
||||
public String[] tags() {
|
||||
return new String[0]; // Old framework does not support tags
|
||||
}
|
||||
|
||||
private org.scalatools.testing.Logger createOldLogger(final Logger logger) {
|
||||
return new org.scalatools.testing.Logger() {
|
||||
public boolean ansiCodesSupported() { return logger.ansiCodesSupported(); }
|
||||
public void error(String msg) { logger.error(msg); }
|
||||
public void warn(String msg) { logger.warn(msg); }
|
||||
public void info(String msg) { logger.info(msg); }
|
||||
public void debug(String msg) { logger.debug(msg); }
|
||||
public void trace(Throwable t) { logger.trace(t); }
|
||||
};
|
||||
}
|
||||
|
||||
private void runRunner(org.scalatools.testing.Runner runner, Fingerprint fingerprint, EventHandler eventHandler) {
|
||||
// Old runner only support subclass fingerprint.
|
||||
final SubclassFingerprint subclassFingerprint = (SubclassFingerprint) fingerprint;
|
||||
org.scalatools.testing.TestFingerprint oldFingerprint =
|
||||
new org.scalatools.testing.TestFingerprint() {
|
||||
public boolean isModule() { return subclassFingerprint.isModule(); }
|
||||
public String superClassName() { return subclassFingerprint.superclassName(); }
|
||||
};
|
||||
runner.run(fullyQualifiedName, oldFingerprint, new EventHandlerWrapper(eventHandler, fullyQualifiedName, subclassFingerprint.isModule()), args);
|
||||
}
|
||||
|
||||
private void runRunner2(org.scalatools.testing.Runner2 runner, Fingerprint fingerprint, EventHandler eventHandler) {
|
||||
org.scalatools.testing.Fingerprint oldFingerprint = null;
|
||||
boolean isModule = false;
|
||||
if (fingerprint instanceof SubclassFingerprint) {
|
||||
final SubclassFingerprint subclassFingerprint = (SubclassFingerprint) fingerprint;
|
||||
oldFingerprint = new org.scalatools.testing.SubclassFingerprint() {
|
||||
public boolean isModule() { return subclassFingerprint.isModule(); }
|
||||
public String superClassName() { return subclassFingerprint.superclassName(); }
|
||||
};
|
||||
isModule = subclassFingerprint.isModule();
|
||||
}
|
||||
else {
|
||||
final AnnotatedFingerprint annotatedFingerprint = (AnnotatedFingerprint) fingerprint;
|
||||
oldFingerprint = new org.scalatools.testing.AnnotatedFingerprint() {
|
||||
public boolean isModule() { return annotatedFingerprint.isModule(); }
|
||||
public String annotationName() { return annotatedFingerprint.annotationName(); }
|
||||
};
|
||||
isModule = annotatedFingerprint.isModule();
|
||||
}
|
||||
runner.run(fullyQualifiedName, oldFingerprint, new EventHandlerWrapper(eventHandler, fullyQualifiedName, isModule), args);
|
||||
}
|
||||
|
||||
public Task[] execute(EventHandler eventHandler, Logger[] loggers) {
|
||||
int length = loggers.length;
|
||||
org.scalatools.testing.Logger[] oldLoggers = new org.scalatools.testing.Logger[length];
|
||||
for (int i=0; i<length; i++) {
|
||||
oldLoggers[i] = createOldLogger(loggers[i]);
|
||||
}
|
||||
|
||||
org.scalatools.testing.Runner runner = oldFramework.testRunner(testClassLoader, oldLoggers);
|
||||
if (runner instanceof org.scalatools.testing.Runner2) {
|
||||
runRunner2((org.scalatools.testing.Runner2) runner, fingerprint, eventHandler);
|
||||
}
|
||||
else {
|
||||
runRunner(runner, fingerprint, eventHandler);
|
||||
}
|
||||
return new Task[0];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public String done() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String[] args() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public String[] remoteArgs() {
|
||||
return new String[0]; // Old framework does not support remoteArgs
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ package sbt
|
|||
*/
|
||||
|
||||
import scala.util.parsing.combinator._
|
||||
import org.scalatools.testing.{Fingerprint, AnnotatedFingerprint, TestFingerprint}
|
||||
import testing.{Fingerprint, AnnotatedFingerprint, TestFingerprint}
|
||||
import DiscoveredParser._
|
||||
|
||||
sealed abstract class Discovered extends Fingerprint with NotNull
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ package sbt
|
|||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, SubclassFingerprint, TestFingerprint}
|
||||
import org.scalatools.testing.{Event, EventHandler, Framework, Runner, Runner2, Logger=>TLogger}
|
||||
import testing.{Logger=>TLogger, Task => TestTask, _}
|
||||
import org.scalatools.testing.{Framework => OldFramework}
|
||||
import classpath.{ClasspathUtilities, DualLoader, FilteredLoader}
|
||||
import scala.annotation.tailrec
|
||||
|
||||
object TestResult extends Enumeration
|
||||
{
|
||||
|
|
@ -17,19 +18,38 @@ object TestResult extends Enumeration
|
|||
object TestFrameworks
|
||||
{
|
||||
val ScalaCheck = new TestFramework("org.scalacheck.ScalaCheckFramework")
|
||||
val ScalaTest = new TestFramework("org.scalatest.tools.ScalaTestFramework")
|
||||
val ScalaTest = new TestFramework("org.scalatest.tools.Framework", "org.scalatest.tools.ScalaTestFramework")
|
||||
val Specs = new TestFramework("org.specs.runner.SpecsFramework")
|
||||
val Specs2 = new TestFramework("org.specs2.runner.SpecsFramework")
|
||||
val JUnit = new TestFramework("com.novocode.junit.JUnitFramework")
|
||||
}
|
||||
|
||||
case class TestFramework(val implClassName: String)
|
||||
case class TestFramework(val implClassNames: String*)
|
||||
{
|
||||
def create(loader: ClassLoader, log: Logger): Option[Framework] =
|
||||
{
|
||||
try { Some(Class.forName(implClassName, true, loader).newInstance.asInstanceOf[Framework]) }
|
||||
catch { case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None }
|
||||
@tailrec
|
||||
private def createFramework(loader: ClassLoader, log: Logger, frameworkClassNames: List[String]): Option[Framework] = {
|
||||
frameworkClassNames match {
|
||||
case head :: tail =>
|
||||
try
|
||||
{
|
||||
Some(Class.forName(head, true, loader).newInstance match {
|
||||
case newFramework: Framework => newFramework
|
||||
case oldFramework: OldFramework => new FrameworkWrapper(oldFramework)
|
||||
})
|
||||
}
|
||||
catch
|
||||
{
|
||||
case e: ClassNotFoundException =>
|
||||
log.debug("Framework implementation '" + head + "' not present.");
|
||||
createFramework(loader, log, tail)
|
||||
}
|
||||
case Nil =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def create(loader: ClassLoader, log: Logger): Option[Framework] =
|
||||
createFramework(loader, log, implClassNames.toList)
|
||||
}
|
||||
final class TestDefinition(val name: String, val fingerprint: Fingerprint)
|
||||
{
|
||||
|
|
@ -43,50 +63,41 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint)
|
|||
override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode
|
||||
}
|
||||
|
||||
final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger)
|
||||
{
|
||||
private[this] def run(testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit =
|
||||
{
|
||||
val loggers = listeners.flatMap(_.contentLogger(testDefinition))
|
||||
val delegate = framework.testRunner(loader, loggers.map(_.log).toArray)
|
||||
try { delegateRun(delegate, testDefinition, handler, args) }
|
||||
finally { loggers.foreach( _.flush() ) }
|
||||
}
|
||||
private[this] def delegateRun(delegate: Runner, testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit =
|
||||
(testDefinition.fingerprint, delegate) match
|
||||
{
|
||||
case (simple: TestFingerprint, _) => delegate.run(testDefinition.name, simple, handler, args)
|
||||
case (basic, runner2: Runner2) => runner2.run(testDefinition.name, basic, handler, args)
|
||||
case _ => sys.error("Framework '" + framework + "' does not support test '" + testDefinition + "'")
|
||||
}
|
||||
final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log: Logger) {
|
||||
|
||||
final def run(testDefinition: TestDefinition, args: Seq[String]): TestResult.Value =
|
||||
final def task(testDefinition: TestDefinition): TestTask =
|
||||
delegate.task(testDefinition.name, testDefinition.fingerprint, false, Array(new SuiteSelector)) // TODO: To pass in correct explicitlySpecified and selectors
|
||||
|
||||
final def run(testDefinition: TestDefinition, testTask: TestTask): (SuiteResult, Seq[TestTask]) =
|
||||
{
|
||||
log.debug("Running " + testDefinition + " with arguments " + args.mkString(", "))
|
||||
log.debug("Running " + testDefinition)
|
||||
val name = testDefinition.name
|
||||
def runTest() =
|
||||
{
|
||||
// here we get the results! here is where we'd pass in the event listener
|
||||
val results = new scala.collection.mutable.ListBuffer[Event]
|
||||
val handler = new EventHandler { def handle(e:Event){ results += e } }
|
||||
run(testDefinition, handler, args.toArray)
|
||||
val loggers = listeners.flatMap(_.contentLogger(testDefinition))
|
||||
val nestedTasks =
|
||||
try testTask.execute(handler, loggers.map(_.log).toArray)
|
||||
finally loggers.foreach( _.flush() )
|
||||
val event = TestEvent(results)
|
||||
safeListenersCall(_.testEvent( event ))
|
||||
event.result
|
||||
(SuiteResult(results), nestedTasks.toSeq)
|
||||
}
|
||||
|
||||
safeListenersCall(_.startGroup(name))
|
||||
try
|
||||
{
|
||||
val result = runTest().getOrElse(TestResult.Passed)
|
||||
safeListenersCall(_.endGroup(name, result))
|
||||
result
|
||||
val (suiteResult, nestedTasks) = runTest()
|
||||
safeListenersCall(_.endGroup(name, suiteResult.result))
|
||||
(suiteResult, nestedTasks)
|
||||
}
|
||||
catch
|
||||
{
|
||||
case e: Throwable =>
|
||||
safeListenersCall(_.endGroup(name, e))
|
||||
TestResult.Error
|
||||
(SuiteResult.Error, Seq.empty[TestTask])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,13 +106,12 @@ final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq
|
|||
}
|
||||
|
||||
object TestFramework
|
||||
{
|
||||
def getTests(framework: Framework): Seq[Fingerprint] =
|
||||
framework.getClass.getMethod("tests").invoke(framework) match
|
||||
{
|
||||
def getFingerprints(framework: Framework): Seq[Fingerprint] =
|
||||
framework.getClass.getMethod("fingerprints").invoke(framework) match
|
||||
{
|
||||
case newStyle: Array[Fingerprint] => newStyle.toList
|
||||
case oldStyle: Array[TestFingerprint] => oldStyle.toList
|
||||
case _ => sys.error("Could not call 'tests' on framework " + framework)
|
||||
case fingerprints: Array[Fingerprint] => fingerprints.toList
|
||||
case _ => sys.error("Could not call 'fingerprints' on framework " + framework)
|
||||
}
|
||||
|
||||
private val ScalaCompilerJarPackages = "scala.tools." :: "jline." :: "ch.epfl.lamp." :: Nil
|
||||
|
|
@ -113,42 +123,43 @@ object TestFramework
|
|||
it.foreach(i => try f(i) catch { case e: Exception => log.trace(e); log.error(e.toString) })
|
||||
|
||||
private[sbt] def hashCode(f: Fingerprint): Int = f match {
|
||||
case s: SubclassFingerprint => (s.isModule, s.superClassName).hashCode
|
||||
case s: SubclassFingerprint => (s.isModule, s.superclassName).hashCode
|
||||
case a: AnnotatedFingerprint => (a.isModule, a.annotationName).hashCode
|
||||
case _ => 0
|
||||
}
|
||||
def matches(a: Fingerprint, b: Fingerprint) =
|
||||
(a, b) match
|
||||
{
|
||||
case (a: SubclassFingerprint, b: SubclassFingerprint) => a.isModule == b.isModule && a.superClassName == b.superClassName
|
||||
case (a: SubclassFingerprint, b: SubclassFingerprint) => a.isModule == b.isModule && a.superclassName == b.superclassName
|
||||
case (a: AnnotatedFingerprint, b: AnnotatedFingerprint) => a.isModule == b.isModule && a.annotationName == b.annotationName
|
||||
case _ => false
|
||||
}
|
||||
def toString(f: Fingerprint): String =
|
||||
f match
|
||||
{
|
||||
case sf: SubclassFingerprint => "subclass(" + sf.isModule + ", " + sf.superClassName + ")"
|
||||
case sf: SubclassFingerprint => "subclass(" + sf.isModule + ", " + sf.superclassName + ")"
|
||||
case af: AnnotatedFingerprint => "annotation(" + af.isModule + ", " + af.annotationName + ")"
|
||||
case _ => f.toString
|
||||
}
|
||||
|
||||
def testTasks(frameworks: Seq[Framework],
|
||||
def testTasks(frameworks: Map[TestFramework, Framework],
|
||||
runners: Map[TestFramework, Runner],
|
||||
testLoader: ClassLoader,
|
||||
tests: Seq[TestDefinition],
|
||||
log: Logger,
|
||||
listeners: Seq[TestReportListener],
|
||||
testArgsByFramework: Map[Framework, Seq[String]]):
|
||||
(() => Unit, Seq[(String, () => TestResult.Value)], TestResult.Value => () => Unit) =
|
||||
(() => Unit, Seq[(String, TestFunction)], TestResult.Value => () => Unit) =
|
||||
{
|
||||
val arguments = testArgsByFramework withDefaultValue Nil
|
||||
val mappedTests = testMap(frameworks, tests, arguments)
|
||||
val mappedTests = testMap(frameworks.values.toSeq, tests, arguments)
|
||||
if(mappedTests.isEmpty)
|
||||
(() => (), Nil, _ => () => () )
|
||||
else
|
||||
createTestTasks(testLoader, mappedTests, tests, log, listeners)
|
||||
createTestTasks(testLoader, runners.map { case (tf, r) => (frameworks(tf), new TestRunner(r, listeners, log))}, mappedTests, tests, log, listeners)
|
||||
}
|
||||
|
||||
private[this] def order(mapped: Map[String, () => TestResult.Value], inputs: Seq[TestDefinition]): Seq[(String, () => TestResult.Value)] =
|
||||
private[this] def order(mapped: Map[String, TestFunction], inputs: Seq[TestDefinition]): Seq[(String, TestFunction)] =
|
||||
for( d <- inputs; act <- mapped.get(d.name) ) yield (d.name, act)
|
||||
|
||||
private[this] def testMap(frameworks: Seq[Framework], tests: Seq[TestDefinition], args: Map[Framework, Seq[String]]):
|
||||
|
|
@ -160,7 +171,7 @@ object TestFramework
|
|||
{
|
||||
for(test <- tests if !map.values.exists(_.contains(test)))
|
||||
{
|
||||
def isTestForFramework(framework: Framework) = getTests(framework).exists {t => matches(t, test.fingerprint) }
|
||||
def isTestForFramework(framework: Framework) = getFingerprints(framework).exists {t => matches(t, test.fingerprint) }
|
||||
for(framework <- frameworks.find(isTestForFramework))
|
||||
map.getOrElseUpdate(framework, new HashSet[TestDefinition]) += test
|
||||
}
|
||||
|
|
@ -171,7 +182,7 @@ object TestFramework
|
|||
}
|
||||
private[this] def mergeDuplicates(framework: Framework, tests: Seq[TestDefinition]): Set[TestDefinition] =
|
||||
{
|
||||
val frameworkPrints = framework.tests.reverse
|
||||
val frameworkPrints = framework.fingerprints.reverse
|
||||
def pickOne(prints: Seq[Fingerprint]): Fingerprint =
|
||||
frameworkPrints.find(prints.toSet) getOrElse prints.head
|
||||
val uniqueDefs =
|
||||
|
|
@ -179,24 +190,25 @@ object TestFramework
|
|||
new TestDefinition(name, pickOne(defs.map(_.fingerprint)))
|
||||
uniqueDefs.toSet
|
||||
}
|
||||
|
||||
private def createTestTasks(loader: ClassLoader, tests: Map[Framework, (Set[TestDefinition], Seq[String])], ordered: Seq[TestDefinition], log: Logger, listeners: Seq[TestReportListener]) =
|
||||
|
||||
private def createTestTasks(loader: ClassLoader, runners: Map[Framework, TestRunner], tests: Map[Framework, (Set[TestDefinition], Seq[String])], ordered: Seq[TestDefinition], log: Logger, listeners: Seq[TestReportListener]) =
|
||||
{
|
||||
val testsListeners = listeners collect { case tl: TestsListener => tl }
|
||||
|
||||
def foreachListenerSafe(f: TestsListener => Unit): () => Unit = () => safeForeach(testsListeners, log)(f)
|
||||
|
||||
import TestResult.{Error,Passed,Failed}
|
||||
|
||||
import TestResult.{Error,Passed,Failed}
|
||||
|
||||
val startTask = foreachListenerSafe(_.doInit)
|
||||
val testTasks =
|
||||
tests flatMap { case (framework, (testDefinitions, testArgs)) =>
|
||||
|
||||
val runner = new TestRunner(framework, loader, listeners, log)
|
||||
for(testDefinition <- testDefinitions) yield
|
||||
{
|
||||
val runTest = () => withContextLoader(loader) { runner.run(testDefinition, testArgs) }
|
||||
(testDefinition.name, runTest)
|
||||
}
|
||||
val runner = runners(framework)
|
||||
for(testDefinition <- testDefinitions) yield
|
||||
{
|
||||
val testTask = withContextLoader(loader) { runner.task(testDefinition) }
|
||||
val testFunction = createTestFunction(loader, testDefinition, runner, testTask)
|
||||
(testDefinition.name, testFunction)
|
||||
}
|
||||
}
|
||||
|
||||
val endTask = (result: TestResult.Value) => foreachListenerSafe(_.doComplete(result))
|
||||
|
|
@ -210,11 +222,20 @@ object TestFramework
|
|||
}
|
||||
def createTestLoader(classpath: Seq[File], scalaInstance: ScalaInstance, tempDir: File): ClassLoader =
|
||||
{
|
||||
val interfaceJar = IO.classLocationFile(classOf[org.scalatools.testing.Framework])
|
||||
val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.")
|
||||
val interfaceJar = IO.classLocationFile(classOf[testing.Framework])
|
||||
val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.") || name.startsWith("sbt.testing.")
|
||||
val notInterfaceFilter = (name: String) => !interfaceFilter(name)
|
||||
val dual = new DualLoader(scalaInstance.loader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
|
||||
val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir)
|
||||
ClasspathUtilities.filterByClasspath(interfaceJar +: classpath, main)
|
||||
}
|
||||
def createTestFunction(loader: ClassLoader, testDefinition: TestDefinition, runner:TestRunner, testTask: TestTask): TestFunction =
|
||||
new TestFunction(testDefinition, runner, (r: TestRunner) => withContextLoader(loader) { r.run(testDefinition, testTask) }) { def tags = testTask.tags }
|
||||
}
|
||||
|
||||
abstract class TestFunction(val testDefinition: TestDefinition, val runner: TestRunner, fun: (TestRunner) => (SuiteResult, Seq[TestTask])) {
|
||||
|
||||
def apply(): (SuiteResult, Seq[TestTask]) = fun(runner)
|
||||
|
||||
def tags: Seq[String]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import org.scalatools.testing.{Logger => TLogger, Event => TEvent, Result => TResult}
|
||||
import testing.{Logger => TLogger, Event => TEvent, Status => TStatus}
|
||||
|
||||
trait TestReportListener
|
||||
{
|
||||
|
|
@ -28,6 +28,24 @@ trait TestsListener extends TestReportListener
|
|||
def doComplete(finalResult: TestResult.Value)
|
||||
}
|
||||
|
||||
final class SuiteResult(val result: TestResult.Value, val passedCount: Int, val failureCount: Int, val errorCount: Int, val skippedCount: Int)
|
||||
object SuiteResult
|
||||
{
|
||||
def apply(events: Seq[TEvent]): SuiteResult =
|
||||
{
|
||||
def count(status: TStatus) = events.count(_.status == status)
|
||||
val overallResult = (TestResult.Passed /: events) { (sum, event) =>
|
||||
val status = event.status
|
||||
if(sum == TestResult.Error || status == TStatus.Error) TestResult.Error
|
||||
else if(sum == TestResult.Failed || status == TStatus.Failure) TestResult.Failed
|
||||
else TestResult.Passed
|
||||
}
|
||||
new SuiteResult (overallResult, count(TStatus.Success), count(TStatus.Failure), count(TStatus.Error), count(TStatus.Skipped))
|
||||
}
|
||||
val Error: SuiteResult = new SuiteResult(TestResult.Error, 0, 0, 0, 0)
|
||||
val Empty: SuiteResult = new SuiteResult(TestResult.Passed, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
abstract class TestEvent extends NotNull
|
||||
{
|
||||
def result: Option[TestResult.Value]
|
||||
|
|
@ -38,9 +56,9 @@ object TestEvent
|
|||
def apply(events: Seq[TEvent]): TestEvent =
|
||||
{
|
||||
val overallResult = (TestResult.Passed /: events) { (sum, event) =>
|
||||
val result = event.result
|
||||
if(sum == TestResult.Error || result == TResult.Error) TestResult.Error
|
||||
else if(sum == TestResult.Failed || result == TResult.Failure) TestResult.Failed
|
||||
val status = event.status
|
||||
if(sum == TestResult.Error || status == TStatus.Error) TestResult.Error
|
||||
else if(sum == TestResult.Failed || status == TStatus.Failure) TestResult.Failed
|
||||
else TestResult.Passed
|
||||
}
|
||||
new TestEvent {
|
||||
|
|
@ -77,43 +95,18 @@ final class TestLogging(val global: TLogger, val logTest: TestDefinition => Cont
|
|||
final class ContentLogger(val log: TLogger, val flush: () => Unit)
|
||||
class TestLogger(val logging: TestLogging) extends TestsListener
|
||||
{
|
||||
import logging.{global => log, logTest}
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
protected val skippedCount, errorsCount, passedCount, failuresCount = new AtomicInteger
|
||||
import logging.{global => log, logTest}
|
||||
|
||||
def startGroup(name: String) {}
|
||||
def testEvent(event: TestEvent): Unit = event.detail.foreach(count)
|
||||
def testEvent(event: TestEvent): Unit = {}
|
||||
def endGroup(name: String, t: Throwable)
|
||||
{
|
||||
log.trace(t)
|
||||
log.error("Could not run test " + name + ": " + t.toString)
|
||||
}
|
||||
def endGroup(name: String, result: TestResult.Value) {}
|
||||
protected def count(event: TEvent): Unit =
|
||||
{
|
||||
val count = event.result match {
|
||||
case TResult.Error => errorsCount
|
||||
case TResult.Success => passedCount
|
||||
case TResult.Failure => failuresCount
|
||||
case TResult.Skipped => skippedCount
|
||||
}
|
||||
count.incrementAndGet()
|
||||
}
|
||||
def doInit
|
||||
{
|
||||
for (count <- List(skippedCount, errorsCount, passedCount, failuresCount)) count.set(0)
|
||||
}
|
||||
/** called once, at end. */
|
||||
def doComplete(finalResult: TestResult.Value): Unit =
|
||||
{
|
||||
val (skipped, errors, passed, failures) = (skippedCount.get, errorsCount.get, passedCount.get, failuresCount.get)
|
||||
val totalCount = failures + errors + skipped + passed
|
||||
val postfix = ": Total " + totalCount + ", Failed " + failures + ", Errors " + errors + ", Passed " + passed + ", Skipped " + skipped
|
||||
finalResult match {
|
||||
case TestResult.Error => log.error("Error" + postfix)
|
||||
case TestResult.Passed => log.info("Passed: " + postfix)
|
||||
case TestResult.Failed => log.error("Failed: " + postfix)
|
||||
}
|
||||
}
|
||||
def doInit {}
|
||||
/** called once, at end of test group. */
|
||||
def doComplete(finalResult: TestResult.Value): Unit = {}
|
||||
override def contentLogger(test: TestDefinition): Option[ContentLogger] = Some(logTest(test))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
package sbt
|
||||
|
||||
sealed trait SourcePosition
|
||||
|
||||
sealed trait FilePosition extends SourcePosition {
|
||||
def path: String
|
||||
def startLine: Int
|
||||
}
|
||||
|
||||
case object NoPosition extends SourcePosition
|
||||
|
||||
final case class LinePosition(path: String, startLine: Int) extends FilePosition
|
||||
|
||||
final case class LineRange(start: Int, end: Int) {
|
||||
def shift(n: Int) = new LineRange(start + n, end + n)
|
||||
}
|
||||
|
||||
final case class RangePosition(path: String, range: LineRange) extends FilePosition {
|
||||
def startLine = range.start
|
||||
}
|
||||
package sbt
|
||||
|
||||
sealed trait SourcePosition
|
||||
|
||||
sealed trait FilePosition extends SourcePosition {
|
||||
def path: String
|
||||
def startLine: Int
|
||||
}
|
||||
|
||||
case object NoPosition extends SourcePosition
|
||||
|
||||
final case class LinePosition(path: String, startLine: Int) extends FilePosition
|
||||
|
||||
final case class LineRange(start: Int, end: Int) {
|
||||
def shift(n: Int) = new LineRange(start + n, end + n)
|
||||
}
|
||||
|
||||
final case class RangePosition(path: String, range: LineRange) extends FilePosition {
|
||||
def startLine = range.start
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue