Skip to content

Commit

Permalink
Send TestResult to Debuggee Listener
Browse files Browse the repository at this point in the history
  • Loading branch information
Kamil Podsiadlo committed Nov 29, 2021
1 parent 5047569 commit f3e879c
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 115 deletions.
16 changes: 9 additions & 7 deletions frontend/src/main/scala/bloop/dap/BloopDebuggeeRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import bloop.engine.caches.ExpressionCompilerCache
import bloop.engine.tasks.{RunMode, Tasks}
import bloop.engine.{Dag, State}
import bloop.logging.Logger
import bloop.testing.{LoggingEventHandler, TestInternals}
import bloop.testing.{LoggingEventHandler, DebugLoggingEventHandler, TestInternals}
import ch.epfl.scala.bsp.ScalaMainClass
import ch.epfl.scala.debugadapter._
import monix.eval.Task
Expand All @@ -23,14 +23,14 @@ abstract class BloopDebuggeeRunner(initialState: State, ioScheduler: Scheduler)
override def run(listener: DebuggeeListener): CancelableFuture[Unit] = {
val debugSessionLogger = new DebuggeeLogger(listener, initialState.logger)

val task = start(initialState.copy(logger = debugSessionLogger))
val task = start(initialState.copy(logger = debugSessionLogger), listener)
.map { status =>
if (!status.isOk) throw new Exception(s"debugee failed with ${status.name}")
}
DapCancellableFuture.runAsync(task, ioScheduler)
}

protected def start(state: State): Task[ExitStatus]
protected def start(state: State, listener: DebuggeeListener): Task[ExitStatus]
}

private final class MainClassDebugAdapter(
Expand All @@ -45,7 +45,7 @@ private final class MainClassDebugAdapter(
) extends BloopDebuggeeRunner(initialState, ioScheduler) {
val javaRuntime: Option[JavaRuntime] = JavaRuntime(env.javaHome.underlying)
def name: String = s"${getClass.getSimpleName}(${project.name}, ${mainClass.`class`})"
def start(state: State): Task[ExitStatus] = {
def start(state: State, listener: DebuggeeListener): Task[ExitStatus] = {
val workingDir = state.commonOptions.workingPath
// TODO: https://github.com/scalacenter/bloop/issues/1456
// Metals used to add the `-J` prefix but it is not needed anymore
Expand Down Expand Up @@ -81,9 +81,9 @@ private final class TestSuiteDebugAdapter(
val filtersStr = filters.mkString("[", ", ", "]")
s"${getClass.getSimpleName}($projectsStr, $filtersStr)"
}
override def start(state: State): Task[ExitStatus] = {
override def start(state: State, listener: DebuggeeListener): Task[ExitStatus] = {
val filter = TestInternals.parseFilters(filters)
val handler = new LoggingEventHandler(state.logger)
val handler = new DebugLoggingEventHandler(state.logger, listener)

val task = Tasks.test(
state,
Expand All @@ -107,7 +107,9 @@ private final class AttachRemoteDebugAdapter(
override val classPath: Seq[Path]
) extends BloopDebuggeeRunner(initialState, ioScheduler) {
override def name: String = s"${getClass.getSimpleName}(${initialState.build.origin})"
override def start(state: State): Task[ExitStatus] = Task(ExitStatus.Ok)
override def start(state: State, listener: DebuggeeListener): Task[ExitStatus] = Task(
ExitStatus.Ok
)
}

object BloopDebuggeeRunner {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/main/scala/bloop/engine/tasks/Tasks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import bloop.engine.{Dag, State}
import bloop.exec.{Forker, JvmProcessForker}
import bloop.io.AbsolutePath
import bloop.util.JavaCompat.EnrichOptional
import bloop.testing.{LoggingEventHandler, TestSuiteEvent, TestSuiteEventHandler}
import bloop.testing.{LoggingEventHandler, BloopTestSuiteEventHandler}
import ch.epfl.scala.debugadapter.testing.TestSuiteEvent
import monix.eval.Task
import sbt.internal.inc.{Analysis, AnalyzingCompiler, ConcreteAnalysisContents, FileAnalysisStore}
import sbt.internal.inc.classpath.ClasspathUtilities
Expand Down Expand Up @@ -89,7 +90,7 @@ object Tasks {
projectsToTest: List[Project],
userTestOptions: List[String],
testFilter: String => Boolean,
testEventHandler: TestSuiteEventHandler,
testEventHandler: BloopTestSuiteEventHandler,
runInParallel: Boolean = false,
mode: RunMode
): Task[State] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
package bloop.testing

import bloop.logging.{DebugFilter, Logger}
import bloop.logging.DebugFilter
import bloop.logging.Logger
import bloop.util.TimeFormat
import ch.epfl.scala.bsp
import ch.epfl.scala.bsp.BuildTargetIdentifier
import ch.epfl.scala.bsp.endpoints.{Build, BuildTarget}
import sbt.testing.{Event, Selector, Status}
import ch.epfl.scala.bsp.endpoints.Build
import ch.epfl.scala.bsp.endpoints.BuildTarget
import ch.epfl.scala.debugadapter.DebuggeeListener
import ch.epfl.scala.debugadapter.SingleTestResult
import ch.epfl.scala.debugadapter.TestSuiteResult
import ch.epfl.scala.debugadapter.TestResultEvent
import ch.epfl.scala.debugadapter.testing.TestSuiteEvent
import ch.epfl.scala.debugadapter.testing.TestSuiteEventHandler
import ch.epfl.scala.debugadapter.testing.TestUtils
import sbt.testing.Event
import sbt.testing.Selector
import sbt.testing.Status
import sbt.testing.TestSelector

import scala.collection.mutable
import scala.meta.jsonrpc.JsonRpcClient
import scala.util.Try

sealed trait TestSuiteEvent
object TestSuiteEvent {
case object Done extends TestSuiteEvent
case class Error(message: String) extends TestSuiteEvent
case class Warn(message: String) extends TestSuiteEvent
case class Info(message: String) extends TestSuiteEvent
case class Debug(message: String) extends TestSuiteEvent
case class Trace(throwable: Throwable) extends TestSuiteEvent

/** @param testSuite Class name of test suite */
case class Results(testSuite: String, events: List[Event]) extends TestSuiteEvent
}
import collection.JavaConverters._

trait TestSuiteEventHandler {
def handle(testSuiteEvent: TestSuiteEvent): Unit
trait BloopTestSuiteEventHandler extends TestSuiteEventHandler {
def report(): Unit
}

class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler {
private type SuiteName = String
private type TestName = String
private type FailureMessage = String
class LoggingEventHandler(logger: Logger) extends BloopTestSuiteEventHandler {
type SuiteName = String
type TestName = String
type FailureMessage = String

private val failedStatuses = Set(Status.Error, Status.Canceled, Status.Failure)

protected var suitesDuration = 0L
protected var suitesPassed = 0
protected var suitesAborted = 0
Expand All @@ -56,46 +59,27 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler {
suitesAborted += 1
suitesTotal += 1

case TestSuiteEvent.Results(testSuite, events) =>
case results @ TestSuiteEvent.Results(testSuite, events) =>
val testsTotal = events.length

val duration = events.map(_.duration()).sum
val passed = events.count(_.status() == Status.Success)
val skipped = events.count(_.status() == Status.Skipped)
val failed = events.count(_.status() == Status.Failure)
val canceled = events.count(_.status() == Status.Canceled)
val ignored = events.count(_.status() == Status.Ignored)
val pending = events.count(_.status() == Status.Pending)
val errors = events.count(_.status() == Status.Error)

logger.info(s"Execution took ${TimeFormat.readableMillis(duration)}")
logger.info(s"Execution took ${TimeFormat.readableMillis(results.duration)}")
val regularMetrics = List(
testsTotal -> "tests",
passed -> "passed",
pending -> "pending",
ignored -> "ignored",
skipped -> "skipped"
results.passed -> "passed",
results.pending -> "pending",
results.ignored -> "ignored",
results.skipped -> "skipped"
)

// If test metrics
val failureCount = failed + canceled + errors
val failureMetrics = List(failed -> "failed", canceled -> "canceled", errors -> "errors")
val failureCount = results.failed + results.canceled + results.errors
val failureMetrics =
List(results.failed -> "failed", results.canceled -> "canceled", results.errors -> "errors")
val testMetrics = formatMetrics(regularMetrics ++ failureMetrics)
if (!testMetrics.isEmpty) logger.info(testMetrics)

val failedStatuses = Set(Status.Error, Status.Canceled, Status.Failure)
if (failureCount > 0) {
val currentFailedTests = {
events
.filter(e => failedStatuses.contains(e.status()))
.map { event =>
val key = TestUtils.printSelector(event.selector, logger).getOrElse("")
val value = TestUtils.printThrowable(event.throwable()).getOrElse("")
key -> value
}
.toMap
}

val currentFailedTests = extractErrors(events, logger)
val previousFailedTests = testsFailedBySuite.getOrElse(testSuite, Map.empty)
testsFailedBySuite += testSuite -> (previousFailedTests ++ currentFailedTests)
} else if (testsTotal <= 0) logger.info("No test suite was run")
Expand All @@ -106,11 +90,27 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler {

logger.info("")
suitesTotal += 1
suitesDuration += duration
suitesDuration += results.duration

case TestSuiteEvent.Done => ()
}

private def extractErrors(events: List[Event], logger: Logger) =
events
.filter(e => failedStatuses.contains(e.status()))
.map { event =>
val selectorOpt = TestUtils.printSelector(event.selector)
if (selectorOpt.isEmpty) {
logger.debug(s"Unexpected test selector ${event.selector} won't be pretty printed!")(
DebugFilter.Test
)
}
val key = selectorOpt.getOrElse("")
val value = TestUtils.printThrowable(event.throwable()).getOrElse("")
key -> value
}
.toMap

override def report(): Unit = {
// TODO: Shall we think of a better way to format this delimiter based on screen length?
logger.info("===============================================")
Expand All @@ -134,12 +134,11 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler {
testsFailedBySuite.foreach {
case (suiteName, failedTests) =>
logger.info(s"- $suiteName:")
failedTests.foreach {
case ("", failureMessage) => logger.info(s" * $failureMessage")
case (testSuiteName, "") => logger.info(s" * $testSuiteName")
case (testSuiteName, failureMessage) =>
logger.info(s" * $testSuiteName - $failureMessage")
val summary = failedTests.map {
case (suiteName, failureMsg) =>
TestSuiteEventHandler.formatError(suiteName, failureMsg, indentSize = 2)
}
summary.foreach(s => logger.info(s))
}
}
}
Expand All @@ -148,6 +147,23 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler {
}
}

/**
* Works just as an ordinary LoggingEventHandler, but
* for TestSuiteEvent.Results extracts information about tests execution and send it to the DebuggeeListener.
*/
final class DebugLoggingEventHandler(logger: Logger, listener: DebuggeeListener)
extends LoggingEventHandler(logger) {

override def handle(event: TestSuiteEvent): Unit =
event match {
case results: TestSuiteEvent.Results =>
TestSuiteEventHandler.summarizeResults(results)
super.handle(event)
case _ =>
super.handle(event)
}
}

final class BspLoggingEventHandler(id: BuildTargetIdentifier, logger: Logger, client: JsonRpcClient)
extends LoggingEventHandler(logger) {
implicit val client0: JsonRpcClient = client
Expand All @@ -160,7 +176,7 @@ final class BspLoggingEventHandler(id: BuildTargetIdentifier, logger: Logger, cl
}
}

object NoopEventHandler extends TestSuiteEventHandler {
object NoopEventHandler extends BloopTestSuiteEventHandler {
override def handle(event: TestSuiteEvent): Unit = ()
override def report(): Unit = ()
}
3 changes: 2 additions & 1 deletion frontend/src/main/scala/bloop/testing/TestInternals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import bloop.engine.ExecutionContext
import bloop.exec.{Forker, JvmProcessForker}
import bloop.io.AbsolutePath
import bloop.logging.{DebugFilter, Logger}
import ch.epfl.scala.debugadapter.testing.TestSuiteEvent
import monix.eval.Task
import monix.execution.atomic.AtomicBoolean
import sbt.testing.{
Expand Down Expand Up @@ -119,7 +120,7 @@ object TestInternals {
discovered: Map[Framework, List[TaskDef]],
args: List[Config.TestArgument],
jvmOptions: List[String],
testEventHandler: TestSuiteEventHandler,
testEventHandler: BloopTestSuiteEventHandler,
logger: Logger,
opts: CommonOptions
): Task[Int] = {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/main/scala/bloop/testing/TestServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import java.net.ServerSocket

import bloop.cli.CommonOptions
import bloop.config.Config

import scala.util.Try
import bloop.logging.{DebugFilter, Logger}
import ch.epfl.scala.debugadapter.testing.TestSuiteEvent
import monix.eval.Task
import monix.execution.misc.NonFatal
import sbt.{ForkConfiguration, ForkTags}
import sbt.testing.{Event, Framework, TaskDef}

import scala.concurrent.Promise
import scala.util.Try

/**
* Implements the protocol that the forked remote JVM talks with the host process.
Expand All @@ -22,7 +22,7 @@ import scala.concurrent.Promise
*/
final class TestServer(
logger: Logger,
eventHandler: TestSuiteEventHandler,
eventHandler: BloopTestSuiteEventHandler,
classLoader: ClassLoader,
discoveredTests: Map[Framework, List[TaskDef]],
args: List[Config.TestArgument],
Expand Down
44 changes: 0 additions & 44 deletions frontend/src/main/scala/bloop/testing/TestUtils.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bloop.testing

import bloop.logging.{DebugFilter, Logger, RecordingLogger}
import ch.epfl.scala.debugadapter.testing.TestSuiteEvent
import sbt.testing.{Event, Fingerprint, OptionalThrowable, Selector, Status, TestSelector}

object LoggingEventHandlerSpec extends BaseSuite {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/test/scala/bloop/testing/TestPrinterSpec.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package bloop.testing
import bloop.util.TestUtil
import ch.epfl.scala.debugadapter.{util => ch}

object TestPrinterSpec extends BaseSuite {
test("stripping parts of message created by test framework") {
Expand All @@ -13,7 +14,7 @@ object TestPrinterSpec extends BaseSuite {
testCases.foreach {
case (testFrameworkMessage, expectedTruncatedMessage) =>
assertNoDiff(
TestUtils.stripTestFrameworkSpecificInformation(testFrameworkMessage),
ch.TestUtils.stripTestFrameworkSpecificInformation(testFrameworkMessage),
expectedTruncatedMessage
)
}
Expand Down

0 comments on commit f3e879c

Please sign in to comment.