org.scalatest.tools.ScalaTestFramework.scala Maven / Gradle / Ivy
package org.scalatest.tools
import org.scalatools.testing._
import org.scalatest.tools.Runner.parsePropertiesArgsIntoMap
import org.scalatest.tools.Runner.parseCompoundArgIntoSet
import SuiteDiscoveryHelper._
import StringReporter.colorizeLinesIndividually
import org.scalatest.Suite.formatterForSuiteStarting
import org.scalatest.Suite.formatterForSuiteCompleted
import org.scalatest.Suite.formatterForSuiteAborted
import org.scalatest.events.SuiteStarting
import org.scalatest.events.SuiteCompleted
import org.scalatest.events.SuiteAborted
import org.scalatest.events.SeeStackDepthException
import org.scalatest.events.TopOfClass
import org.scalatest._
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.CountDownLatch
/**
* Class that makes ScalaTest tests visible to sbt.
*
*
* To use ScalaTest from within sbt, simply add a line like this to your project file (for sbt 0.1.0 or higher):
*
*
*
* libraryDependencies += "org.scalatest" % "scalatest_2.9.0" % "1.6.1" % "test"
*
*
*
* The above line of code will work for any version of Scala 2.9 (for example, it works for Scala 2.9.0-1).
*
*
*
* libraryDependencies += "org.scalatest" % "scalatest_2.8.1" % "1.5.1" % "test"
*
*
*
* You can configure the output shown when running with sbt in four ways: 1) turn off color, 2) show
* short stack traces, 3) full stack traces, and 4) show durations for everything. To do that
* you need to add test options, like this:
*
*
*
* override def testOptions = super.testOptions ++
* Seq(TestArgument(TestFrameworks.ScalaTest, "-oD"))
*
*
*
* After the -o, place any combination of:
*
*
*
* - D - show durations
* - S - show short stack traces
* - F - show full stack traces
* - W - without color
*
*
*
* For example, "-oDF" would show full stack traces and durations (the amount
* of time spent in each test).
*
*
* @author Bill Venners
* @author Josh Cough
*/
class ScalaTestFramework extends Framework {
/**
* Returns "ScalaTest"
, the human readable name for this test framework.
*/
def name = "ScalaTest"
/**
* Returns an array containing fingerprint for ScalaTest's test, which are classes
* whose superclass name is org.scalatest.Suite
* or is annotated with org.scalatest.WrapWith
.
*/
def tests =
Array(
new org.scalatools.testing.TestFingerprint {
def superClassName = "org.scalatest.Suite"
def isModule = false
},
new org.scalatools.testing.AnnotatedFingerprint {
def annotationName = "org.scalatest.WrapWith"
def isModule = false
}
)
object RunConfig {
private class SbtLogInfoReporter(loggers: Array[Logger], presentAllDurations: Boolean, presentInColor: Boolean, presentShortStackTraces: Boolean, presentFullStackTraces: Boolean)
extends StringReporter(presentAllDurations, presentInColor, presentShortStackTraces, presentFullStackTraces, false) {
protected def printPossiblyInColor(text: String, ansiColor: String) {
loggers.foreach { logger =>
logger.info(if (logger.ansiCodesSupported && presentInColor) colorizeLinesIndividually(text, ansiColor) else text)
}
}
def dispose() = ()
}
private var reporter: DispatchReporter = null
private var reporterConfigs: ReporterConfigurations = null
private var filter: Filter = null
private var configMap: Map[String, String] = null
private val resultHolder = new SuiteResultHolder()
def getConfigurations(args: Array[String], loggers: Array[Logger], eventHandler: EventHandler, testLoader: ClassLoader) =
synchronized {
if (reporterConfigs == null) {
// Why are we getting rid of empty strings? Were empty strings coming in from sbt? -bv 11/09/2011
val translator = new FriendlyParamsTranslator()
val (propertiesArgsList, includesArgsList, excludesArgsList, repoArgsList, concurrentList, memberOnlyList, wildcardList,
suiteList, junitList, testngList) = translator.parsePropsAndTags(args.filter(!_.equals("")))
configMap = parsePropertiesArgsIntoMap(propertiesArgsList)
val tagsToInclude: Set[String] = parseCompoundArgIntoSet(includesArgsList, "-n")
val tagsToExclude: Set[String] = parseCompoundArgIntoSet(excludesArgsList, "-l")
filter = org.scalatest.Filter(if (tagsToInclude.isEmpty) None else Some(tagsToInclude), tagsToExclude)
// If no reporters specified, just give them a default stdout reporter
reporterConfigs = Runner.parseReporterArgsIntoConfigurations(if(repoArgsList.isEmpty) "-o" :: Nil else repoArgsList)
}
object SbtReporterFactory extends ReporterFactory {
override def createStandardOutReporter(configSet: Set[ReporterConfigParam]) = {
if (configSetMinusNonFilterParams(configSet).isEmpty)
new SbtLogInfoReporter(
loggers,
configSet.contains(PresentAllDurations),
!configSet.contains(PresentWithoutColor),
configSet.contains(PresentShortStackTraces) || configSet.contains(PresentFullStackTraces),
configSet.contains(PresentFullStackTraces) // If they say both S and F, F overrules
)
else
new FilterReporter(
new SbtLogInfoReporter(
loggers,
configSet.contains(PresentAllDurations),
!configSet.contains(PresentWithoutColor),
configSet.contains(PresentShortStackTraces) || configSet.contains(PresentFullStackTraces),
configSet.contains(PresentFullStackTraces) // If they say both S and F, F overrules
),
configSet
)
}
}
if (reporter == null || reporter.isDisposed)
reporter = SbtReporterFactory.getDispatchReporter(reporterConfigs, None, None, testLoader, Some(resultHolder))
(reporter, filter, configMap)
}
private val atomicLatch = new AtomicReference(new CountDownLatch(0))
def increaseLatch() {
synchronized {
val current = atomicLatch.get()
atomicLatch.set(new CountDownLatch((current.getCount() + 1).toInt))
}
}
def decreaseLatch() {
synchronized {
val latch = atomicLatch.get
latch.countDown()
if (latch.getCount() == 0) {
reporter match {
case resourcefulRep: ResourcefulReporter =>
resourcefulRep.dispose()
case _ =>
}
reporter = null
reporterConfigs = null
filter = null
configMap = null
}
}
}
}
/**
* Returns an org.scalatools.testing.Runner
that will load test classes via the passed testLoader
* and direct output from running the tests to the passed array of Logger
s.
*/
def testRunner(testLoader: ClassLoader, loggers: Array[Logger]) = {
new ScalaTestRunner(testLoader, loggers)
}
/**The test runner for ScalaTest suites. It is compiled in a second step after the rest of sbt.*/
private[tools] class ScalaTestRunner(val testLoader: ClassLoader, val loggers: Array[Logger]) extends org.scalatools.testing.Runner2 {
/*
test-only FredSuite -- -A -B -C -d all things to right of == come in as a separate string in the array
the other way is to set up the options and when I say test it always comes in that way
new wqay, if one framework
testOptions in Test += Tests.Arguments("-d", "-g")
so each of those would come in as one separate string in the aray
testOptions in Test += Tests.Arguments(TestFrameworks.ScalaTest, "-d", "-g")
Remember:
maybe add a distributor like thing to run
maybe add some event things like pending, ignored as well skipped
maybe a call back for the summary
st look at wiki on xsbt
tasks & commands. commands have full control over everything.
tasks are more integrated, don't need to know as much.
write a sbt plugin to deploy the task.
Commands that should work:
-Ddbname=testdb -Dserver=192.168.1.188
Can't do a runpath
Can add more reporters. -g seems odd, but could be done, -o seems odd. Maybe it is a no-op. -e could work. -r for sure. -u for sure.
Ask Mark about -o. If there's some way to turn off his output, then that could mean -o. Or maybe -o is the default, which I think
it should be for runner anyway, and then if you say -g you don't get -o. Meaning I don't send the strings to log. yes, -o maybe
means log in the sbt case.
Reporters can be configured.
Tags to include and exclude: -n "CheckinTests FunctionalTests" -l "SlowTests NetworkTests"
*/
def run(testClassName: String, fingerprint: Fingerprint, eventHandler: EventHandler, args: Array[String]) {
val suiteClass = Class.forName(testClassName, true, testLoader)
//println("sbt args: " + args.toList)
if (isAccessibleSuite(suiteClass) || isRunnable(suiteClass)) {
val (reporter, filter, configMap) = RunConfig.getConfigurations(args, loggers, eventHandler, testLoader)
val report = new SbtReporter(eventHandler, Some(reporter))
val tracker = new Tracker
val suiteStartTime = System.currentTimeMillis
val wrapWithAnnotation = suiteClass.getAnnotation(classOf[WrapWith])
val suite =
if (wrapWithAnnotation == null)
suiteClass.newInstance.asInstanceOf[Suite]
else {
val suiteClazz = wrapWithAnnotation.value
val constructorList = suiteClazz.getDeclaredConstructors()
val constructor = constructorList.find { c =>
val types = c.getParameterTypes
types.length == 1 && types(0) == classOf[java.lang.Class[_]]
}
constructor.get.newInstance(suiteClass).asInstanceOf[Suite]
}
val formatter = formatterForSuiteStarting(suite)
RunConfig.increaseLatch()
report(SuiteStarting(tracker.nextOrdinal(), suite.suiteName, suite.suiteId, Some(suiteClass.getName), formatter, Some(TopOfClass(suiteClass.getName))))
try { // TODO: I had to pass Set.empty for chosen styles now. Fix this later.
suite.run(None, Args(report, Stopper.default, filter, configMap, None, tracker, Set.empty))
val formatter = formatterForSuiteCompleted(suite)
val duration = System.currentTimeMillis - suiteStartTime
report(SuiteCompleted(tracker.nextOrdinal(), suite.suiteName, suite.suiteId, Some(suiteClass.getName), Some(duration), formatter, Some(TopOfClass(suiteClass.getName))))
}
catch {
case e: Exception => {
// TODO: Could not get this from Resources. Got:
// java.util.MissingResourceException: Can't find bundle for base name org.scalatest.ScalaTestBundle, locale en_US
// TODO Chee Seng, I wonder why we couldn't access resources, and if that's still true. I'd rather get this stuff
// from the resource file so we can later localize.
val rawString = "Exception encountered when attempting to run a suite with class name: " + suiteClass.getName
val formatter = formatterForSuiteAborted(suite, rawString)
val duration = System.currentTimeMillis - suiteStartTime
report(SuiteAborted(tracker.nextOrdinal(), rawString, suite.suiteName, suite.suiteId, Some(suiteClass.getName), Some(e), Some(duration), formatter, Some(SeeStackDepthException)))
}
}
finally {
RunConfig.decreaseLatch()
}
}
else throw new IllegalArgumentException("Class is not an accessible org.scalatest.Suite: " + testClassName)
}
private val emptyClassArray = new Array[java.lang.Class[T] forSome {type T}](0)
private class SbtReporter(eventHandler: EventHandler, report: Option[DispatchReporter]) extends Reporter {
import org.scalatest.events._
def fireEvent(tn: String, r: Result, e: Option[Throwable]) = {
eventHandler.handle(
new org.scalatools.testing.Event {
def testName = tn
def description = tn
def result = r
def error = e getOrElse null
}
)
}
override def apply(event: Event) {
report match {
case Some(report) => report(event)
case None =>
}
event match {
// the results of running an actual test
case t: TestPending => fireEvent(t.testName, Result.Skipped, None)
case t: TestFailed => fireEvent(t.testName, Result.Failure, t.throwable)
case t: TestSucceeded => fireEvent(t.testName, Result.Success, None)
case t: TestIgnored => fireEvent(t.testName, Result.Skipped, None)
case t: TestCanceled => fireEvent(t.testName, Result.Skipped, None)
case t: SuiteAborted => fireEvent("!!! Suite Aborted !!!", Result.Failure, t.throwable)
case _ =>
}
}
def dispose() {
report match {
case Some(report) =>
report.dispatchDisposeAndWaitUntilDone()
case None =>
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy