mirror of https://github.com/sbt/sbt.git
testQuick: track previous test status.
This commit is contained in:
parent
fe753768d9
commit
6e0ad08ad3
|
|
@ -64,11 +64,8 @@ object Defaults extends BuildCommon
|
|||
logBuffered :== false,
|
||||
connectInput :== false,
|
||||
cancelable :== false,
|
||||
cancelable :== false,
|
||||
autoScalaLibrary :== true,
|
||||
onLoad <<= onLoad ?? idFun[State],
|
||||
tags in test := Seq(Tags.Test -> 1),
|
||||
tags in testOnly <<= tags in test,
|
||||
onUnload <<= (onUnload ?? idFun[State]),
|
||||
onUnload <<= (onUnload, taskTemporaryDirectory) { (f, dir) => s => { try f(s) finally IO.delete(dir) } },
|
||||
watchingMessage <<= watchingMessage ?? Watched.defaultWatchingMessage,
|
||||
|
|
@ -78,8 +75,6 @@ object Defaults extends BuildCommon
|
|||
trapExit in run :== true,
|
||||
traceLevel in run :== 0,
|
||||
traceLevel in runMain :== 0,
|
||||
logBuffered in testOnly :== true,
|
||||
logBuffered in test :== true,
|
||||
traceLevel in console :== Int.MaxValue,
|
||||
traceLevel in consoleProject :== Int.MaxValue,
|
||||
autoCompilerPlugins :== true,
|
||||
|
|
@ -121,7 +116,13 @@ object Defaults extends BuildCommon
|
|||
includeFilter in unmanagedResources :== AllPassFilter,
|
||||
excludeFilter :== (".*" - ".") || HiddenFileFilter,
|
||||
pomIncludeRepository :== Classpaths.defaultRepositoryFilter
|
||||
))
|
||||
) ++ {
|
||||
val testSettings = for (task <- Seq(test, testOnly, testQuick)) yield Seq[Setting[_]](
|
||||
logBuffered in task := true,
|
||||
tags in task := Seq(Tags.Test -> 1)
|
||||
)
|
||||
testSettings.flatten
|
||||
})
|
||||
def projectCore: Seq[Setting[_]] = Seq(
|
||||
name <<= thisProject(_.id),
|
||||
logManager <<= extraLoggers(LogManager.defaults),
|
||||
|
|
@ -285,16 +286,18 @@ object Defaults extends BuildCommon
|
|||
definedTestNames <<= definedTests map ( _.map(_.name).distinct) storeAs definedTestNames triggeredBy compile,
|
||||
testListeners in GlobalScope :== Nil,
|
||||
testOptions in GlobalScope :== Nil,
|
||||
testExecution in test <<= testExecutionTask(test),
|
||||
testExecution in testOnly <<= testExecutionTask(testOnly),
|
||||
testFilter in testOnly :== (selectedFilter _),
|
||||
testFilter in testQuick <<= testQuickFilter,
|
||||
executeTests <<= (streams in test, loadedTestFrameworks, testExecution in test, testLoader, definedTests, resolvedScoped, state) flatMap {
|
||||
(s, frameworkMap, config, loader, discovered, scoped, st) =>
|
||||
implicit val display = Project.showContextKey(st)
|
||||
Tests(frameworkMap, loader, discovered, config, noTestsMessage(ScopedKey(scoped.scope, test.key)), s.log)
|
||||
},
|
||||
test <<= (executeTests, streams) map { (results, s) => Tests.showResults(s.log, results) },
|
||||
testOnly <<= testOnlyTask
|
||||
testOnly <<= inputTests(testOnly),
|
||||
testQuick <<= inputTests(testQuick)
|
||||
) ++ (
|
||||
for (task <- Seq(test, testOnly, testQuick)) yield testExecution in task <<= testExecutionTask(task)
|
||||
)
|
||||
private[this] def noTestsMessage(scoped: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): String =
|
||||
"No tests to run for " + display(scoped)
|
||||
|
|
@ -324,7 +327,14 @@ object Defaults extends BuildCommon
|
|||
def testExecutionTask(task: Scoped): Initialize[Task[Tests.Execution]] =
|
||||
(testOptions in task, parallelExecution in task, tags in task) map { (opts, par, ts) => new Tests.Execution(opts, par, ts) }
|
||||
|
||||
def testOnlyTask: Initialize[InputTask[Unit]] = inputTests(testOnly)
|
||||
def testQuickFilter: Initialize[Task[Seq[String] => String => Boolean]] =
|
||||
(compile in test, cacheDirectory) map {
|
||||
case (analysis, dir) =>
|
||||
val succeeded = new TestResultFilter(dir / "succeeded_tests")
|
||||
args => test => selectedFilter(args)(test) && {
|
||||
!succeeded(test) // Add recompilation status.
|
||||
}
|
||||
}
|
||||
|
||||
def inputTests(key: InputKey[_]): Initialize[InputTask[Unit]] =
|
||||
InputTask( loadForParser(definedTestNames)( (s, i) => testOnlyParser(s, i getOrElse Nil) ) ) { result =>
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ object Keys
|
|||
val executeTests = TaskKey[Tests.Output]("execute-tests", "Executes all tests, producing a report.")
|
||||
val test = TaskKey[Unit]("test", "Executes all tests.")
|
||||
val testOnly = InputKey[Unit]("test-only", "Executes the tests provided as arguments or all tests if no arguments are provided.")
|
||||
val testQuick = InputKey[Unit]("test-quick", "Executes the tests that either failed before, were not run or whose transitive dependencies changed, among those provided as arguments.")
|
||||
val testOptions = TaskKey[Seq[TestOption]]("test-options", "Options for running tests.")
|
||||
val testFrameworks = SettingKey[Seq[TestFramework]]("test-frameworks", "Registered, although not necessarily present, test frameworks.")
|
||||
val testListeners = TaskKey[Seq[TestReportListener]]("test-listeners", "Defines test listeners.")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009, 2010, 2011, 2012 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
import scala.collection.mutable.Map
|
||||
|
||||
// Assumes exclusive ownership of the file.
|
||||
private[sbt] class TestStatusReporter(f: File) extends TestsListener
|
||||
{
|
||||
private lazy val succeeded = TestStatus.read(f)
|
||||
|
||||
def doInit {}
|
||||
def startGroup(name: String) { succeeded remove name }
|
||||
def testEvent(event: TestEvent) {}
|
||||
def endGroup(name: String, t: Throwable) {}
|
||||
def endGroup(name: String, result: TestResult.Value) {
|
||||
if(result == TestResult.Passed)
|
||||
succeeded(name) = System.currentTimeMillis
|
||||
}
|
||||
def doComplete(finalResult: TestResult.Value) {
|
||||
TestStatus.write(succeeded, "Successful Tests", f)
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] class TestResultFilter(f: File) extends (String => Boolean) with NotNull
|
||||
{
|
||||
private lazy val succeeded = TestStatus.read(f)
|
||||
def apply(test: String) = succeeded.contains(test)
|
||||
}
|
||||
|
||||
private object TestStatus
|
||||
{
|
||||
import java.util.Properties
|
||||
def read(f: File): Map[String, Long] =
|
||||
{
|
||||
import scala.collection.JavaConversions.{enumerationAsScalaIterator, propertiesAsScalaMap}
|
||||
val properties = new Properties
|
||||
IO.load(properties, f)
|
||||
properties map {case (k, v) => (k, v.toLong)}
|
||||
}
|
||||
def write(map: Map[String, Long], label: String, f: File)
|
||||
{
|
||||
val properties = new Properties
|
||||
for( (test, lastSuccessTime) <- map)
|
||||
properties.setProperty(test, lastSuccessTime.toString)
|
||||
IO.write(properties, label, f)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.impl
|
||||
import sbt._
|
||||
|
||||
import java.io.File
|
||||
import scala.collection.mutable.{HashMap, Map}
|
||||
|
||||
/** Only intended to be used once per instance. */
|
||||
private[sbt] class TestStatusReporter(path: Path, log: Logger) extends TestsListener
|
||||
{
|
||||
private lazy val succeeded: Map[String, Long] = TestStatus.read(path, log)
|
||||
|
||||
def doInit {}
|
||||
def startGroup(name: String) { succeeded remove name }
|
||||
def testEvent(event: TestEvent) {}
|
||||
def endGroup(name: String, t: Throwable) {}
|
||||
def endGroup(name: String, result: Result.Value)
|
||||
{
|
||||
if(result == Result.Passed)
|
||||
succeeded(name) = System.currentTimeMillis
|
||||
}
|
||||
def doComplete(finalResult: Result.Value) { complete() }
|
||||
def doComplete(t: Throwable) { complete() }
|
||||
|
||||
private def complete()
|
||||
{
|
||||
TestStatus.write(succeeded, "Successful Tests", path, log)
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] class TestQuickFilter(testAnalysis: CompileAnalysis, failedOnly: Boolean, path: Path, log: Logger) extends (String => Boolean) with NotNull
|
||||
{
|
||||
private lazy val exclude = TestStatus.read(path, log)
|
||||
private lazy val map = testAnalysis.testSourceMap
|
||||
def apply(test: String) =
|
||||
exclude.get(test) match
|
||||
{
|
||||
case None => true // include because this test has not been run or did not succeed
|
||||
case Some(lastSuccessTime) => // succeeded the last time it was run
|
||||
if(failedOnly)
|
||||
false // don't include because the last time succeeded
|
||||
else
|
||||
testAnalysis.products(map(test)) match
|
||||
{
|
||||
case None => true
|
||||
case Some(products) => products.exists(lastSuccessTime <= _.lastModified) // include if the test is newer than the last run
|
||||
}
|
||||
}
|
||||
}
|
||||
private object TestStatus
|
||||
{
|
||||
import java.util.Properties
|
||||
def read(path: Path, log: Logger): Map[String, Long] =
|
||||
{
|
||||
val map = new HashMap[String, Long]
|
||||
val properties = new Properties
|
||||
logError(PropertiesUtilities.load(properties, path, log), "loading", log)
|
||||
for(test <- PropertiesUtilities.propertyNames(properties))
|
||||
map.put(test, properties.getProperty(test).toLong)
|
||||
map
|
||||
}
|
||||
def write(map: Map[String, Long], label: String, path: Path, log: Logger)
|
||||
{
|
||||
val properties = new Properties
|
||||
for( (test, lastSuccessTime) <- map)
|
||||
properties.setProperty(test, lastSuccessTime.toString)
|
||||
logError(PropertiesUtilities.write(properties, label, path, log), "writing", log)
|
||||
}
|
||||
private def logError(result: Option[String], action: String, log: Logger)
|
||||
{
|
||||
result.foreach(msg => log.error("Error " + action + " test status: " + msg))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue