Comprehensive code coverage tool for Scala providing statement and branch coverage through compiler plugin instrumentation and report generation
—
The ScoverageXmlWriter generates structured XML coverage reports in the scoverage format for programmatic consumption, CI/CD integration, and archival purposes. The XML format provides complete coverage data including statement details, method information, and hierarchical package/class organization.
class ScoverageXmlWriter(
sourceDirectories: Seq[File],
outputDir: File,
debug: Boolean,
sourceEncoding: Option[String]
) extends BaseReportWriter(sourceDirectories, outputDir, sourceEncoding) {
def write(coverage: Coverage): Unit
}Constructor Parameters:
sourceDirectories: Sequence of source directories for relative path calculationoutputDir: Directory where XML report will be generateddebug: Whether to include debug information in the XML outputsourceEncoding: Optional character encoding for reading source filesMethods:
write(coverage: Coverage): Unit - Generates XML report file// Single source directory constructor
class ScoverageXmlWriter(
sourceDir: File,
outputDir: File,
debug: Boolean,
sourceEncoding: Option[String]
)import java.io.File
import scoverage.reporter.ScoverageXmlWriter
import scoverage.domain.Coverage
val sourceDirectories = Seq(new File("src/main/scala"))
val outputDir = new File("target/scoverage-report")
val debug = false
val writer = new ScoverageXmlWriter(sourceDirectories, outputDir, debug, Some("UTF-8"))
val coverage: Coverage = loadCoverageData()
writer.write(coverage)import java.io.File
import scoverage.reporter.ScoverageXmlWriter
// Debug mode includes additional statement details like symbol names and tree names
val writer = new ScoverageXmlWriter(
sourceDirectories = Seq(new File("src/main/scala")),
outputDir = new File("target/scoverage-report"),
debug = true,
sourceEncoding = Some("UTF-8")
)
val coverage: Coverage = loadCoverageData()
writer.write(coverage)// Generate XML report for automated processing
val xmlWriter = new ScoverageXmlWriter(
sourceDirectories = projectSourceDirs,
outputDir = buildOutputDir,
debug = false, // Minimal output for CI processing
sourceEncoding = Some("UTF-8")
)
xmlWriter.write(coverage)
// The generated scoverage.xml can then be parsed by CI tools
val reportFile = new File(buildOutputDir, "scoverage.xml")
// Process with CI/coverage tools...The XML report includes the following hierarchical structure:
<scoverage statement-count="1250"
statements-invoked="1100"
statement-rate="88.00"
branch-rate="82.50"
version="1.0"
timestamp="1609459200000">
<packages>
<package name="com.example.myapp"
statement-count="500"
statements-invoked="450"
statement-rate="90.00">
<classes>
<class name="com.example.myapp.MyClass"
filename="MyClass.scala"
statement-count="50"
statements-invoked="45"
statement-rate="90.00"
branch-rate="85.00">
<methods>
<method name="myMethod"
statement-count="10"
statements-invoked="9"
statement-rate="90.00"
branch-rate="80.00">
<statements>
<statement package="com.example.myapp"
class="MyClass"
class-type="Class"
full-class-name="com.example.myapp.MyClass"
source="MyClass.scala"
method="myMethod"
start="150"
end="175"
line="10"
branch="false"
invocation-count="5"
ignored="false"/>
</statements>
</method>
</methods>
</class>
</classes>
</package>
</packages>
</scoverage>When debug = true, statements include additional debugging information:
<statement package="com.example.myapp"
class="MyClass"
class-type="Class"
full-class-name="com.example.myapp.MyClass"
source="MyClass.scala"
method="myMethod"
start="150"
end="175"
line="10"
symbol="scala.Predef.println"
tree="Apply"
branch="false"
invocation-count="5"
ignored="false">
println("Hello World")
</statement>scoverage.xmlscoverage-debug.xmlReports are written to the specified outputDir directory.
statement-count: Total number of statements in the codebasestatements-invoked: Number of statements that were executedstatement-rate: Statement coverage percentage (formatted to 2 decimal places)branch-rate: Branch coverage percentage (formatted to 2 decimal places)version: Scoverage XML format versiontimestamp: Report generation timestamp (milliseconds since epoch)name: Package name (fully qualified)statement-count: Statements in this packagestatements-invoked: Invoked statements in this packagestatement-rate: Package-level statement coverage percentagename: Fully qualified class namefilename: Relative path to source filestatement-count: Statements in this classstatements-invoked: Invoked statements in this classstatement-rate: Class-level statement coverage percentagebranch-rate: Class-level branch coverage percentagename: Method name with parameter signaturestatement-count: Statements in this methodstatements-invoked: Invoked statements in this methodstatement-rate: Method-level statement coverage percentagebranch-rate: Method-level branch coverage percentagepackage: Package containing the statementclass: Simple class nameclass-type: Type of class (Class, Object, or Trait)full-class-name: Fully qualified class namesource: Relative path to source filemethod: Method containing the statementstart: Character offset start positionend: Character offset end positionline: Line number in source filebranch: Whether this statement represents a branch pointinvocation-count: Number of times statement was executedignored: Whether statement was excluded from coveragesymbol: Scala symbol name (e.g., "scala.Predef.println")tree: Scala compiler tree type (e.g., "Apply", "Select")// Generate XML report after coverage collection
val coverage = loadCoverageData()
val xmlWriter = new ScoverageXmlWriter(
sourceDirectories = (Compile / scalaSource).value :: Nil,
outputDir = crossTarget.value,
debug = false,
sourceEncoding = Some("UTF-8")
)
xmlWriter.write(coverage)// Using Array constructor for Gradle compatibility
val sourceArray: Array[File] = sourceDirectories.toArray
val xmlWriter = new ScoverageXmlWriter(sourceArray, outputDir, debug, encoding)
xmlWriter.write(coverage)Common Issues:
IOException: File writing permissions or disk space issuesRuntimeException: Invalid source directory paths or canonical path resolution failuresIllegalArgumentException: Invalid coverage data or missing statement informationBest Practices:
Install with Tessl CLI
npx tessl i tessl/maven-org-scoverage--scalac-scoverage-plugin