Comprehensive code coverage tool for Scala providing statement and branch coverage through compiler plugin instrumentation and report generation
npx @tessl/cli install tessl/maven-org-scoverage--scalac-scoverage-plugin@2.3.0Scalac Scoverage Plugin is a comprehensive code coverage tool for Scala that provides statement and branch coverage through compiler plugin instrumentation. It offers multiple modules working together to instrument code during compilation, collect coverage data during test execution, and generate detailed coverage reports in various formats including HTML, XML, and Cobertura.
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.0")"org.scoverage" %% "scalac-scoverage-plugin" % "2.3.0""org.scoverage" %% "scalac-scoverage-reporter" % "2.3.0""org.scoverage" %% "scalac-scoverage-domain" % "2.3.0""org.scoverage" %% "scalac-scoverage-runtime" % "2.3.0""org.scoverage" %% "scalac-scoverage-serializer" % "2.3.0"// Compiler plugin (used automatically when enabled)
import scoverage.{ScoveragePlugin, ScoverageOptions}
// Reporter module for generating reports
import scoverage.reporter._
// Domain model for coverage data
import scoverage.domain._
// Serialization support
import scoverage.serialize.Serializer
// Runtime instrumentation
import scoverage.Invoker// project/plugins.sbt
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.0")
// Run coverage
sbt clean coverage test coverageReportimport java.io.File
import scoverage.reporter.{ScoverageHtmlWriter, ScoverageXmlWriter}
import scoverage.serialize.Serializer
// Load coverage data from serialized file
val dataDir = new File("target/scoverage-data")
val sourceRoot = new File("src/main/scala")
val coverageFile = Serializer.coverageFile(dataDir)
val coverage = Serializer.deserialize(coverageFile, sourceRoot)
// Apply measurement data
val measurementFiles = scoverage.reporter.IOUtils.findMeasurementFiles(dataDir)
val measurements = scoverage.reporter.IOUtils.invoked(measurementFiles.toIndexedSeq)
coverage.apply(measurements)
// Generate HTML report
val outputDir = new File("target/scoverage-report")
val htmlWriter = new ScoverageHtmlWriter(Seq(sourceRoot), outputDir, None)
htmlWriter.write(coverage)
// Generate XML report
val xmlWriter = new ScoverageXmlWriter(Seq(sourceRoot), outputDir, false, None)
xmlWriter.write(coverage)// Enable compiler plugin with options
scalacOptions ++= Seq(
"-Xplugin:path/to/scalac-scoverage-plugin.jar",
"-P:scoverage:dataDir:target/scoverage-data",
"-P:scoverage:reportTestName:true",
"-P:scoverage:excludedPackages:.*\\.test\\..*"
)The project consists of five main modules working together:
scoverage): Scala compiler plugin that instruments source code during compilationscoverage): Runtime support for collecting coverage measurements during executionscoverage.domain): Core data model for coverage metrics, statements, and filesscoverage.serialize): Coverage data persistence and loading utilitiesscoverage.reporter): Report generation in HTML, XML, and Cobertura formatsScala compiler plugin that instruments source code with coverage measurement calls during compilation.
class ScoveragePlugin(val global: Global) extends Plugin {
val name: String
val description: String
val components: List[PluginComponent]
def init(opts: List[String], error: String => Unit): Boolean
val optionsHelp: Option[String]
}
case class ScoverageOptions(
excludedPackages: Seq[String],
excludedFiles: Seq[String],
excludedSymbols: Seq[String],
dataDir: String,
reportTestName: Boolean,
sourceRoot: String
)Runtime support for collecting coverage measurements during test execution.
object Invoker {
def invoked(id: Int, dataDir: String, reportTestName: Boolean = false): Unit
def measurementFile(dataDir: File): File
def findMeasurementFiles(dataDir: File): Array[File]
def invoked(files: Seq[File]): Set[Int]
}Generates comprehensive visual HTML coverage reports with source code highlighting, coverage statistics, and interactive navigation.
class ScoverageHtmlWriter(
sourceDirectories: Seq[File],
outputDir: File,
sourceEncoding: Option[String]
) extends BaseReportWriter {
def write(coverage: Coverage): Unit
}Generates structured XML coverage reports in scoverage format for programmatic consumption and CI/CD integration.
class ScoverageXmlWriter(
sourceDirectories: Seq[File],
outputDir: File,
debug: Boolean,
sourceEncoding: Option[String]
) extends BaseReportWriter {
def write(coverage: Coverage): Unit
}Generates Cobertura-compatible XML reports for integration with build systems and coverage analysis tools.
class CoberturaXmlWriter(
sourceDirectories: Seq[File],
outputDir: File,
sourceEncoding: Option[String]
) extends BaseReportWriter {
def write(coverage: Coverage): Unit
}Core data structures representing coverage information including statements, classes, methods, packages, and files with coverage metrics.
case class Coverage() extends CoverageMetrics with MethodBuilders
with ClassBuilders with PackageBuilders with FileBuilders {
def add(stmt: Statement): Unit
def addIgnoredStatement(stmt: Statement): Unit
def apply(ids: Iterable[(Int, String)]): Unit
def invoked(id: (Int, String)): Unit
def risks(limit: Int): Seq[MeasuredClass]
}
case class Statement(
location: Location,
id: Int,
start: Int,
end: Int,
line: Int,
desc: String,
symbolName: String,
treeName: String,
branch: Boolean,
var count: Int = 0,
ignored: Boolean = false,
tests: mutable.Set[String] = mutable.Set[String]()
) {
def source: String
def invoked(test: String): Unit
def isInvoked: Boolean
}Combines coverage data from multiple subprojects or test runs into unified reports.
object CoverageAggregator {
def aggregate(dataDirs: Seq[File], sourceRoot: File): Option[Coverage]
def aggregatedCoverage(dataDirs: Seq[File], sourceRoot: File): Coverage
}Persists and loads coverage data to/from files for report generation and analysis.
object Serializer {
def serialize(coverage: Coverage, file: File, sourceRoot: File): Unit
def serialize(coverage: Coverage, dataDir: String, sourceRoot: String): Unit
def deserialize(file: File, sourceRoot: File): Coverage
def coverageFile(dataDir: File): File
}Provides utilities for working with coverage data files, measurement files, and report output.
object IOUtils {
def findMeasurementFiles(dataDir: File): Array[File]
def invoked(files: Seq[File], encoding: String = "UTF-8"): Set[(Int, String)]
def writeToFile(file: File, str: String, encoding: Option[String]): Unit
def clean(dataDir: File): Unit
def reportFile(outputDir: File, debug: Boolean = false): File
}trait CoverageMetrics {
def statements: Iterable[Statement]
def statementCount: Int
def ignoredStatements: Iterable[Statement]
def ignoredStatementCount: Int
def invokedStatements: Iterable[Statement]
def invokedStatementCount: Int
def statementCoverage: Double
def statementCoveragePercent: Double
def statementCoverageFormatted: String
def branches: Iterable[Statement]
def branchCount: Int
def invokedBranches: Iterable[Statement]
def invokedBranchesCount: Int
def branchCoverage: Double
def branchCoveragePercent: Double
def branchCoverageFormatted: String
}
case class Location(
packageName: String,
className: String,
fullClassName: String,
classType: ClassType,
method: String,
sourcePath: String
)
sealed trait ClassType
object ClassType {
case object Object extends ClassType
case object Class extends ClassType
case object Trait extends ClassType
def fromString(str: String): ClassType
}case class MeasuredClass(fullClassName: String, statements: Iterable[Statement])
extends CoverageMetrics with MethodBuilders {
def source: String
def loc: Int
def displayClassName: String
}
case class MeasuredMethod(name: String, statements: Iterable[Statement]) extends CoverageMetrics
case class MeasuredPackage(name: String, statements: Iterable[Statement])
extends CoverageMetrics with ClassCoverage with ClassBuilders with FileBuilders
case class MeasuredFile(source: String, statements: Iterable[Statement])
extends CoverageMetrics with ClassCoverage with ClassBuilders {
def filename: String
def loc: Int
}sealed trait StatementStatus
case object Invoked extends StatementStatus
case object NotInvoked extends StatementStatus
case object NoData extends StatementStatus
case class ClassRef(name: String) {
lazy val simpleName: String
lazy val getPackage: String
}
object ClassRef {
def fromFilepath(path: String): ClassRef
def apply(_package: String, className: String): ClassRef
}trait CoverageFilter {
def isClassIncluded(className: String): Boolean
def isFileIncluded(file: SourceFile): Boolean
def isLineIncluded(position: Position): Boolean
def isSymbolIncluded(symbolName: String): Boolean
def getExcludedLineNumbers(sourceFile: SourceFile): List[Range]
}
class RegexCoverageFilter(
excludedPackages: Seq[String],
excludedFiles: Seq[String],
excludedSymbols: Seq[String],
reporter: Reporter
) extends CoverageFilter