Comprehensive code coverage tool for Scala providing statement and branch coverage through compiler plugin instrumentation and report generation
—
The CoberturaXmlWriter generates Cobertura-compatible XML reports for integration with build systems, CI/CD pipelines, and coverage analysis tools that expect the standard Cobertura format. This enables seamless integration with tools like Jenkins, SonarQube, and other coverage analysis platforms.
class CoberturaXmlWriter(
sourceDirectories: Seq[File],
outputDir: File,
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 the cobertura.xml file will be generatedsourceEncoding: Optional character encoding for reading source filesMethods:
write(coverage: Coverage): Unit - Generates cobertura.xml file// Single base directory constructor
class CoberturaXmlWriter(
baseDir: File,
outputDir: File,
sourceEncoding: Option[String]
)import java.io.File
import scoverage.reporter.CoberturaXmlWriter
import scoverage.domain.Coverage
val sourceDirectories = Seq(new File("src/main/scala"))
val outputDir = new File("target/scoverage-report")
val writer = new CoberturaXmlWriter(sourceDirectories, outputDir, Some("UTF-8"))
val coverage: Coverage = loadCoverageData()
writer.write(coverage)
// Generates target/scoverage-report/cobertura.xmlimport java.io.File
import scoverage.reporter.CoberturaXmlWriter
// Generate Cobertura report for Jenkins/SonarQube integration
val sourceDirectories = Seq(
new File("module1/src/main/scala"),
new File("module2/src/main/scala")
)
val outputDir = new File(sys.env.getOrElse("BUILD_DIR", "target") + "/coverage-reports")
val writer = new CoberturaXmlWriter(sourceDirectories, outputDir, Some("UTF-8"))
val coverage: Coverage = aggregateMultiModuleCoverage()
writer.write(coverage)
// The cobertura.xml can now be consumed by CI tools
println(s"Cobertura report generated at: ${outputDir}/cobertura.xml")// Aggregate coverage from multiple projects
val allSourceDirs = Seq(
new File("project-a/src/main/scala"),
new File("project-b/src/main/scala"),
new File("shared/src/main/scala")
)
val coberturaWriter = new CoberturaXmlWriter(
allSourceDirs,
new File("target/aggregated-coverage"),
Some("UTF-8")
)
val aggregatedCoverage = CoverageAggregator.aggregatedCoverage(dataDirs, sourceRoot)
coberturaWriter.write(aggregatedCoverage)The Cobertura XML format follows the standard Cobertura DTD structure:
<?xml version="1.0"?>
<!DOCTYPE coverage SYSTEM "https://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage line-rate="0.85"
lines-valid="1000"
lines-covered="850"
branches-valid="200"
branches-covered="160"
branch-rate="0.80"
complexity="0"
version="1.0"
timestamp="1609459200000">
<sources>
<source>--source</source>
<source>/path/to/src/main/scala</source>
</sources>
<packages>
<package name="com.example.myapp"
line-rate="0.90"
branch-rate="0.85"
complexity="0">
<classes>
<class name="com.example.myapp.MyClass"
filename="MyClass.scala"
line-rate="0.92"
branch-rate="0.88"
complexity="0">
<methods>
<method name="myMethod"
signature="()V"
line-rate="0.95"
branch-rate="0.90"
complexity="0">
<lines>
<line number="10" hits="5" branch="false"/>
<line number="15" hits="3" branch="true"/>
</lines>
</method>
</methods>
<lines>
<line number="10" hits="5" branch="false"/>
<line number="15" hits="3" branch="true"/>
<line number="20" hits="0" branch="false"/>
</lines>
</class>
</classes>
</package>
</packages>
</coverage>line-rate: Overall line coverage as decimal (0.0 to 1.0)lines-valid: Total number of executable lineslines-covered: Number of lines that were executedbranches-valid: Total number of branch pointsbranches-covered: Number of branches that were executedbranch-rate: Overall branch coverage as decimal (0.0 to 1.0)complexity: Cyclomatic complexity (always 0 in scoverage)version: Report format versiontimestamp: Generation time in millisecondsLists all source directories included in the coverage analysis:
--source: Marker indicating source directories follow<source> elements for each source directory pathname: Package name (fully qualified)line-rate: Package-level line coverage rate (0.0 to 1.0)branch-rate: Package-level branch coverage rate (0.0 to 1.0)complexity: Always 0 in scoverage reportsname: Fully qualified class namefilename: Relative path to source fileline-rate: Class-level line coverage rate (0.0 to 1.0)branch-rate: Class-level branch coverage rate (0.0 to 1.0)complexity: Always 0 in scoverage reportsname: Method namesignature: Method signature (simplified to "()V" in scoverage)line-rate: Method-level line coverage rate (0.0 to 1.0)branch-rate: Method-level branch coverage rate (0.0 to 1.0)complexity: Always 0 in scoverage reportsnumber: Line number in source filehits: Number of times line was executed (0 means not covered)branch: Whether line contains branch logic (true/false)The report is always generated as cobertura.xml in the specified output directory.
The XML file includes:
// Generate report for Jenkins Cobertura plugin
val writer = new CoberturaXmlWriter(sourceDirectories, new File("target/site/cobertura"), None)
writer.write(coverage)
// Configure Jenkins to read target/site/cobertura/cobertura.xml// Generate for SonarQube analysis
val sonarReportDir = new File("target/sonar-reports")
sonarReportDir.mkdirs()
val writer = new CoberturaXmlWriter(sourceDirectories, sonarReportDir, Some("UTF-8"))
writer.write(coverage)
// Configure sonar.scala.coverage.reportPaths=target/sonar-reports/cobertura.xml// Generate Cobertura report in Gradle build
val sourceArray: Array[File] = sourceDirectories.toArray
val writer = new CoberturaXmlWriter(sourceArray, outputDir, encoding)
writer.write(coverage)The Cobertura format differs from the native scoverage XML format:
Common Issues:
IOException: File writing permissions or disk space problemsRuntimeException: Source directory path resolution failuresIllegalStateException: Invalid coverage data structureBest Practices:
Install with Tessl CLI
npx tessl i tessl/maven-org-scoverage--scalac-scoverage-plugin