Merge pull request #3066 from eed3si9n/wip/testing

[sbt 1.0] Proof-of-concept testing events
This commit is contained in:
eugene yokota 2017-04-04 03:31:33 -07:00 committed by GitHub
commit 40f6c4d01b
44 changed files with 944 additions and 184 deletions

View File

@ -14,4 +14,5 @@ Migration notes
- Log options `-error`, `-warn`, `-info`, `-debug` are added as shorthand for `"early(error)"` etc.
- `sbt.Process` and `sbt.ProcessExtra` are gone. Use `scala.sys.process` instead.
- `incOptions.value.withNameHashing(...)` option is removed.
- `TestResult.Value` is now `TestResult`.
- the scripted plugin is cross-versioned now, so you must use %% when depending on it

View File

@ -101,11 +101,14 @@ lazy val bundledLauncherProj =
// Runner for uniform test interface
lazy val testingProj = (project in file("testing")).
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
dependsOn(testAgentProj).
settings(
baseSettings,
name := "Testing",
libraryDependencies ++= Seq(testInterface,launcherInterface)
libraryDependencies ++= Seq(testInterface,launcherInterface, sjsonNewScalaJson),
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats
).
configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)

View File

@ -12,6 +12,7 @@ import Tests.{ Output => TestOutput, _ }
import sbt.io.IO
import sbt.util.Logger
import sbt.ConcurrentRestrictions.Tag
import sbt.protocol.testing._
private[sbt] object ForkTests {
def apply(runners: Map[TestFramework, Runner], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger, tag: Tag): Task[TestOutput] = {

View File

@ -2,6 +2,7 @@ package sbt
import sbt.Tests.{ Output, Summary }
import sbt.util.{ Level, Logger }
import sbt.protocol.testing.TestResult
/**
* Logs information about tests after they finish.
@ -138,7 +139,7 @@ object TestResultLogger {
})
val printFailures = TestResultLogger((log, results, _) => {
def select(resultTpe: TestResult.Value) = results.events collect {
def select(resultTpe: TestResult) = results.events collect {
case (name, tpe) if tpe.result == resultTpe =>
scala.reflect.NameTransformer.decode(name)
}

View File

@ -15,7 +15,9 @@ import ConcurrentRestrictions.Tag
import testing.{ AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner, TaskDef, SuiteSelector, Task => TestTask }
import scala.annotation.tailrec
import sbt.internal.util.ManagedLogger
import sbt.util.Logger
import sbt.protocol.testing.TestResult
sealed trait TestOption
object Tests {
@ -26,7 +28,7 @@ object Tests {
* @param events The result of each test group (suite) executed during this test run.
* @param summaries Explicit summaries directly provided by test frameworks. This may be empty, in which case a default summary will be generated.
*/
final case class Output(overall: TestResult.Value, events: Map[String, SuiteResult], summaries: Iterable[Summary])
final case class Output(overall: TestResult, events: Map[String, SuiteResult], summaries: Iterable[Summary])
/**
* Summarizes a test run.
@ -162,7 +164,7 @@ object Tests {
in.filter(t => seen.add(f(t)))
}
def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, runners: Map[TestFramework, Runner], 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: ManagedLogger): Task[Output] =
{
val o = processOptions(config, discovered, log)
testTask(testLoader, frameworks, runners, o.tests, o.setup, o.cleanup, log, o.testListeners, config)
@ -170,7 +172,7 @@ object Tests {
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], config: Execution): Task[Output] =
log: ManagedLogger, testListeners: Seq[TestReportListener], config: Execution): Task[Output] =
{
def fj(actions: Iterable[() => Unit]): Task[Unit] = nop.dependsOn(actions.toSeq.fork(_()): _*)
def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map { a => () => a(loader) }
@ -249,12 +251,20 @@ object Tests {
def processResults(results: Iterable[(String, SuiteResult)]): Output =
Output(overall(results.map(_._2.result)), results.toMap, Iterable.empty)
private def severity(r: TestResult): Int =
r match {
case TestResult.Passed => 0
case TestResult.Failed => 1
case TestResult.Error => 2
}
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
if (results.isEmpty)
task { Output(TestResult.Passed, Map.empty, Nil) }
else if (parallel)
reduced(results.toIndexedSeq, {
case (Output(v1, m1, _), Output(v2, m2, _)) => Output(if (v1.id < v2.id) v2 else v1, m1 ++ m2, Iterable.empty)
case (Output(v1, m1, _), Output(v2, m2, _)) => Output(if (severity(v1) < severity(v2)) v2 else v1, m1 ++ m2, Iterable.empty)
})
else {
def sequence(tasks: List[Task[Output]], acc: List[Output]): Task[List[Output]] = tasks match {
@ -266,8 +276,8 @@ object Tests {
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 overall(results: Iterable[TestResult]): TestResult =
((TestResult.Passed: TestResult) /: results) { (acc, result) => if (severity(acc) < severity(result)) result else acc }
def discover(frameworks: Seq[Framework], analysis: CompileAnalysis, log: Logger): (Seq[TestDefinition], Set[String]) =
discover(frameworks flatMap TestFramework.getFingerprints, allDefs(analysis), log)

View File

@ -3,51 +3,44 @@
*/
package sbt
import scala.concurrent.duration.FiniteDuration
import sbt.internal._
import sbt.internal.util.Attributed.data
import Scope.{ fillTaskAxis, GlobalScope, ThisScope }
import sbt.internal.librarymanagement.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties }
import Project.{ inConfig, inScope, inTask, richInitialize, richInitializeTask, richTaskSessionVar }
import Def.{ Initialize, ScopedKey, Setting, SettingsDefinition }
import sbt.internal.librarymanagement.{ CustomPomParser, DependencyFilter }
import sbt.librarymanagement.Artifact.{ DocClassifier, SourceClassifier }
import sbt.librarymanagement.{ Configuration, Configurations, ConflictManager, CrossVersion, MavenRepository, Resolver, ScalaArtifacts, UpdateOptions }
import sbt.librarymanagement.Configurations.{ Compile, CompilerPlugin, IntegrationTest, names, Provided, Runtime, Test }
import sbt.librarymanagement.CrossVersion.{ binarySbtVersion, binaryScalaVersion, partialVersion }
import sbt.internal.util.complete._
import std.TaskExtra._
import testing.{ Framework, Runner, AnnotatedFingerprint, SubclassFingerprint }
import sjsonnew.{ IsoLList, JsonFormat, LList, LNil }, LList.:*:
import sbt.internal.util.CacheImplicits._
import sbt.librarymanagement.{ `package` => _, _ }
import sbt.internal.librarymanagement._
import sbt.internal.librarymanagement.syntax._
import sbt.internal.util._
import sbt.util.{ Level, Logger, ShowLines }
import scala.xml.NodeSeq
import scala.util.control.NonFatal
import org.apache.ivy.core.module.{ descriptor, id }
import descriptor.ModuleDescriptor, id.ModuleRevisionId
import java.io.{ File, PrintWriter }
import java.net.{ URI, URL }
import java.util.concurrent.{ TimeUnit, Callable }
import sbt.internal.CommandStrings.ExportStream
import xsbti.{ CrossValue, Maybe }
import sbt.util.InterfaceUtil.{ f1, o2m }
import sbt.internal.util.Types._
import sbt.internal.io.WatchState
import sbt.io.{ AllPassFilter, FileFilter, GlobFilter, HiddenFileFilter, IO, NameFilter, NothingFilter, Path, PathFinder, SimpleFileFilter, DirectoryFilter, Hash }
import Path._
import sbt.io.syntax._
import Keys._
import org.apache.ivy.core.module.{ descriptor, id }, descriptor.ModuleDescriptor, id.ModuleRevisionId
import Project.{ inConfig, inScope, inTask, richInitialize, richInitializeTask, richTaskSessionVar }
import sbt.internal._
import sbt.internal.CommandStrings.ExportStream
import sbt.internal.io.WatchState
import sbt.internal.librarymanagement._
import sbt.internal.librarymanagement.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties }
import sbt.internal.librarymanagement.syntax._
import sbt.internal.librarymanagement.{ CustomPomParser, DependencyFilter }
import sbt.internal.testing.TestLogger
import sbt.internal.util._
import sbt.internal.util.Attributed.data
import sbt.internal.util.CacheImplicits._
import sbt.internal.util.complete._
import sbt.internal.util.Types._
import sbt.io.syntax._
import sbt.io.{ AllPassFilter, FileFilter, GlobFilter, HiddenFileFilter, IO, NameFilter, NothingFilter, Path, PathFinder, SimpleFileFilter, DirectoryFilter, Hash }, Path._
import sbt.librarymanagement.Artifact.{ DocClassifier, SourceClassifier }
import sbt.librarymanagement.Configurations.{ Compile, CompilerPlugin, IntegrationTest, names, Provided, Runtime, Test }
import sbt.librarymanagement.CrossVersion.{ binarySbtVersion, binaryScalaVersion, partialVersion }
import sbt.librarymanagement.{ `package` => _, _ }
import sbt.librarymanagement.{ Configuration, Configurations, ConflictManager, CrossVersion, MavenRepository, Resolver, ScalaArtifacts, UpdateOptions }
import sbt.util.InterfaceUtil.{ f1, o2m }
import sbt.util.{ Level, Logger, ShowLines }
import scala.concurrent.duration.FiniteDuration
import scala.util.control.NonFatal
import scala.xml.NodeSeq
import Scope.{ fillTaskAxis, GlobalScope, ThisScope }
import sjsonnew.{ IsoLList, JsonFormat, LList, LNil }, LList.:*:
import std.TaskExtra._
import testing.{ Framework, Runner, AnnotatedFingerprint, SubclassFingerprint }
import xsbti.compile.IncToolOptionsUtil
import xsbti.{ CrossValue, Maybe }
// incremental compiler
import xsbt.api.Discovery

View File

@ -10,9 +10,12 @@ import sbt.util.Level
import sbt.internal.util._
import sbt.protocol.LogEvent
import sbt.internal.util.codec._
import scala.json.ast.unsafe._
class RelayAppender(name: String) extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) {
import JsonProtocol._
lazy val exchange = StandardMain.exchange
lazy val jsonFormat = new sjsonnew.BasicJsonProtocol
with JValueFormats {}
def append(event: XLogEvent): Unit =
{
@ -26,10 +29,27 @@ class RelayAppender(name: String) extends AbstractAppender(name, null, PatternLa
}
}
def appendLog(level: Level.Value, message: => String): Unit = {
StandardMain.exchange.publishEventMessage(LogEvent(level.toString, message))
exchange.publishEventMessage(LogEvent(level.toString, message))
}
def appendEvent(level: Level.Value, event: AnyRef): Unit =
event match {
case x: StringEvent => StandardMain.exchange.publishEvent(x: AbstractEntry)
case x: StringEvent =>
{
import JsonProtocol._
exchange.publishEvent(x: AbstractEntry)
}
case x: ObjectEvent[_] =>
{
import jsonFormat._
val json = JObject(JField("type", JString(x.contentType)), (
Vector(JField("message", x.json),
JField("level", JString(x.level.toString))) ++
(x.channelName.toVector map { channelName => JField("channelName", JString(channelName)) }) ++
(x.execId.toVector map { execId => JField("execId", JString(execId)) })): _*)
exchange.publishEvent(json: JValue)
}
case _ =>
println(s"appendEvent: ${event.getClass}")
()
}
}

View File

@ -22,7 +22,8 @@ object ContrabandConfig {
case "Option" | "Set" | "scala.Vector" => { tpe => getFormats(oneArg(tpe)) }
case "Map" | "Tuple2" | "scala.Tuple2" => { tpe => twoArgs(tpe).flatMap(getFormats) }
case "Int" | "Long" => { _ => Nil }
case "scala.json.ast.unsafe.JValue" => { _ => "sbt.internal.JValueFormat" :: Nil }
case "sbt.testing.Status" => { _ => "sbt.internal.testing.StatusFormats" :: Nil }
case "scala.json.ast.unsafe.JValue" => { _ => "sbt.internal.util.codec.JValueFormats" :: Nil }
}
/** Returns the list of formats required to encode the given `TpeRef`. */

View File

@ -12,7 +12,7 @@ object Dependencies {
// sbt modules
private val ioVersion = "1.0.0-M9"
private val utilVersion = "1.0.0-M20"
private val utilVersion = "1.0.0-M21"
private val lmVersion = "1.0.0-X7"
private val zincVersion = "1.0.0-X11"

View File

@ -5,6 +5,6 @@
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.JValueFormat with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats =>
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.util.codec.JValueFormats with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat5[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure]("type")
}

View File

@ -11,7 +11,7 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.ChannelAcceptedEventFormats
with sbt.protocol.codec.LogEventFormats
with sbt.protocol.codec.ExecStatusEventFormats
with sbt.internal.JValueFormat
with sbt.internal.util.codec.JValueFormats
with sbt.protocol.codec.SettingQuerySuccessFormats
with sbt.protocol.codec.SettingQueryFailureFormats
with sbt.protocol.codec.EventMessageFormats

View File

@ -5,6 +5,6 @@
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait SettingQueryResponseFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats =>
trait SettingQueryResponseFormats { self: sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats =>
implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = flatUnionFormat2[sbt.protocol.SettingQueryResponse, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure]("type")
}

View File

@ -5,7 +5,7 @@
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait SettingQuerySuccessFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol =>
trait SettingQuerySuccessFormats { self: sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val SettingQuerySuccessFormat: JsonFormat[sbt.protocol.SettingQuerySuccess] = new JsonFormat[sbt.protocol.SettingQuerySuccess] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQuerySuccess = {
jsOpt match {

View File

@ -1,45 +0,0 @@
/*
* Copyright (C) 2017 Lightbend Inc. <http://www.lightbend.com>
*/
package sbt
package internal
import sjsonnew.{ JsonWriter => JW, JsonReader => JR, JsonFormat => JF, _ }
import scala.json.ast.unsafe._
trait JValueFormat { self: sjsonnew.BasicJsonProtocol =>
implicit val JNullJF: JF[JNull.type] = new JF[JNull.type] {
def write[J](x: JNull.type, b: Builder[J]) = b.writeNull()
def read[J](j: Option[J], u: Unbuilder[J]) = JNull
}
implicit val JBooleanJF: JF[JBoolean] = project(_.get, JBoolean(_))
implicit val JStringJF: JF[JString] = project(_.value, JString(_))
implicit val JNumberJF: JF[JNumber] = project(x => BigDecimal(x.value), (x: BigDecimal) => JNumber(x.toString))
implicit val JArrayJF: JF[JArray] = project[JArray, Array[JValue]](_.value, JArray(_))
implicit lazy val JObjectJW: JW[JObject] = new JW[JObject] {
def write[J](x: JObject, b: Builder[J]) = {
b.beginObject()
x.value foreach (jsonField => JValueJW.addField(jsonField.field, jsonField.value, b))
b.endObject()
}
}
implicit lazy val JValueJW: JW[JValue] = new JW[JValue] {
def write[J](x: JValue, b: Builder[J]) = x match {
case x: JNull.type => JNullJF.write(x, b)
case x: JBoolean => JBooleanJF.write(x, b)
case x: JString => JStringJF.write(x, b)
case x: JNumber => JNumberJF.write(x, b)
case x: JArray => JArrayJF.write(x, b)
case x: JObject => JObjectJW.write(x, b)
}
}
implicit lazy val JValueJR: JR[JValue] = new JR[JValue] {
def read[J](j: Option[J], u: Unbuilder[J]) = ??? // Is this even possible? with no Manifest[J]?
}
implicit lazy val JValueJF: JF[JValue] = jsonFormat[JValue](JValueJR, JValueJW)
}

View File

@ -8,6 +8,10 @@ trait Import {
type URI = java.net.URI
type URL = java.net.URL
// sbt.testing
type TestResult = sbt.protocol.testing.TestResult
val TestResult = sbt.protocol.testing.TestResult
// sbt.io
val AllPassFilter = sbt.io.AllPassFilter
val DirectoryFilter = sbt.io.DirectoryFilter

View File

@ -26,7 +26,7 @@ lazy val root = (project in file(".")).
}
def startGroup(name: String): Unit = ()
def endGroup(name: String, t: Throwable): Unit = ()
def endGroup(name: String, result: TestResult.Value): Unit = ()
def endGroup(name: String, result: TestResult): Unit = ()
},
check := {
val exists = marker.exists

View File

@ -0,0 +1,37 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Called if test completed with an error. */
final class EndTestGroupErrorEvent private (
val name: String,
val error: String) extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: EndTestGroupErrorEvent => (this.name == x.name) && (this.error == x.error)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + name.##) + error.##)
}
override def toString: String = {
"EndTestGroupErrorEvent(" + name + ", " + error + ")"
}
protected[this] def copy(name: String = name, error: String = error): EndTestGroupErrorEvent = {
new EndTestGroupErrorEvent(name, error)
}
def withName(name: String): EndTestGroupErrorEvent = {
copy(name = name)
}
def withError(error: String): EndTestGroupErrorEvent = {
copy(error = error)
}
}
object EndTestGroupErrorEvent {
def apply(name: String, error: String): EndTestGroupErrorEvent = new EndTestGroupErrorEvent(name, error)
}

View File

@ -0,0 +1,37 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Called if test completed. */
final class EndTestGroupEvent private (
val name: String,
val result: sbt.protocol.testing.TestResult) extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: EndTestGroupEvent => (this.name == x.name) && (this.result == x.result)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + name.##) + result.##)
}
override def toString: String = {
"EndTestGroupEvent(" + name + ", " + result + ")"
}
protected[this] def copy(name: String = name, result: sbt.protocol.testing.TestResult = result): EndTestGroupEvent = {
new EndTestGroupEvent(name, result)
}
def withName(name: String): EndTestGroupEvent = {
copy(name = name)
}
def withResult(result: sbt.protocol.testing.TestResult): EndTestGroupEvent = {
copy(result = result)
}
}
object EndTestGroupEvent {
def apply(name: String, result: sbt.protocol.testing.TestResult): EndTestGroupEvent = new EndTestGroupEvent(name, result)
}

View File

@ -0,0 +1,33 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Called for each class or equivalent grouping. */
final class StartTestGroupEvent private (
val name: String) extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: StartTestGroupEvent => (this.name == x.name)
case _ => false
}
override def hashCode: Int = {
37 * (17 + name.##)
}
override def toString: String = {
"StartTestGroupEvent(" + name + ")"
}
protected[this] def copy(name: String = name): StartTestGroupEvent = {
new StartTestGroupEvent(name)
}
def withName(name: String): StartTestGroupEvent = {
copy(name = name)
}
}
object StartTestGroupEvent {
def apply(name: String): StartTestGroupEvent = new StartTestGroupEvent(name)
}

View File

@ -0,0 +1,33 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Called once, at end of the testing. */
final class TestCompleteEvent private (
val result: sbt.protocol.testing.TestResult) extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestCompleteEvent => (this.result == x.result)
case _ => false
}
override def hashCode: Int = {
37 * (17 + result.##)
}
override def toString: String = {
"TestCompleteEvent(" + result + ")"
}
protected[this] def copy(result: sbt.protocol.testing.TestResult = result): TestCompleteEvent = {
new TestCompleteEvent(result)
}
def withResult(result: sbt.protocol.testing.TestResult): TestCompleteEvent = {
copy(result = result)
}
}
object TestCompleteEvent {
def apply(result: sbt.protocol.testing.TestResult): TestCompleteEvent = new TestCompleteEvent(result)
}

View File

@ -0,0 +1,30 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Called once, at beginning of the testing. */
final class TestInitEvent private () extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestInitEvent => true
case _ => false
}
override def hashCode: Int = {
17
}
override def toString: String = {
"TestInitEvent()"
}
protected[this] def copy(): TestInitEvent = {
new TestInitEvent()
}
}
object TestInitEvent {
def apply(): TestInitEvent = new TestInitEvent()
}

View File

@ -0,0 +1,54 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Mini version of sbt.testing.Event */
final class TestItemDetail private (
/**
* The fully qualified name of a class that can rerun the suite or test
* about which an event was fired.
*/
val fullyQualifiedName: String,
/** Indicates whether the event represents a test success, failure, error, skipped, ignored, canceled, pending. */
val status: sbt.testing.Status,
/**
* An amount of time, in milliseconds, that was required to complete the action reported by this event.
* None, if no duration was available.
*/
val duration: Option[Long]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestItemDetail => (this.fullyQualifiedName == x.fullyQualifiedName) && (this.status == x.status) && (this.duration == x.duration)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + fullyQualifiedName.##) + status.##) + duration.##)
}
override def toString: String = {
"TestItemDetail(" + fullyQualifiedName + ", " + status + ", " + duration + ")"
}
protected[this] def copy(fullyQualifiedName: String = fullyQualifiedName, status: sbt.testing.Status = status, duration: Option[Long] = duration): TestItemDetail = {
new TestItemDetail(fullyQualifiedName, status, duration)
}
def withFullyQualifiedName(fullyQualifiedName: String): TestItemDetail = {
copy(fullyQualifiedName = fullyQualifiedName)
}
def withStatus(status: sbt.testing.Status): TestItemDetail = {
copy(status = status)
}
def withDuration(duration: Option[Long]): TestItemDetail = {
copy(duration = duration)
}
def withDuration(duration: Long): TestItemDetail = {
copy(duration = Option(duration))
}
}
object TestItemDetail {
def apply(fullyQualifiedName: String, status: sbt.testing.Status, duration: Option[Long]): TestItemDetail = new TestItemDetail(fullyQualifiedName, status, duration)
def apply(fullyQualifiedName: String, status: sbt.testing.Status, duration: Long): TestItemDetail = new TestItemDetail(fullyQualifiedName, status, Option(duration))
}

View File

@ -0,0 +1,41 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Called for each test method or equivalent. */
final class TestItemEvent private (
val result: Option[sbt.protocol.testing.TestResult],
val detail: Vector[sbt.protocol.testing.TestItemDetail]) extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestItemEvent => (this.result == x.result) && (this.detail == x.detail)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + result.##) + detail.##)
}
override def toString: String = {
"TestItemEvent(" + result + ", " + detail + ")"
}
protected[this] def copy(result: Option[sbt.protocol.testing.TestResult] = result, detail: Vector[sbt.protocol.testing.TestItemDetail] = detail): TestItemEvent = {
new TestItemEvent(result, detail)
}
def withResult(result: Option[sbt.protocol.testing.TestResult]): TestItemEvent = {
copy(result = result)
}
def withResult(result: sbt.protocol.testing.TestResult): TestItemEvent = {
copy(result = Option(result))
}
def withDetail(detail: Vector[sbt.protocol.testing.TestItemDetail]): TestItemEvent = {
copy(detail = detail)
}
}
object TestItemEvent {
def apply(result: Option[sbt.protocol.testing.TestResult], detail: Vector[sbt.protocol.testing.TestItemDetail]): TestItemEvent = new TestItemEvent(result, detail)
def apply(result: sbt.protocol.testing.TestResult, detail: Vector[sbt.protocol.testing.TestItemDetail]): TestItemEvent = new TestItemEvent(Option(result), detail)
}

View File

@ -0,0 +1,26 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Events for testing */
abstract class TestMessage() extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestMessage => true
case _ => false
}
override def hashCode: Int = {
17
}
override def toString: String = {
"TestMessage()"
}
}
object TestMessage {
}

View File

@ -0,0 +1,15 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
/** Testing result */
sealed abstract class TestResult extends Serializable
object TestResult {
case object Passed extends TestResult
case object Failed extends TestResult
case object Error extends TestResult
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing
final class TestStringEvent private (
val value: String) extends sbt.protocol.testing.TestMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestStringEvent => (this.value == x.value)
case _ => false
}
override def hashCode: Int = {
37 * (17 + value.##)
}
override def toString: String = {
value
}
protected[this] def copy(value: String = value): TestStringEvent = {
new TestStringEvent(value)
}
def withValue(value: String): TestStringEvent = {
copy(value = value)
}
}
object TestStringEvent {
def apply(value: String): TestStringEvent = new TestStringEvent(value)
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EndTestGroupErrorEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val EndTestGroupErrorEventFormat: JsonFormat[sbt.protocol.testing.EndTestGroupErrorEvent] = new JsonFormat[sbt.protocol.testing.EndTestGroupErrorEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.EndTestGroupErrorEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val name = unbuilder.readField[String]("name")
val error = unbuilder.readField[String]("error")
unbuilder.endObject()
sbt.protocol.testing.EndTestGroupErrorEvent(name, error)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.EndTestGroupErrorEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("name", obj.name)
builder.addField("error", obj.error)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EndTestGroupEventFormats { self: sbt.protocol.testing.codec.TestResultFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val EndTestGroupEventFormat: JsonFormat[sbt.protocol.testing.EndTestGroupEvent] = new JsonFormat[sbt.protocol.testing.EndTestGroupEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.EndTestGroupEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val name = unbuilder.readField[String]("name")
val result = unbuilder.readField[sbt.protocol.testing.TestResult]("result")
unbuilder.endObject()
sbt.protocol.testing.EndTestGroupEvent(name, result)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.EndTestGroupEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("name", obj.name)
builder.addField("result", obj.result)
builder.endObject()
}
}
}

View File

@ -0,0 +1,19 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.testing.codec.TestStringEventFormats
with sbt.protocol.testing.codec.TestInitEventFormats
with sbt.protocol.testing.codec.TestResultFormats
with sbt.protocol.testing.codec.TestCompleteEventFormats
with sbt.protocol.testing.codec.StartTestGroupEventFormats
with sbt.protocol.testing.codec.EndTestGroupEventFormats
with sbt.protocol.testing.codec.EndTestGroupErrorEventFormats
with sbt.internal.testing.StatusFormats
with sbt.protocol.testing.codec.TestItemDetailFormats
with sbt.protocol.testing.codec.TestItemEventFormats
with sbt.protocol.testing.codec.TestMessageFormats
object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait StartTestGroupEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val StartTestGroupEventFormat: JsonFormat[sbt.protocol.testing.StartTestGroupEvent] = new JsonFormat[sbt.protocol.testing.StartTestGroupEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.StartTestGroupEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val name = unbuilder.readField[String]("name")
unbuilder.endObject()
sbt.protocol.testing.StartTestGroupEvent(name)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.StartTestGroupEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("name", obj.name)
builder.endObject()
}
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestCompleteEventFormats { self: sbt.protocol.testing.codec.TestResultFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val TestCompleteEventFormat: JsonFormat[sbt.protocol.testing.TestCompleteEvent] = new JsonFormat[sbt.protocol.testing.TestCompleteEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestCompleteEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val result = unbuilder.readField[sbt.protocol.testing.TestResult]("result")
unbuilder.endObject()
sbt.protocol.testing.TestCompleteEvent(result)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.TestCompleteEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("result", obj.result)
builder.endObject()
}
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestInitEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TestInitEventFormat: JsonFormat[sbt.protocol.testing.TestInitEvent] = new JsonFormat[sbt.protocol.testing.TestInitEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestInitEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
unbuilder.endObject()
sbt.protocol.testing.TestInitEvent()
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.TestInitEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.endObject()
}
}
}

View File

@ -0,0 +1,31 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestItemDetailFormats { self: sbt.internal.testing.StatusFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val TestItemDetailFormat: JsonFormat[sbt.protocol.testing.TestItemDetail] = new JsonFormat[sbt.protocol.testing.TestItemDetail] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestItemDetail = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val fullyQualifiedName = unbuilder.readField[String]("fullyQualifiedName")
val status = unbuilder.readField[sbt.testing.Status]("status")
val duration = unbuilder.readField[Option[Long]]("duration")
unbuilder.endObject()
sbt.protocol.testing.TestItemDetail(fullyQualifiedName, status, duration)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.TestItemDetail, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("fullyQualifiedName", obj.fullyQualifiedName)
builder.addField("status", obj.status)
builder.addField("duration", obj.duration)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestItemEventFormats { self: sbt.protocol.testing.codec.TestResultFormats with sbt.protocol.testing.codec.TestItemDetailFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val TestItemEventFormat: JsonFormat[sbt.protocol.testing.TestItemEvent] = new JsonFormat[sbt.protocol.testing.TestItemEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestItemEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val result = unbuilder.readField[Option[sbt.protocol.testing.TestResult]]("result")
val detail = unbuilder.readField[Vector[sbt.protocol.testing.TestItemDetail]]("detail")
unbuilder.endObject()
sbt.protocol.testing.TestItemEvent(result, detail)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.TestItemEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("result", obj.result)
builder.addField("detail", obj.detail)
builder.endObject()
}
}
}

View File

@ -0,0 +1,10 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.testing.codec.TestStringEventFormats with sbt.protocol.testing.codec.TestInitEventFormats with sbt.protocol.testing.codec.TestResultFormats with sbt.protocol.testing.codec.TestCompleteEventFormats with sbt.protocol.testing.codec.StartTestGroupEventFormats with sbt.protocol.testing.codec.EndTestGroupEventFormats with sbt.protocol.testing.codec.EndTestGroupErrorEventFormats with sbt.protocol.testing.codec.TestItemDetailFormats with sbt.protocol.testing.codec.TestItemEventFormats =>
implicit lazy val TestMessageFormat: JsonFormat[sbt.protocol.testing.TestMessage] = flatUnionFormat7[sbt.protocol.testing.TestMessage, sbt.protocol.testing.TestStringEvent, sbt.protocol.testing.TestInitEvent, sbt.protocol.testing.TestCompleteEvent, sbt.protocol.testing.StartTestGroupEvent, sbt.protocol.testing.EndTestGroupEvent, sbt.protocol.testing.EndTestGroupErrorEvent, sbt.protocol.testing.TestItemEvent]("type")
}

View File

@ -0,0 +1,31 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestResultFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TestResultFormat: JsonFormat[sbt.protocol.testing.TestResult] = new JsonFormat[sbt.protocol.testing.TestResult] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestResult = {
jsOpt match {
case Some(js) =>
unbuilder.readString(js) match {
case "Passed" => sbt.protocol.testing.TestResult.Passed
case "Failed" => sbt.protocol.testing.TestResult.Failed
case "Error" => sbt.protocol.testing.TestResult.Error
}
case None =>
deserializationError("Expected JsString but found None")
}
}
override def write[J](obj: sbt.protocol.testing.TestResult, builder: Builder[J]): Unit = {
val str = obj match {
case sbt.protocol.testing.TestResult.Passed => "Passed"
case sbt.protocol.testing.TestResult.Failed => "Failed"
case sbt.protocol.testing.TestResult.Error => "Error"
}
builder.writeString(str)
}
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.testing.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait TestStringEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TestStringEventFormat: JsonFormat[sbt.protocol.testing.TestStringEvent] = new JsonFormat[sbt.protocol.testing.TestStringEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestStringEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val value = unbuilder.readField[String]("value")
unbuilder.endObject()
sbt.protocol.testing.TestStringEvent(value)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.testing.TestStringEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("value", obj.value)
builder.endObject()
}
}
}

View File

@ -0,0 +1,65 @@
package sbt.protocol.testing
@target(Scala)
@codecPackage("sbt.protocol.testing.codec")
@fullCodec("JsonProtocol")
## Events for testing
interface TestMessage {
}
type TestStringEvent implements TestMessage {
value: String!
#xtostring value
}
## Called once, at beginning of the testing.
type TestInitEvent implements TestMessage {}
## Called once, at end of the testing.
type TestCompleteEvent implements TestMessage {
result: sbt.protocol.testing.TestResult!
}
## Called for each class or equivalent grouping.
type StartTestGroupEvent implements TestMessage {
name: String!
}
## Called if test completed.
type EndTestGroupEvent implements TestMessage {
name: String!
result: sbt.protocol.testing.TestResult!
}
## Called if test completed with an error.
type EndTestGroupErrorEvent implements TestMessage {
name: String!
error: String!
}
## Called for each test method or equivalent.
type TestItemEvent implements TestMessage {
result: sbt.protocol.testing.TestResult
detail: [sbt.protocol.testing.TestItemDetail]
}
## Mini version of sbt.testing.Event
type TestItemDetail {
## The fully qualified name of a class that can rerun the suite or test
## about which an event was fired.
fullyQualifiedName: String!
## Indicates whether the event represents a test success, failure, error, skipped, ignored, canceled, pending.
status: sbt.testing.Status!
## An amount of time, in milliseconds, that was required to complete the action reported by this event.
## None, if no duration was available.
duration: Long
}
## Testing result
enum TestResult {
Passed
Failed
Error
}

View File

@ -8,6 +8,7 @@ import scala.collection.mutable.ListBuffer
import scala.util.DynamicVariable
import scala.xml.{ Elem, Node => XNode, XML }
import testing.{ Event => TEvent, Status => TStatus, OptionalThrowable, TestSelector }
import sbt.protocol.testing.TestResult
/**
* A tests listener that outputs the results it receives in junit xml
@ -161,7 +162,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener {
* Ends the current suite, wraps up the result and writes it to an XML file
* in the output folder that is named after the suite.
*/
override def endGroup(name: String, result: TestResult.Value) = {
override def endGroup(name: String, result: TestResult) = {
writeSuite()
}
@ -177,7 +178,7 @@ class JUnitXmlTestsListener(val outputDir: String) extends TestsListener {
}
/**Does nothing, as we write each file after a suite is done.*/
override def doComplete(finalResult: TestResult.Value): Unit = {}
override def doComplete(finalResult: TestResult): Unit = {}
/**Returns None*/
override def contentLogger(test: TestDefinition): Option[ContentLogger] = None

View File

@ -10,12 +10,9 @@ import org.scalatools.testing.{ Framework => OldFramework }
import sbt.internal.inc.classpath.{ ClasspathUtilities, DualLoader }
import sbt.internal.inc.ScalaInstance
import scala.annotation.tailrec
import sbt.util.Logger
import sbt.internal.util.ManagedLogger
import sbt.io.IO
object TestResult extends Enumeration {
val Passed, Failed, Error = Value
}
import sbt.protocol.testing.TestResult
object TestFrameworks {
val ScalaCheck = new TestFramework("org.scalacheck.ScalaCheckFramework")
@ -27,7 +24,7 @@ object TestFrameworks {
case class TestFramework(implClassNames: String*) {
@tailrec
private def createFramework(loader: ClassLoader, log: Logger, frameworkClassNames: List[String]): Option[Framework] = {
private def createFramework(loader: ClassLoader, log: ManagedLogger, frameworkClassNames: List[String]): Option[Framework] = {
frameworkClassNames match {
case head :: tail =>
try {
@ -45,7 +42,7 @@ case class TestFramework(implClassNames: String*) {
}
}
def create(loader: ClassLoader, log: Logger): Option[Framework] =
def create(loader: ClassLoader, log: ManagedLogger): Option[Framework] =
createFramework(loader, log, implClassNames.toList)
}
final class TestDefinition(val name: String, val fingerprint: Fingerprint, val explicitlySpecified: Boolean, val selectors: Array[Selector]) {
@ -58,7 +55,7 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint, val e
override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode
}
final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log: Logger) {
final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log: ManagedLogger) {
final def tasks(testDefs: Set[TestDefinition]): Array[TestTask] =
delegate.tasks(testDefs.map(df => new TaskDef(df.name, df.fingerprint, df.explicitlySpecified, df.selectors)).toArray)
@ -106,7 +103,7 @@ object TestFramework {
case _ => sys.error("Could not call 'fingerprints' on framework " + framework)
}
private[sbt] def safeForeach[T](it: Iterable[T], log: Logger)(f: T => Unit): Unit =
private[sbt] def safeForeach[T](it: Iterable[T], log: ManagedLogger)(f: T => Unit): Unit =
it.foreach(i => try f(i) catch { case NonFatal(e) => log.trace(e); log.error(e.toString) })
private[sbt] def hashCode(f: Fingerprint): Int = f match {
@ -132,9 +129,9 @@ object TestFramework {
runners: Map[TestFramework, Runner],
testLoader: ClassLoader,
tests: Seq[TestDefinition],
log: Logger,
log: ManagedLogger,
listeners: Seq[TestReportListener]
): (() => Unit, Seq[(String, TestFunction)], TestResult.Value => () => Unit) =
): (() => Unit, Seq[(String, TestFunction)], TestResult => () => Unit) =
{
val mappedTests = testMap(frameworks.values.toSeq, tests)
if (mappedTests.isEmpty)
@ -160,7 +157,7 @@ object TestFramework {
map.toMap.mapValues(_.toSet)
}
private def createTestTasks(loader: ClassLoader, runners: Map[Framework, TestRunner], tests: Map[Framework, Set[TestDefinition]], ordered: Seq[TestDefinition], log: Logger, listeners: Seq[TestReportListener]) =
private def createTestTasks(loader: ClassLoader, runners: Map[Framework, TestRunner], tests: Map[Framework, Set[TestDefinition]], ordered: Seq[TestDefinition], log: ManagedLogger, listeners: Seq[TestReportListener]) =
{
val testsListeners = listeners collect { case tl: TestsListener => tl }
@ -178,7 +175,7 @@ object TestFramework {
}
}
val endTask = (result: TestResult.Value) => foreachListenerSafe(_.doComplete(result))
val endTask = (result: TestResult) => foreachListenerSafe(_.doComplete(result))
(startTask, order(testTasks, ordered), endTask)
}
private[this] def withContextLoader[T](loader: ClassLoader)(eval: => T): T =

View File

@ -5,8 +5,7 @@
package sbt
import testing.{ Logger => TLogger, Event => TEvent, Status => TStatus }
import sbt.internal.util.{ BufferedLogger, FullLogger }
import sbt.util.Level
import sbt.protocol.testing._
trait TestReportListener {
/** called for each class or equivalent grouping */
@ -19,23 +18,25 @@ trait TestReportListener {
def endGroup(name: String, t: Throwable): Unit
/** called if test completed */
def endGroup(name: String, result: TestResult.Value): Unit
def endGroup(name: String, result: TestResult): Unit
/** Used by the test framework for logging test results*/
def contentLogger(test: TestDefinition): Option[ContentLogger] = None
}
final class ContentLogger(val log: TLogger, val flush: () => Unit)
trait TestsListener extends TestReportListener {
/** called once, at beginning. */
def doInit(): Unit
/** called once, at end. */
def doComplete(finalResult: TestResult.Value): Unit
def doComplete(finalResult: TestResult): Unit
}
/** Provides the overall `result` of a group of tests (a suite) and test counts for each result type. */
final class SuiteResult(
val result: TestResult.Value,
val result: TestResult,
val passedCount: Int, val failureCount: Int, val errorCount: Int,
val skippedCount: Int, val ignoredCount: Int, val canceledCount: Int, val pendingCount: Int
) {
@ -65,7 +66,7 @@ object SuiteResult {
}
abstract class TestEvent {
def result: Option[TestResult.Value]
def result: Option[TestResult]
def detail: Seq[TEvent] = Nil
}
object TestEvent {
@ -75,8 +76,8 @@ object TestEvent {
override val detail = events
}
private[sbt] def overallResult(events: Seq[TEvent]): TestResult.Value =
(TestResult.Passed /: events) { (sum, event) =>
private[sbt] def overallResult(events: Seq[TEvent]): TestResult =
((TestResult.Passed: TestResult) /: events) { (sum, event) =>
(sum, event.status) match {
case (TestResult.Error, _) => TestResult.Error
case (_, TStatus.Error) => TestResult.Error
@ -86,60 +87,3 @@ object TestEvent {
}
}
}
object TestLogger {
@deprecated("Doesn't provide for underlying resources to be released.", "0.13.1")
def apply(logger: sbt.util.Logger, logTest: TestDefinition => sbt.util.Logger, buffered: Boolean): TestLogger =
new TestLogger(new TestLogging(wrap(logger), tdef => contentLogger(logTest(tdef), buffered)))
@deprecated("Doesn't provide for underlying resources to be released.", "0.13.1")
def contentLogger(log: sbt.util.Logger, buffered: Boolean): ContentLogger =
{
val blog = new BufferedLogger(FullLogger(log))
if (buffered) blog.record()
new ContentLogger(wrap(blog), () => blog.stopQuietly())
}
final class PerTest private[sbt] (val log: sbt.util.Logger, val flush: () => Unit, val buffered: Boolean)
def make(global: sbt.util.Logger, perTest: TestDefinition => PerTest): TestLogger =
{
def makePerTest(tdef: TestDefinition): ContentLogger =
{
val per = perTest(tdef)
val blog = new BufferedLogger(FullLogger(per.log))
if (per.buffered) blog.record()
new ContentLogger(wrap(blog), () => { blog.stopQuietly(); per.flush() })
}
val config = new TestLogging(wrap(global), makePerTest)
new TestLogger(config)
}
def wrap(logger: sbt.util.Logger): TLogger =
new TLogger {
def error(s: String) = log(Level.Error, s)
def warn(s: String) = log(Level.Warn, s)
def info(s: String) = log(Level.Info, s)
def debug(s: String) = log(Level.Debug, s)
def trace(t: Throwable) = logger.trace(t)
private def log(level: Level.Value, s: String) = logger.log(level, s)
def ansiCodesSupported() = logger.ansiCodesSupported
}
}
final class TestLogging(val global: TLogger, val logTest: TestDefinition => ContentLogger)
final class ContentLogger(val log: TLogger, val flush: () => Unit)
class TestLogger(val logging: TestLogging) extends TestsListener {
import logging.{ global => log, logTest }
def startGroup(name: String): Unit = ()
def testEvent(event: TestEvent): Unit = ()
def endGroup(name: String, t: Throwable): Unit = {
log.trace(t)
log.error("Could not run test " + name + ": " + t.toString)
}
def endGroup(name: String, result: TestResult.Value): Unit = ()
def doInit: Unit = ()
/** called once, at end of test group. */
def doComplete(finalResult: TestResult.Value): Unit = ()
override def contentLogger(test: TestDefinition): Option[ContentLogger] = Some(logTest(test))
}

View File

@ -7,6 +7,7 @@ import java.io.File
import sbt.io.IO
import scala.collection.mutable.Map
import sbt.protocol.testing.TestResult
// Assumes exclusive ownership of the file.
private[sbt] class TestStatusReporter(f: File) extends TestsListener {
@ -16,11 +17,11 @@ private[sbt] class TestStatusReporter(f: File) extends TestsListener {
def startGroup(name: String): Unit = { succeeded remove name }
def testEvent(event: TestEvent): Unit = ()
def endGroup(name: String, t: Throwable): Unit = ()
def endGroup(name: String, result: TestResult.Value): Unit = {
def endGroup(name: String, result: TestResult): Unit = {
if (result == TestResult.Passed)
succeeded(name) = System.currentTimeMillis
}
def doComplete(finalResult: TestResult.Value): Unit = {
def doComplete(finalResult: TestResult): Unit = {
TestStatus.write(succeeded, "Successful Tests", f)
}
}

View File

@ -0,0 +1,38 @@
package sbt.internal.testing
import sbt.testing.Status
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait StatusFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val StatusFormat: JsonFormat[Status] = new JsonFormat[Status] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Status = {
jsOpt match {
case Some(js) =>
unbuilder.readString(js) match {
case "Success" => Status.Success
case "Error" => Status.Error
case "Failure" => Status.Failure
case "Skipped" => Status.Skipped
case "Ignored" => Status.Ignored
case "Canceled" => Status.Canceled
case "Pending" => Status.Pending
}
case None =>
deserializationError("Expected JsString but found None")
}
}
override def write[J](obj: Status, builder: Builder[J]): Unit = {
val str = obj match {
case Status.Success => "Success"
case Status.Error => "Error"
case Status.Failure => "Failure"
case Status.Skipped => "Skipped"
case Status.Ignored => "Ignored"
case Status.Canceled => "Canceled"
case Status.Pending => "Pending"
}
builder.writeString(str)
}
}
}

View File

@ -0,0 +1,101 @@
package sbt
package internal.testing
import testing.{ Logger => TLogger }
import sbt.internal.util.{ ManagedLogger, BufferedAppender }
import sbt.util.{ Level, LogExchange }
import sbt.protocol.testing._
import java.util.concurrent.atomic.AtomicInteger
import scala.collection.JavaConverters._
object TestLogger {
import sbt.protocol.testing.codec.JsonProtocol._
private def generateName: String =
"test-" + generateId.incrementAndGet
private val generateId: AtomicInteger = new AtomicInteger
private def generateBufferName: String =
"testbuffer-" + generateBufferId.incrementAndGet
private val generateBufferId: AtomicInteger = new AtomicInteger
final class PerTest private[sbt] (val log: ManagedLogger, val flush: () => Unit, val buffered: Boolean)
def make(global: ManagedLogger, perTest: TestDefinition => PerTest): TestLogger =
{
def makePerTest(tdef: TestDefinition): ContentLogger =
{
val per = perTest(tdef)
val l0 = per.log
val config = LogExchange.loggerConfig(l0.name)
val as = config.getAppenders.asScala
val buffs: List[BufferedAppender] = (as map {
case (k, v) => BufferedAppender(generateBufferName, v)
}).toList
val newLog = LogExchange.logger(generateName, l0.channelName, l0.execId)
LogExchange.bindLoggerAppenders(newLog.name, buffs map { x => (x, Level.Debug) })
if (per.buffered) {
buffs foreach { _.record() }
}
new ContentLogger(wrap(newLog), () => {
buffs foreach { _.stopQuietly() }
per.flush()
})
}
val config = new TestLogging(wrap(global), global, makePerTest)
new TestLogger(config)
}
def wrap(logger: ManagedLogger): TLogger =
new TLogger {
def error(s: String) = log(Level.Error, TestStringEvent(s))
def warn(s: String) = log(Level.Warn, TestStringEvent(s))
def info(s: String) = log(Level.Info, TestStringEvent(s))
def debug(s: String) = log(Level.Debug, TestStringEvent(s))
def trace(t: Throwable) = logger.trace(t)
private def log(level: Level.Value, event: TestStringEvent) = logger.logEvent(level, event)
def ansiCodesSupported() = logger.ansiCodesSupported
}
private[sbt] def toTestItemEvent(event: TestEvent): TestItemEvent =
TestItemEvent(event.result, event.detail.toVector map { d =>
TestItemDetail(
d.fullyQualifiedName,
d.status,
d.duration match {
case -1 => None
case x => Some(x)
}
)
})
}
final class TestLogging(
val global: TLogger,
val managed: ManagedLogger,
val logTest: TestDefinition => ContentLogger
)
class TestLogger(val logging: TestLogging) extends TestsListener {
import TestLogger._
import logging.{ global => log, logTest, managed }
import sbt.protocol.testing.codec.JsonProtocol._
def startGroup(name: String): Unit =
managed.logEvent(Level.Info, StartTestGroupEvent(name))
def testEvent(event: TestEvent): Unit =
managed.logEvent(Level.Info, toTestItemEvent(event))
def endGroup(name: String, t: Throwable): Unit = {
log.trace(t)
log.error("Could not run test " + name + ": " + t.toString)
managed.logEvent(Level.Info, EndTestGroupErrorEvent(
name,
t.getMessage + "\n" + t.getStackTrace.toList.mkString("\n")
))
}
def endGroup(name: String, result: TestResult): Unit =
managed.logEvent(Level.Info, EndTestGroupEvent(name, result))
def doInit: Unit = managed.logEvent(Level.Info, TestInitEvent())
/** called once, at end of test group. */
def doComplete(finalResult: TestResult): Unit =
managed.logEvent(Level.Info, TestCompleteEvent(finalResult))
override def contentLogger(test: TestDefinition): Option[ContentLogger] = Some(logTest(test))
}