Skip to content

Support multiple source roots in report generators (issue #104) #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions scalac-scoverage-plugin/src/main/scala/scoverage/IOUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,22 @@ object IOUtils {
}
acc
}

/**
* Converts absolute path to relative one if any of the source directories is it's parent.
* If there is no parent directory, the path is returned unchanged (absolute).
*
* @param src absolute file path in canonical form
* @param sourcePaths absolute source paths in canonical form WITH trailing file separators
*/
def relativeSource(src: String, sourcePaths: Seq[String]): String = {
val sourceRoot: Option[String] = sourcePaths.find(
sourcePath => src.startsWith(sourcePath)
)
sourceRoot match {
case Some(path: String) => src.replace(path, "")
case _ => throw new RuntimeException(s"No source root found for '$src'"); //TODO Change exception class
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to decide what exception will be the best :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe IllegalArgumentException? Method contract states src must have at least one source root in given sourcePaths.

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ import java.io.File

import scoverage._

import scala.xml.Node
import scala.xml.{Node, PrettyPrinter}

/** @author Stephen Samuel */
class CoberturaXmlWriter(baseDir: File, outputDir: File) {
class CoberturaXmlWriter(sourceDirectories: Seq[File], outputDir: File) {

def this (baseDir: File, outputDir: File) {
this(Seq(baseDir), outputDir);
}

// Source paths in canonical form WITH trailing file separator
val formattedSourcePaths: Seq[String] = sourceDirectories filter ( _.isDirectory ) map ( _.getCanonicalPath + File.separator )

def format(double: Double): String = "%.2f".format(double)

def write(coverage: Coverage): Unit = {
IOUtils.writeToFile(new File(outputDir.getAbsolutePath + "/cobertura.xml"),
"<?xml version=\"1.0\"?>\n<!DOCTYPE coverage SYSTEM \"http://cobertura.sourceforge.net/xml/coverage-04.dtd\">\n" +
xml(coverage))
val file = new File(outputDir, "cobertura.xml")
IOUtils.writeToFile(file, "<?xml version=\"1.0\"?>\n<!DOCTYPE coverage SYSTEM \"http://cobertura.sourceforge.net/xml/coverage-04.dtd\">\n" +
new PrettyPrinter(120, 4).format(xml(coverage)))
}

def method(method: MeasuredMethod): Node = {
Expand All @@ -35,12 +42,7 @@ class CoberturaXmlWriter(baseDir: File, outputDir: File) {

def klass(klass: MeasuredClass): Node = {
<class name={klass.name}
filename={
val absPath = baseDir.getAbsolutePath.last == File.separatorChar match {
case true => baseDir.getAbsolutePath
case false => baseDir.getAbsolutePath + File.separatorChar
}
klass.source.replace(absPath, "")}
filename={relativeSource(klass.source).replace(File.separator, "/")}
line-rate={format(klass.statementCoverage)}
branch-rate={format(klass.branchCoverage)}
complexity="0">
Expand Down Expand Up @@ -69,6 +71,10 @@ class CoberturaXmlWriter(baseDir: File, outputDir: File) {
</package>
}

def source(src: File): Node = {
<source>{src.getCanonicalPath.replace(File.separator, "/")}</source>
}

def xml(coverage: Coverage): Node = {
<coverage line-rate={format(coverage.statementCoverage)}
lines-covered={coverage.statementCount.toString}
Expand All @@ -80,11 +86,15 @@ class CoberturaXmlWriter(baseDir: File, outputDir: File) {
version="1.0"
timestamp={System.currentTimeMillis.toString}>
<sources>
<source>/src/main/scala</source>
<source>--source</source>
{sourceDirectories.filter(_.isDirectory()).map(source)}
</sources>
<packages>
{coverage.packages.map(pack)}
</packages>
</coverage>
}

private def relativeSource(src: String): String = IOUtils.relativeSource(src, formattedSourcePaths)

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ object CoverageAggregator {

def aggregate(baseDir: File, clean: Boolean): Option[Coverage] = {
val files = IOUtils.reportFileSearch(baseDir, IOUtils.isReportFile)
aggregate(files, clean)
}

def aggregate(files: Seq[File], clean: Boolean): Option[Coverage] = {
println(s"[info] Found ${files.size} subproject report files [${files.mkString(",")}]")
if (files.size > 1) {
val coverage = aggregatedCoverage(files)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ import scoverage._
import scala.xml.Node

/** @author Stephen Samuel */
class ScoverageHtmlWriter(sourceDirectory: File, outputDir: File) {
class ScoverageHtmlWriter(sourceDirectories: Seq[File], outputDir: File) {

def this (sourceDirectory: File, outputDir: File) {
this(Seq(sourceDirectory), outputDir);
}

// Source paths in canonical form WITH trailing file separator
val formattedSourcePaths: Seq[String] = sourceDirectories filter ( _.isDirectory ) map ( _.getCanonicalPath + File.separator )

def write(coverage: Coverage): Unit = {
val indexFile = new File(outputDir.getAbsolutePath + "/index.html")
val packageFile = new File(outputDir.getAbsolutePath + "/packages.html")
Expand All @@ -23,8 +30,6 @@ class ScoverageHtmlWriter(sourceDirectory: File, outputDir: File) {
coverage.packages.foreach(writePackage)
}

private def relativeSource(src: String): String = src.replace(sourceDirectory.getCanonicalPath + File.separator, "")

private def writePackage(pkg: MeasuredPackage): Unit = {
// package overview files are written out using a filename that respects the package name
// that means package com.example declared in a class at src/main/scala/mystuff/MyClass.scala will be written
Expand Down Expand Up @@ -535,5 +540,8 @@ class ScoverageHtmlWriter(sourceDirectory: File, outputDir: File) {
</tr>
</table>
}

private def relativeSource(src: String): String = IOUtils.relativeSource(src, formattedSourcePaths)

}

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ import _root_.scoverage._
import scala.xml.{Node, PrettyPrinter}

/** @author Stephen Samuel */
class ScoverageXmlWriter(sourceDir: File, outputDir: File, debug: Boolean) {
class ScoverageXmlWriter(sourceDirectories: Seq[File], outputDir: File, debug: Boolean) {

def this (sourceDir: File, outputDir: File, debug: Boolean) {
this(Seq(sourceDir), outputDir, debug);
}

// Source paths in canonical form WITH trailing file separator
val formattedSourcePaths: Seq[String] = sourceDirectories filter ( _.isDirectory ) map ( _.getCanonicalPath + File.separator )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is duplicated in 3 different writers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make as small changes as possible required. This can be refactored of course.


def write(coverage: Coverage): Unit = {
val file = IOUtils.reportFile(outputDir, debug)
IOUtils.writeToFile(file, new PrettyPrinter(120, 4).format(xml(coverage)))
Expand Down Expand Up @@ -74,7 +81,7 @@ class ScoverageXmlWriter(sourceDir: File, outputDir: File, debug: Boolean) {

private def klass(klass: MeasuredClass): Node = {
<class name={klass.name}
filename={klass.source.replace(sourceDir.getAbsolutePath, "")}
filename={relativeSource(klass.source)}
statement-count={klass.statementCount.toString}
statements-invoked={klass.invokedStatementCount.toString}
statement-rate={klass.statementCoverageFormatted}
Expand All @@ -96,5 +103,7 @@ class ScoverageXmlWriter(sourceDir: File, outputDir: File, debug: Boolean) {
</package>
}

private def relativeSource(src: String): String = IOUtils.relativeSource(src, formattedSourcePaths)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if formattedSourcePaths is used only here, maybe move it's implementation to IOUtils.relativeSource to avoid this duplication?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or use something like IOUtils.relativeSource(src, IOUtils.formatPaths(sourceDirectories))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be refactored.


}