CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-scoverage--scalac-scoverage-plugin

Comprehensive code coverage tool for Scala providing statement and branch coverage through compiler plugin instrumentation and report generation

Pending
Overview
Eval results
Files

runtime.mddocs/

Runtime Support

The runtime module provides the infrastructure for collecting coverage measurements during test execution. When the compiler plugin instruments code, it injects calls to the runtime system that record which statements were executed. This module supports JVM, Scala.js, and Scala Native platforms.

Capabilities

Coverage Data Collection

The main interface for recording coverage measurements during runtime.

/**
 * Core runtime object for collecting coverage measurements
 * This object is called by instrumented code to record statement execution
 */
object Invoker {
  /**
   * Record that a statement was invoked during execution
   * @param id Unique identifier for the statement
   * @param dataDir Directory where measurement data should be written
   * @param reportTestName Whether to include test name information
   */
  def invoked(id: Int, dataDir: String, reportTestName: Boolean = false): Unit
  
  /**
   * Get the name of the currently executing ScalaTest test
   * @returns Test name if running under ScalaTest, empty string otherwise
   */
  def getCallingScalaTest: String
  
  /**
   * Get the measurement file for a data directory
   * @param dataDir Coverage data directory as File
   * @returns File where measurements are written
   */
  def measurementFile(dataDir: File): File
  
  /**
   * Get the measurement file for a data directory
   * @param dataDir Coverage data directory as String path
   * @returns File where measurements are written
   */
  def measurementFile(dataDir: String): File
  
  /**
   * Find all measurement files in a data directory
   * @param dataDir Coverage data directory as String path
   * @returns Array of measurement files found
   */
  def findMeasurementFiles(dataDir: String): Array[File]
  
  /**
   * Find all measurement files in a data directory
   * @param dataDir Coverage data directory as File
   * @returns Array of measurement files found
   */
  def findMeasurementFiles(dataDir: File): Array[File]
  
  /**
   * Load statement IDs that were invoked from measurement files
   * @param files Sequence of measurement files to read
   * @returns Set of statement IDs that were executed
   */
  def invoked(files: Seq[File]): Set[Int]
}

Usage Examples:

import scoverage.Invoker
import java.io.File

// This is typically called by instrumented code, not manually
Invoker.invoked(12345, "target/scoverage-data", reportTestName = true)

// Find measurement files
val dataDir = new File("target/scoverage-data")
val measurementFiles = Invoker.findMeasurementFiles(dataDir)

// Load executed statement IDs
val executedStatements = Invoker.invoked(measurementFiles.toSeq)
println(s"Executed ${executedStatements.size} statements")

// Get measurement file location
val measurementFile = Invoker.measurementFile(dataDir)
println(s"Measurements written to: ${measurementFile.getAbsolutePath}")

Platform Abstractions

The runtime provides platform-specific abstractions for different Scala targets.

JVM Platform

/**
 * JVM-specific platform abstractions
 */
object Platform {
  /** Thread-safe map implementation for JVM */
  type ThreadSafeMap[A, B] = scala.collection.concurrent.TrieMap[A, B]
  lazy val ThreadSafeMap = scala.collection.concurrent.TrieMap
  
  /** File type for JVM platform */
  type File = java.io.File
  
  /** FileWriter type for JVM platform */
  type FileWriter = java.io.FileWriter
  
  /** FileFilter type for JVM platform */  
  type FileFilter = java.io.FileFilter
  
  /** Source companion object for reading files */
  lazy val Source = scala.io.Source
  
  /** Generate random UUID (not cryptographically secure) */
  def insecureRandomUUID(): java.util.UUID
}

Scala.js Platform

/**
 * Scala.js-specific platform abstractions
 */
object Platform {
  /** Thread-safe map implementation for Scala.js */
  type ThreadSafeMap[A, B] = scala.collection.mutable.HashMap[A, B]
  lazy val ThreadSafeMap = scala.collection.mutable.HashMap
  
  /** File type for Scala.js platform */
  type File = scalajssupport.File
  
  /** FileWriter type for Scala.js platform */
  type FileWriter = scalajssupport.FileWriter
  
  /** FileFilter type for Scala.js platform */
  type FileFilter = scalajssupport.FileFilter
  
  /** Source companion object for reading files */
  lazy val Source = scalajssupport.Source
  
  /** Generate random UUID (not cryptographically secure) */
  def insecureRandomUUID(): java.util.UUID
}

Scala Native Platform

/**
 * Scala Native-specific platform abstractions
 */
object Platform {
  /** Thread-safe map implementation for Scala Native */
  type ThreadSafeMap[A, B] = scala.collection.mutable.HashMap[A, B]
  lazy val ThreadSafeMap = scala.collection.mutable.HashMap
  
  /** File type for Scala Native platform */
  type File = scalanativesupport.File
  
  /** FileWriter type for Scala Native platform */
  type FileWriter = scalanativesupport.FileWriter
  
  /** FileFilter type for Scala Native platform */
  type FileFilter = scalanativesupport.FileFilter
  
  /** Source companion object for reading files */
  lazy val Source = scalanativesupport.Source
  
  /** Generate random UUID (not cryptographically secure) */
  def insecureRandomUUID(): java.util.UUID
}

Scala.js File Support

File system abstractions for Scala.js environments.

/**
 * File abstraction for Scala.js environments
 * @param path File path
 */
class File(path: String) {
  /**
   * Create file with parent path and child name
   * @param path Parent directory path
   * @param child Child file/directory name
   */
  def this(path: String, child: String)
  
  /** Delete this file or directory */
  def delete(): Unit
  
  /** Get absolute path of this file */
  def getAbsolutePath(): String
  
  /** Get name of this file (last component of path) */
  def getName(): String
  
  /** Get path of this file */
  def getPath(): String
  
  /** Check if this file represents a directory */
  def isDirectory(): Boolean
  
  /** Create directory and any necessary parent directories */
  def mkdirs(): Unit
  
  /** List all files in this directory */
  def listFiles(): Array[File]
  
  /**
   * List files in this directory matching the filter
   * @param filter FileFilter to apply
   * @returns Array of matching files
   */
  def listFiles(filter: FileFilter): Array[File]
  
  /** Read entire contents of this file as string */
  def readFile(): String
  
  override def toString: String
}

object File {
  /** Global JavaScript object reference */
  val globalObject: js.Dynamic
  
  /** JavaScript file operations object */
  val jsFile: JsFileObject
  
  /**
   * Join path components
   * @param path Base path
   * @param child Child component to append
   * @returns Joined path
   */
  def pathJoin(path: String, child: String): String
  
  /**
   * Write data to file
   * @param path File path to write to
   * @param data String data to write
   * @param mode Write mode ("a" for append, "w" for overwrite)
   */
  def write(path: String, data: String, mode: String = "a"): Unit
}

File I/O Support

Additional file system support classes for different JavaScript environments.

/**
 * File writer abstraction for Scala.js
 */
class FileWriter(file: File) {
  def write(data: String): Unit
  def close(): Unit
}

/**
 * File filter for selecting files
 */
trait FileFilter {
  def accept(file: File): Boolean
}

/**
 * Source file reading support
 */
object Source {
  /**
   * Create source from file
   * @param file File to read from
   * @returns Source for reading file contents
   */
  def fromFile(file: File): Source
  
  /**
   * Create source from file path
   * @param path Path to file
   * @returns Source for reading file contents
   */
  def fromFile(path: String): Source
}

class Source {
  /** Get all lines from source as iterator */
  def getLines(): Iterator[String]
  
  /** Close the source */
  def close(): Unit
}

Usage Examples:

import scoverage.Platform
import Platform.{File, FileWriter, Source}

// Create and write to file (works across all platforms)
val file = new File("coverage-data", "measurements.txt")
file.mkdirs() // Ensure parent directory exists

val writer = new FileWriter(file)
writer.write("statement_id:12345\n")
writer.close()

// Read file contents
val source = Source.fromFile(file)
val lines = source.getLines().toList
source.close()

// Use platform-specific thread-safe map
val measurements = Platform.ThreadSafeMap[Int, String]()
measurements.put(12345, "com.example.Service.method")

Runtime Integration

Instrumented Code Example

When the compiler plugin instruments code, it injects calls like:

// Original code:
def processUser(user: User): String = {
  val name = user.name
  if (name.nonEmpty) {
    s"Hello, $name"
  } else {
    "Hello, Anonymous"
  }
}

// Instrumented code (simplified):
def processUser(user: User): String = {
  scoverage.Invoker.invoked(1001, "target/scoverage-data")
  val name = {
    scoverage.Invoker.invoked(1002, "target/scoverage-data")  
    user.name
  }
  if ({
    scoverage.Invoker.invoked(1003, "target/scoverage-data")
    name.nonEmpty
  }) {
    scoverage.Invoker.invoked(1004, "target/scoverage-data")
    s"Hello, $name"
  } else {
    scoverage.Invoker.invoked(1005, "target/scoverage-data")
    "Hello, Anonymous"  
  }
}

Test Integration

The runtime automatically detects ScalaTest execution:

import org.scalatest.flatspec.AnyFlatSpec

class UserServiceSpec extends AnyFlatSpec {
  "UserService" should "process valid users" in {
    // When this test runs, Invoker.getCallingScalaTest returns:
    // "UserServiceSpec.UserService should process valid users"
    val result = userService.processUser(validUser)
    assert(result.startsWith("Hello,"))
  }
}

Multi-Module Support

For multi-module projects, each module writes to its own measurement files:

// Module A measurements: target/scoverage-data/scoverage.measurements.module-a.12345
// Module B measurements: target/scoverage-data/scoverage.measurements.module-b.67890

val allFiles = Invoker.findMeasurementFiles("target/scoverage-data")
val allInvoked = Invoker.invoked(allFiles.toSeq)
// Contains statement IDs from all modules

Error Handling

  • IOException: File I/O operations may fail if coverage data directory is not writable
  • SecurityException: May be thrown if running in restricted security environment
  • OutOfMemoryError: Can occur with very large codebases generating excessive measurement data
  • ConcurrentModificationException: Rare threading issues when multiple test processes write simultaneously

Install with Tessl CLI

npx tessl i tessl/maven-org-scoverage--scalac-scoverage-plugin

docs

aggregation.md

cobertura-reports.md

coverage-model.md

html-reports.md

index.md

io-utils.md

plugin.md

runtime.md

serialization.md

xml-reports.md

tile.json