or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cross-platform-execution.mdevent-handling.mdframework-integration.mdindex.mdjvm-execution.md
tile.json

jvm-execution.mddocs/

JVM Test Execution

JVM-specific test runner implementation with advanced features including signal handling, shared runtime management, and comprehensive test lifecycle control. The JVM platform provides the most feature-rich implementation of ZIO Test SBT integration.

Capabilities

ZTestRunnerJVM

Main JVM test runner that manages the complete test execution lifecycle, including runtime creation, task coordination, and result aggregation.

/**
 * JVM-specific test runner with advanced lifecycle management
 * Extends sbt.testing.Runner
 */
class ZTestRunnerJVM(
  val args: Array[String],
  val remoteArgs: Array[String], 
  testClassLoader: ClassLoader
) extends Runner {
  /** Current test renderer (mutable, updated based on test args) */
  @volatile var renderer: TestRenderer
  
  /** Optional cleanup hook for runtime shutdown */
  @volatile var shutdownHook: Option[() => Unit]
  
  /** Thread-safe collection of test execution summaries */
  val summaries: AtomicReference[Vector[Summary]]
  
  /** Creates test tasks from SBT task definitions */
  def tasks(defs: Array[TaskDef]): Array[Task]
  
  /** Returns final aggregated test results and performs cleanup */
  def done(): String
  
  /** Internal task creation with ZIO Console integration */
  def tasksZ(
    defs: Array[TaskDef], 
    console: zio.Console
  ): Array[ZTestTask[TestOutput]]
}

Usage Example:

The runner is created automatically by the framework, but you can observe its behavior:

// The runner processes all discovered test classes
// and creates individual tasks for execution
sbt test  // Triggers runner.tasks() -> task.execute() -> runner.done()

ZTestTask for JVM

JVM-specific test task implementation that extends the base task with signal handling capabilities for debugging and monitoring.

/**
 * JVM test task with signal handling support
 * Extends BaseTestTask with JVM-specific features
 */
class ZTestTask[T](
  taskDef: TaskDef,
  testClassLoader: ClassLoader,
  sendSummary: SendSummary,
  testArgs: TestArgs, 
  spec: ZIOSpecAbstract,
  runtime: zio.Runtime[T],
  console: zio.Console
) extends BaseTestTask(
  taskDef, testClassLoader, sendSummary, testArgs, spec, runtime, console
) {
  /** Enhanced test execution with signal handler installation */
  private[zio] def run(eventHandlerZ: ZTestEventHandler)(implicit trace: Trace): ZIO[Any, Throwable, Unit]
  
  /** Installs signal handlers for fiber debugging (JVM-specific) */
  private def installSignalHandlers()(implicit trace: Trace): Unit
}

Signal Handling:

The JVM implementation installs signal handlers for debugging:

  • Windows: INT signal dumps fiber information
  • Unix/Linux: INFO and USR1 signals dump fiber information
# Send signal to dump running fibers (Unix/Linux)
kill -USR1 <pid>

# On Windows, Ctrl+C triggers fiber dump

Runtime and Resource Management

The JVM runner creates sophisticated runtime environments with proper resource management:

/**
 * Creates scoped runtime with shared layers and proper cleanup
 */
val runtime: Runtime.Scoped[TestOutput] = 
  zio.Runtime.unsafe.fromLayer(sharedLayer)(Trace.empty, Unsafe.unsafe)

val shutdownHook: Option[() => Unit] = 
  Some(() => runtime.unsafe.shutdown()(Unsafe.unsafe))

Resource Lifecycle:

  1. Initialization: Creates shared ZLayer combining all test spec bootstrap layers
  2. Execution: Runs tests within the scoped runtime context
  3. Cleanup: Automatically shuts down runtime and releases resources via shutdown hook

Test Argument Processing

The JVM runner processes and applies test arguments for filtering and configuration:

/**
 * Processes command-line arguments into structured TestArgs
 */
val testArgs: TestArgs = TestArgs.parse(args)

// Updates renderer based on arguments
renderer = testArgs.testRenderer

// Creates event printer layer with specified renderer  
val sharedTestOutputLayer = 
  ExecutionEventPrinter.live(console, testArgs.testEventRenderer) >>> 
  TestOutput.live

Common Test Arguments:

# Run tests with specific renderer
sbt "test -- --format pretty"

# Run tests matching pattern
sbt "test -- --grep 'user login'"

# Run tests with tags
sbt "test -- --tags integration"

Summary Collection and Reporting

The JVM runner collects and aggregates test results with comprehensive reporting:

/**
 * Thread-safe summary collection
 */
val summaries: AtomicReference[Vector[Summary]] = 
  new AtomicReference(Vector.empty)

def sendSummary: SendSummary = SendSummary.fromSendZIO { summary =>
  ZIO.succeed {
    summaries.updateAndGet(_ :+ summary)
    ()
  }
}

/**
 * Generates final test report with colored output
 */
def done(): String = {
  val allSummaries = summaries.get
  val total = allSummaries.map(_.total).sum
  val ignore = allSummaries.map(_.ignore).sum
  
  val compositeSummary = allSummaries.foldLeft(Summary.empty)(_.add(_))
  val renderedSummary = renderer.renderSummary(compositeSummary)
  
  // Returns colored summary or appropriate message for no tests
  if (allSummaries.nonEmpty && total != ignore)
    colored(renderedSummary)
  else if (ignore > 0)
    s"${Console.YELLOW}All eligible tests are currently ignored${Console.RESET}"
  else
    s"${Console.YELLOW}No tests were executed${Console.RESET}"
}

Platform-Specific Optimizations

The JVM implementation includes several optimizations specific to the JVM platform:

  • Shared Runtime: Single runtime instance shared across all test tasks for efficiency
  • Signal Handlers: Debug support via OS signals for fiber inspection
  • Thread Safety: Atomic operations for summary collection across concurrent test execution
  • Memory Management: Proper shutdown hooks to prevent resource leaks
  • Class Loading: Sophisticated reflection-based spec loading with portable Scala reflection