Skip to content

Commit

Permalink
Report testsCompleted to report (#1005)
Browse files Browse the repository at this point in the history
  • Loading branch information
hugo-vrijswijk authored Nov 1, 2021
1 parent 1fb400b commit 350ca90
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 34 deletions.
8 changes: 6 additions & 2 deletions api/src/main/protobuf/stryker4s/api/testprocess.proto
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ message Response {
}

message SetupTestContextSuccessful {}
message TestsSuccessful {}
message TestsUnsuccessful {}
message TestsSuccessful {
int32 tests_completed = 1;
}
message TestsUnsuccessful {
int32 tests_completed = 1;
}
message ErrorDuringTestRun {
string msg = 1;
}
Expand Down
22 changes: 15 additions & 7 deletions core/src/main/scala/stryker4s/model/MutantRunResult.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@ package stryker4s.model
sealed trait MutantRunResult {
def mutant: Mutant
def description: Option[String]
def testsCompleted: Option[Int]
}

sealed trait Detected extends MutantRunResult

sealed trait Undetected extends MutantRunResult

final case class Killed(mutant: Mutant, description: Option[String] = None) extends Detected
final case class Killed(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends Detected

final case class TimedOut(mutant: Mutant, description: Option[String] = None) extends Detected
final case class TimedOut(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends Detected

final case class Survived(mutant: Mutant, description: Option[String] = None) extends Undetected
final case class Survived(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends Undetected

final case class NoCoverage(mutant: Mutant, description: Option[String] = None) extends Undetected
final case class NoCoverage(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends Undetected

final case class Error(mutant: Mutant, description: Option[String] = None) extends MutantRunResult
final case class Error(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends MutantRunResult

final case class Ignored(mutant: Mutant, description: Option[String] = None) extends MutantRunResult
final case class Ignored(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends MutantRunResult

final case class CompileError(mutant: Mutant, description: Option[String] = None) extends MutantRunResult
final case class CompileError(mutant: Mutant, description: Option[String] = None, testsCompleted: Option[Int] = None)
extends MutantRunResult
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ trait MutantRunResultMapper {
mutant.mutated.syntax,
toLocation(mutant.original.pos),
toMutantStatus(runResult),
runResult.description
runResult.description,
testsCompleted = runResult.testsCompleted
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,27 @@ final class TestRunnerMessageHandler() extends MessageHandler {
req match {
case StartTestRun(mutation, testNames) =>
try {
val status = testRunner.runMutation(mutation, testNames)
toTestResult(status)
val result = testRunner.runMutation(mutation, testNames)
toTestResult(result)
} catch {
case NonFatal(e) => ErrorDuringTestRun(e.toString())
}

case StartInitialTestRun() =>
val (status, report) = stryker4s.coverage.collectCoverage {
val (result, report) = stryker4s.coverage.collectCoverage {
testRunner.initialTestRun()
}
toInitialTestResult(status, report)
toInitialTestResult(result.status, report)
case testContext: TestProcessContext =>
testRunner = new SbtTestInterfaceRunner(testContext)
println("Set up testContext")
SetupTestContextSuccessful()
case Request.Empty => throw new MatchError(req)
}

def toTestResult(status: Status): Response =
status match {
case Status.Success => TestsSuccessful()
case _ => TestsUnsuccessful()
}
def toTestResult(result: TestRunResult): Response =
if (result.status == Status.Success) TestsSuccessful(result.testsCompleted)
else TestsUnsuccessful(result.testsCompleted)

def toInitialTestResult(status: Status, coverage: CoverageTestNameMap): Response =
CoverageTestRunResult(status == Status.Success, Some(coverage))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ package stryker4s.sbt.testrunner
import sbt.testing.{Event, EventHandler, Framework, Status, Task}
import stryker4s.api.testprocess._

import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.{AtomicInteger, AtomicReference}
import java.util.function.UnaryOperator
import scala.annotation.tailrec
import scala.util.control.NonFatal

sealed trait TestRunner {
def runMutation(mutation: Int, fingerprints: Seq[String]): Status
def initialTestRun(): Status
def runMutation(mutation: Int, fingerprints: Seq[String]): TestRunResult
def initialTestRun(): TestRunResult
}

private[stryker4s] case class TestRunResult(status: Status, testsCompleted: Int)

class SbtTestInterfaceRunner(context: TestProcessContext) extends TestRunner with TestInterfaceMapper {

val testFunctions: Option[(Int, Seq[String])] => Status = {
val testFunctions: Option[(Int, Seq[String])] => TestRunResult = {
val tasks = {
val cl = getClass().getClassLoader()
context.testGroups.flatMap { testGroup =>
Expand All @@ -31,22 +33,30 @@ class SbtTestInterfaceRunner(context: TestProcessContext) extends TestRunner wit
tasks.filter(t => testNames.contains(t.taskDef().fullyQualifiedName()))
case None => tasks
}
val testsCompleted = new AtomicInteger(0)
val statusRef = new AtomicReference[Status](Status.Success)
val eventHandler = new StatusEventHandler(statusRef, testsCompleted)
mutation.foreach { case (mutantId, _) => stryker4s.activeMutation = mutantId }
runTests(tasksToRun, new AtomicReference(Status.Success))
val status = runTests(tasksToRun, statusRef, eventHandler)

TestRunResult(status, testsCompleted.get())
}
}

override def runMutation(mutation: Int, testNames: Seq[String]) = {
override def runMutation(mutation: Int, testNames: Seq[String]): TestRunResult = {
testFunctions(Some((mutation, testNames)))
}

override def initialTestRun(): Status = {
override def initialTestRun(): TestRunResult = {
testFunctions(None)
}

@tailrec
private def runTests(testTasks: Seq[Task], status: AtomicReference[Status]): sbt.testing.Status = {
val eventHandler = new StatusEventHandler(status)
private def runTests(
testTasks: Seq[Task],
status: AtomicReference[Status],
eventHandler: EventHandler
): sbt.testing.Status = {

val newTasks = testTasks.flatMap(task =>
status.get() match {
Expand All @@ -58,12 +68,13 @@ class SbtTestInterfaceRunner(context: TestProcessContext) extends TestRunner wit
task.execute(eventHandler, Array.empty)
}
)
if (newTasks.nonEmpty) runTests(newTasks, status)
if (newTasks.nonEmpty) runTests(newTasks, status, eventHandler)
else status.get()
}

class StatusEventHandler(status: AtomicReference[Status]) extends EventHandler {
class StatusEventHandler(status: AtomicReference[Status], testsCompleted: AtomicInteger) extends EventHandler {
override def handle(event: Event) = {
testsCompleted.incrementAndGet()
if (event.status() != Status.Success) {
println(s"Test unsuccessful: ${event.fullyQualifiedName()} status ${event.status()} with ${event.throwable()}")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class ProcessTestRunner(testProcess: TestRunnerConnection) extends TestRunner {
override def runMutant(mutant: Mutant, testNames: Seq[String]): IO[MutantRunResult] = {
val message = StartTestRun(mutant.id.globalId, testNames)
testProcess.sendMessage(message).map {
case _: TestsSuccessful => Survived(mutant)
case _: TestsUnsuccessful => Killed(mutant)
case ErrorDuringTestRun(msg) => Killed(mutant, Some(msg))
case _ => Error(mutant)
case TestsSuccessful(testsCompleted) => Survived(mutant, testsCompleted = Some(testsCompleted))
case TestsUnsuccessful(testsCompleted) => Killed(mutant, testsCompleted = Some(testsCompleted))
case ErrorDuringTestRun(msg) => Killed(mutant, Some(msg))
case _ => Error(mutant)
}
}

Expand Down

0 comments on commit 350ca90

Please sign in to comment.