ZIO Test SBT Framework integration that provides test interface implementation for running ZIO-based tests within SBT build environments
—
Scala Native test runner optimized for native compilation with efficient memory usage and native-specific execution patterns. Provides cross-platform compatibility for native executables.
Abstract base class for Scala Native test runners that provides common functionality optimized for native compilation and execution.
/**
* Base Scala Native test runner with master/slave architecture support
* Optimized for native compilation with efficient memory usage
*/
sealed abstract class ZTestRunnerNative(
val args: Array[String],
remoteArgs0: Array[String],
testClassLoader: ClassLoader,
runnerType: String
) extends Runner {
/** Get remote execution arguments */
def remoteArgs(): Array[String]
/** Abstract method for summary handling strategy */
def sendSummary: SendSummary
/** Thread-safe queue for collecting test summaries */
val summaries: ConcurrentLinkedQueue[Summary]
/**
* Create test tasks from discovered test definitions
* @param defs Array of SBT task definitions
* @return Array of executable native test tasks
*/
def tasks(defs: Array[TaskDef]): Array[Task]
/**
* Complete test execution and return formatted results
* Efficiently processes summaries with minimal memory allocation
* @return Formatted test results or "No tests executed" message
*/
def done(): String
/**
* Receive serialized summaries from distributed execution
* @param summary Serialized summary string from SummaryProtocol
* @return Optional response message (always None for Native)
*/
override def receiveMessage(summary: String): Option[String]
/**
* Serialize task for distributed execution
* @param task Task to serialize
* @param serializer SBT-provided serialization function
* @return Serialized task string
*/
override def serializeTask(task: Task, serializer: TaskDef => String): String
/**
* Deserialize task from distributed execution
* @param task Serialized task string
* @param deserializer SBT-provided deserialization function
* @return Reconstructed test task
*/
override def deserializeTask(task: String, deserializer: String => TaskDef): Task
}Master runner for single-process Scala Native execution, optimized for native executable environments.
/**
* Master Scala Native test runner for single-process execution
* Provides efficient local summary collection for native executables
*/
final class ZMasterTestRunner(
args: Array[String],
remoteArgs: Array[String],
testClassLoader: ClassLoader
) extends ZTestRunnerNative(args, remoteArgs, testClassLoader, "master") {
/**
* Summary sender that collects results locally using thread-safe queue
* Optimized for native execution with minimal overhead
*/
override val sendSummary: SendSummary
}Slave runner for distributed Scala Native execution, used when tests are executed across multiple native processes.
/**
* Slave Scala Native test runner for distributed execution
* Sends summaries to master process via serialization protocol
*/
final class ZSlaveTestRunner(
args: Array[String],
remoteArgs: Array[String],
testClassLoader: ClassLoader,
val sendSummary: SendSummary
) extends ZTestRunnerNative(args, remoteArgs, testClassLoader, "slave")Scala Native-specific test task implementation with blocking execution optimized for native environments.
/**
* Scala Native-specific test task with blocking execution
* Optimized for native compilation and minimal memory usage
*/
sealed class ZTestTask(
taskDef: TaskDef,
testClassLoader: ClassLoader,
runnerType: String,
sendSummary: SendSummary,
testArgs: TestArgs,
spec: ZIOSpecAbstract
) extends BaseTestTask(
taskDef, testClassLoader, sendSummary, testArgs, spec,
zio.Runtime.default, zio.Console.ConsoleLive
) {
/**
* Execute test with blocking semantics suitable for native execution
* Uses Await.result for synchronous execution model
* @param eventHandler SBT event handler for reporting
* @param loggers Array of SBT loggers
* @return Empty array (no sub-tasks created)
*/
override def execute(eventHandler: EventHandler, loggers: Array[Logger]): Array[sbt.testing.Task]
}Factory methods for creating Scala Native test tasks.
object ZTestTask {
/**
* Create a Scala Native test task instance
* @param taskDef SBT task definition
* @param testClassLoader ClassLoader for loading test classes
* @param runnerType Runner type identifier ("master" or "slave")
* @param sendSummary Summary collection effect
* @param args Parsed test arguments
* @return Configured native ZTestTask instance
*/
def apply(
taskDef: TaskDef,
testClassLoader: ClassLoader,
runnerType: String,
sendSummary: SendSummary,
args: TestArgs
): ZTestTask
}The native platform uses optimized summary processing for minimal memory allocation:
def done(): String = {
val log = new StringBuilder
var summary = summaries.poll()
var total = 0
var ignore = 0
val isEmpty = summary eq null
// Process summaries without creating intermediate collections
while (summary ne null) {
total += summary.total
ignore += summary.ignore
val details = summary.failureDetails
if (!details.isBlank) {
log append colored(details)
log append '\n'
}
summary = summaries.poll()
}
if (isEmpty || total == ignore)
s"${Console.YELLOW}No tests were executed${Console.RESET}"
else
log.append("Done").result()
}Native test tasks use blocking execution for simplicity and efficiency:
override def execute(eventHandler: EventHandler, loggers: Array[Logger]): Array[sbt.testing.Task] = {
var resOutter: CancelableFuture[Unit] = null
try {
resOutter = Runtime.default.unsafe.runToFuture {
ZIO.consoleWith { console =>
(for {
summary <- spec.runSpecAsApp(FilteredSpec(spec.spec, args), args, console)
_ <- sendSummary.provideSomeEnvironment[Any](_.add(summary))
_ <- ZIO.when(summary.status == Summary.Failure) {
ZIO.attempt(eventHandler.handle(ZTestEvent(/* failure event */)))
}
} yield ()).provideLayer(
sharedFilledTestLayer +!+ (Scope.default >>> spec.bootstrap)
)
}.mapError {
case t: Throwable => t
case other => new RuntimeException(s"Unknown error during tests: $other")
}
}
// Block until completion for native execution model
Await.result(resOutter, SDuration.Inf)
Array()
} catch {
case t: Throwable =>
if (resOutter != null) resOutter.cancel()
throw t
}
}The native platform uses the same serialization protocol as JavaScript for inter-process communication:
/**
* Serialization protocol for test summaries in native environments
* Identical to JavaScript implementation for consistency
*/
object SummaryProtocol {
/**
* Serialize test summary to string for transmission
* @param summary Test execution summary to serialize
* @return Tab-separated string representation
*/
def serialize(summary: Summary): String
/**
* Deserialize string back to test summary
* @param s Serialized summary string
* @return Optional Summary if deserialization succeeds
*/
def deserialize(s: String): Option[Summary]
/**
* Escape tab characters for serialization
* @param token String token to escape
* @return Escaped string
*/
def escape(token: String): String
/**
* Unescape tab characters from serialized tokens
* @param token Escaped string token
* @return Unescaped original string
*/
def unescape(token: String): String
}Scala Native provides several optimizations:
ConcurrentLinkedQueue and StringBuilder for minimal allocationsAwait.result for native environmentsNative execution includes comprehensive error handling:
// Explicit error mapping for native environments
.mapError {
case t: Throwable => t
case other => new RuntimeException(s"Unknown error during tests: $other")
}
// Proper resource cleanup on cancellation
catch {
case t: Throwable =>
if (resOutter != null) resOutter.cancel()
throw t
}The native platform has specific considerations:
// Native framework usage is identical to other platforms
val framework = new ZTestFramework()
val runner = framework.runner(args, remoteArgs, classLoader)
// Native compilation produces fast-starting executable
// $ scala-native:nativeLink
// $ ./target/scala-native/my-testsInstall with Tessl CLI
npx tessl i tessl/maven-dev-zio--zio-test-sbt-3