or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/maven-org-scoverage--scalac-scoverage-runtime-2-11

Runtime support library for scoverage code coverage tool that collects coverage data during program execution when code has been instrumented by the scalac compiler plugin.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/org.scoverage/scalac-scoverage-runtime_2.11@1.4.x

To install, run

npx @tessl/cli install tessl/maven-org-scoverage--scalac-scoverage-runtime-2-11@1.4.0

index.mddocs/

Scalac Scoverage Runtime

Scalac Scoverage Runtime is the runtime support library for scoverage, a code coverage tool for Scala. This library provides the essential instrumentation infrastructure that collects coverage data during program execution when code has been instrumented by the scalac-scoverage-plugin compiler plugin. It supports both JVM and JavaScript platforms through Scala.js.

Package Information

  • Package Name: scalac-scoverage-runtime_2.11
  • Package Type: Maven
  • Language: Scala
  • Platforms: JVM, JavaScript (Scala.js)
  • Installation:
    • SBT: libraryDependencies += "org.scoverage" %% "scalac-scoverage-runtime" % "1.4.11"
    • Maven: <artifactId>scalac-scoverage-runtime_2.11</artifactId>

Core Imports

import scoverage.Invoker
import scoverage.Platform._

For JavaScript-specific functionality:

import scalajssupport.{File, FileWriter, Source}

Basic Usage

The runtime library is typically used automatically by instrumented code, but can also be used directly for coverage analysis:

import scoverage.Invoker
import scoverage.Platform._

// Record statement execution (called by instrumented code)
Invoker.invoked(statementId = 42, dataDir = "/path/to/coverage/data")

// Find measurement files for analysis
val dataDir = "/path/to/coverage/data"
val measurementFiles = Invoker.findMeasurementFiles(dataDir)

// Load invoked statement IDs from files
val invokedIds = Invoker.invoked(measurementFiles)
println(s"Covered statements: ${invokedIds.size}")

Architecture

The runtime library uses a cross-platform architecture with several key components:

  • Platform Abstraction: scoverage.Platform provides unified interfaces across JVM and JavaScript with compile-time type resolution
  • Coverage Collection: scoverage.Invoker handles thread-safe recording of statement executions with per-thread measurement files
  • UUID-based File Naming: Uses runtime UUID (runtimeUUID) combined with thread ID to create unique measurement files
  • File System Abstraction: JavaScript support includes comprehensive file system abstractions that auto-detect and adapt to different JS runtimes (Node.js, Rhino, PhantomJS)
  • Thread Safety: Uses ThreadLocal storage and concurrent TrieMap data structures on JVM, simplified HashMap collections on JavaScript
  • Deduplication: In-memory tracking of recorded statement IDs prevents duplicate writes within the same JVM execution

Capabilities

Coverage Data Collection

Core instrumentation runtime that records which statements have been executed during program execution.

object Invoker {
  /**
   * Records that a statement has been executed
   * @param id the ID of the statement that was invoked
   * @param dataDir the directory where measurement data is stored
   * @param reportTestName whether to include test name information
   */
  def invoked(id: Int, dataDir: String, reportTestName: Boolean = false): Unit

  /**
   * Gets the measurement file path for a given data directory
   * @param dataDir the data directory as File
   * @return measurement file for current thread
   */
  def measurementFile(dataDir: File): File

  /**
   * Gets the measurement file path for a given data directory
   * @param dataDir the data directory path as String
   * @return measurement file for current thread  
   */
  def measurementFile(dataDir: String): File

  /**
   * Finds all measurement files in the given directory
   * @param dataDir the directory to search as String
   * @return array of measurement files
   */
  def findMeasurementFiles(dataDir: String): Array[File]

  /**
   * Finds all measurement files in the given directory
   * @param dataDir the directory to search as File
   * @return array of measurement files
   */
  def findMeasurementFiles(dataDir: File): Array[File]

  /**
   * Loads all invoked statement IDs from measurement files
   * @param files sequence of measurement files to read
   * @return set of statement IDs that were executed
   * @note Each line may contain just the ID, or ID followed by test name (space-separated)
   */
  def invoked(files: Seq[File]): Set[Int]

  /**
   * Utility method to identify calling ScalaTest suite
   * @return name of calling test suite or empty string
   */
  def getCallingScalaTest: String
}

Platform Abstraction

Provides unified cross-platform interfaces for file operations and concurrent data structures.

object Platform {
  // JVM Platform (when compiling to JVM bytecode)
  type ThreadSafeMap[A, B] = scala.collection.concurrent.TrieMap[A, B]
  lazy val ThreadSafeMap: TrieMap.type = TrieMap
  type File = java.io.File
  type FileWriter = java.io.FileWriter
  type FileFilter = java.io.FileFilter
  lazy val Source: scala.io.Source.type = scala.io.Source

  // JavaScript Platform (when compiling to JavaScript via Scala.js)
  type ThreadSafeMap[A, B] = scala.collection.mutable.HashMap[A, B]
  lazy val ThreadSafeMap: HashMap.type = HashMap
  type File = scalajssupport.File
  type FileWriter = scalajssupport.FileWriter
  type FileFilter = scalajssupport.FileFilter
  lazy val Source: scalajssupport.Source.type = scalajssupport.Source
}

JavaScript File System Support

JavaScript-compatible file system abstractions that emulate Java I/O APIs for Scala.js environments.

// JavaScript File abstraction
class File(path: String) {
  /**
   * Creates a File with parent path and child name
   * @param path parent directory path
   * @param child child file/directory name
   */
  def this(path: String, child: String)

  /** Deletes the file or directory */
  def delete(): Unit

  /** Returns the absolute path */
  def getAbsolutePath(): String

  /** Returns the file name */
  def getName(): String

  /** Returns the file path */
  def getPath(): String

  /** Checks if this is a directory */
  def isDirectory(): Boolean

  /** Creates directory structure */
  def mkdirs(): Unit

  /** Lists all files in directory */
  def listFiles(): Array[File]

  /** Lists files matching filter */
  def listFiles(filter: FileFilter): Array[File]

  /** Reads entire file content as string */
  def readFile(): String
}

object File {
  /** Joins path components */
  def pathJoin(path: String, child: String): String

  /** Writes data to file */
  def write(path: String, data: String, mode: String = "a"): Unit
}

// JavaScript FileWriter abstraction
class FileWriter(file: File, append: Boolean) {
  /** Create FileWriter for file without append mode */
  def this(file: File)
  /** Create FileWriter for file path without append mode */
  def this(file: String)  
  /** Create FileWriter for file path with append mode */
  def this(file: String, append: Boolean)

  /** Appends character sequence, returns this for chaining */
  def append(csq: CharSequence): FileWriter

  /** Closes the writer */
  def close(): Unit

  /** Flushes the writer */
  def flush(): Unit
}

// JavaScript Source abstraction
object Source {
  /** Creates source reader from file */
  def fromFile(file: File): scala.io.Source
}

// File filtering interface
trait FileFilter {
  /** Returns true if file should be included */
  def accept(file: File): Boolean
}

// JavaScript File trait hierarchy
trait JsFile {
  /** Deletes the file or directory */
  def delete(): Unit
  /** Returns the absolute path */
  def getAbsolutePath(): String
  /** Returns the file name */
  def getName(): String
  /** Returns the file path */
  def getPath(): String
  /** Checks if this is a directory */
  def isDirectory(): Boolean
  /** Creates directory structure */
  def mkdirs(): Unit
  /** Lists all files in directory */
  def listFiles(): Array[File]
  /** Lists files matching filter */
  def listFiles(filter: FileFilter): Array[File]
  /** Reads entire file content as string */
  def readFile(): String
}

trait JsFileObject {
  /** Writes data to file with mode (default append) */
  def write(path: String, data: String, mode: String = "a"): Unit
  /** Joins path components */
  def pathJoin(path: String, child: String): String
  /** Creates JsFile instance for path */
  def apply(path: String): JsFile
}

Types

// Core platform types (resolved at compile time based on target platform)
type ThreadSafeMap[A, B] // TrieMap on JVM, HashMap on JS
type File // java.io.File on JVM, scalajssupport.File on JS
type FileWriter // java.io.FileWriter on JVM, scalajssupport.FileWriter on JS
type FileFilter // java.io.FileFilter on JVM, scalajssupport.FileFilter on JS

Error Handling

The library handles errors gracefully across both platforms:

  • File operations: JavaScript implementations catch exceptions and return appropriate defaults (e.g., false for isDirectory() if file doesn't exist)
  • Thread safety:
    • JVM: Synchronized blocks prevent race conditions in concurrent ID tracking using dataDirToIds.synchronized to guard against SI-7943
    • JavaScript: Single-threaded environment eliminates race conditions
  • Measurement files: Each thread writes to separate files (using thread ID in filename) to avoid write conflicts, especially important on Windows
  • Empty lines: The invoked(files) method skips empty lines in measurement files using if (!line.isEmpty)
  • ThreadLocal cleanup: ThreadLocal storage for FileWriter instances is properly managed per thread
  • File parsing: Measurement file lines are parsed as integers with proper error handling for malformed data

Platform-Specific Behavior

JVM Platform

  • Uses java.util.concurrent.TrieMap for thread-safe collections
  • Leverages standard Java I/O classes
  • Supports true concurrent access across multiple threads
  • Uses ThreadLocal storage for file writers

JavaScript Platform

  • Uses scala.collection.mutable.HashMap (JavaScript is single-threaded)
  • Provides file system abstractions for different JS environments
  • Automatically detects and adapts to Node.js, Rhino, or PhantomJS environments using global object detection
  • File operations delegate to environment-specific implementations (NodeFile, RhinoFile, PhantomFile)
  • Environment detection: checks for Packages (Rhino), callPhantom (PhantomJS), or defaults to Node.js

Measurement File Format

  • File naming: scoverage.measurements.<UUID>.<threadId>
  • Content format: Each line contains either:
    • Just the statement ID: "123"
    • ID with test name: "123 MySuite" (when reportTestName = true)
  • Multiple files: One file per thread to avoid write conflicts
  • Parsing: Only the first part (before space) is used as the statement ID

Usage Examples

Recording coverage data (typically done by instrumented code):

import scoverage.Invoker

// Record that statement 123 was executed
Invoker.invoked(123, "/tmp/scoverage-data")

// Record with test name information
Invoker.invoked(456, "/tmp/scoverage-data", reportTestName = true)

Analyzing coverage data:

import scoverage.Invoker
import scoverage.Platform._

val dataDir = "/tmp/scoverage-data"

// Find all measurement files
val files = Invoker.findMeasurementFiles(dataDir)
println(s"Found ${files.length} measurement files")

// Load executed statement IDs
val executedIds = Invoker.invoked(files.toSeq)
println(s"Total statements executed: ${executedIds.size}")

// Check if specific statement was executed
val statementId = 42
if (executedIds.contains(statementId)) {
  println(s"Statement $statementId was executed")
}

Cross-platform file operations:

import scoverage.Platform._

// This works on both JVM and JavaScript
val file = new File("/path/to/data", "measurements.txt")
val writer = new FileWriter(file, append = true)
writer.append("42\n")
writer.close()

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