Event handling system that converts ZIO test events to SBT-compatible events for proper test result reporting and integration. This system ensures that ZIO test results are correctly reported to SBT and integrated with build tools and IDEs.
Thread-safe event handler that processes ZIO test execution events and reports them to SBT's event handling system.
/**
* Handles ZIO test events and reports them to SBT
* Extends ZTestEventHandler for ZIO Test integration
*/
class ZTestEventHandlerSbt(
eventHandler: EventHandler,
taskDef: TaskDef,
renderer: TestRenderer
) extends ZTestEventHandler {
/** Semaphore for thread-safe event handling (initialized with permit count 1) */
val semaphore: Semaphore = Semaphore.unsafe.make(1L)(Unsafe.unsafe)
/** Processes ZIO execution events and converts them to SBT events */
def handle(event: ExecutionEvent): UIO[Unit]
}The handler processes different types of ZIO test events:
Usage Example:
// Created automatically by test tasks
val eventHandler = new ZTestEventHandlerSbt(sbtEventHandler, taskDef, renderer)
// Processes events during test execution
eventHandler.handle(ExecutionEvent.Test(...)) // Converts to SBT eventSBT-compatible test event that wraps ZIO test results in the format expected by SBT's reporting system.
/**
* SBT-compatible test event representation
* Implements sbt.testing.Event interface
*/
case class ZTestEvent(
fullyQualifiedName0: String,
selector0: Selector,
status0: Status,
maybeThrowable: Option[Throwable],
duration0: Long,
fingerprint0: Fingerprint
) extends Event {
/** Test execution duration in milliseconds */
def duration(): Long = duration0
/** Test fingerprint for SBT integration */
def fingerprint(): Fingerprint = fingerprint0
/** Fully qualified test class name */
def fullyQualifiedName(): String = fullyQualifiedName0
/** Test selector identifying specific test */
def selector(): Selector = selector0
/** Test execution status (Success, Failure, Ignored, etc.) */
def status(): Status = status0
/** Optional exception for test failures */
def throwable(): OptionalThrowable
}The ZTestEvent companion object provides utilities for converting ZIO test events to SBT events.
/**
* Companion object with event conversion utilities
*/
object ZTestEvent {
/**
* Converts ZIO ExecutionEvent.Test to SBT Event
* Includes failure message rendering and status mapping
*/
def convertEvent(
test: ExecutionEvent.Test[_],
taskDef: TaskDef,
renderer: TestRenderer
): Event
/** Maps ZIO test results to SBT status codes */
private def statusFrom(test: ExecutionEvent.Test[_]): Status = {
test.test match {
case Left(_) => Status.Failure
case Right(value) =>
value match {
case TestSuccess.Succeeded(_) => Status.Success
case TestSuccess.Ignored(_) => Status.Ignored
}
}
}
}Status Mapping:
| ZIO Test Result | SBT Status |
|---|---|
TestSuccess.Succeeded | Status.Success |
TestSuccess.Ignored | Status.Ignored |
Left(failure) | Status.Failure |
Usage Example:
// Automatic conversion during event handling
val zioTestEvent = ExecutionEvent.Test(...)
val sbtEvent = ZTestEvent.convertEvent(zioTestEvent, taskDef, renderer)
sbtEventHandler.handle(sbtEvent)When tests fail, the event handler creates detailed failure reports using the configured renderer.
// Failure handling in ZTestEventHandlerSbt.handle()
case evt @ ExecutionEvent.Test(_, _, _, _, _, _, _) =>
val zTestEvent = ZTestEvent.convertEvent(evt, taskDef, renderer)
semaphore.withPermit(ZIO.succeed(eventHandler.handle(zTestEvent)))
// For failures, includes rendered failure message as exception
// The convertEvent method automatically creates exception with failure detailsFailure Message Contents:
The event handler also processes runtime failures that occur outside of individual tests.
case ExecutionEvent.RuntimeFailure(_, _, failure, _) =>
failure match {
case TestFailure.Assertion(_, _) =>
ZIO.unit // Assertion failures come through ExecutionEvent.Test path
case TestFailure.Runtime(cause, annotations) =>
val zTestEvent = ZTestEvent(
taskDef.fullyQualifiedName(),
taskDef.selectors().head,
Status.Failure,
cause.dieOption, // Extract throwable from ZIO Cause
annotations.get(TestAnnotation.timing).toMillis,
ZioSpecFingerprint
)
// Report runtime failure to SBT with thread-safe handling
semaphore.withPermit(ZIO.succeed(eventHandler.handle(zTestEvent)))
}All event handling is thread-safe through the use of semaphores to serialize access to SBT's event handler.
// Thread-safe event reporting
val semaphore = Semaphore.unsafe.make(1L)
def handle(event: ExecutionEvent): UIO[Unit] = {
val zTestEvent = ZTestEvent.convertEvent(event, taskDef, renderer)
// Serialize access to SBT event handler
semaphore.withPermit(ZIO.succeed(eventHandler.handle(zTestEvent)))
}This ensures that multiple concurrent tests don't interfere with each other when reporting results to SBT.
The event handling system ensures proper integration with various build tools and IDEs:
SBT Integration:
IDE Integration:
CI/CD Integration:
ExecutionEvent instancesZTestEventHandlerSbt receives and processes eventsEvent instances