Skip to content
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

feature: Implemented 'exclude' filter functionality for Scala 3 #533

Closed
wants to merge 2 commits into from
Closed
Changes from all 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
98 changes: 93 additions & 5 deletions src/main/scala/scoverage/ScoverageSbtPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import sbt.plugins.JvmPlugin
import scoverage.reporter.CoberturaXmlWriter
import scoverage.domain.Constants
import scoverage.domain.Coverage
import scoverage.domain.Statement
import scoverage.reporter.CoverageAggregator
import scoverage.reporter.IOUtils
import scoverage.reporter.ScoverageHtmlWriter
import scoverage.reporter.ScoverageXmlWriter
import scoverage.serialize.Serializer

import java.time.Instant
import scala.util.Try
import scala.util.matching.Regex

object ScoverageSbtPlugin extends AutoPlugin {

Expand Down Expand Up @@ -225,10 +228,19 @@ object ScoverageSbtPlugin extends AutoPlugin {
coverageSourceRoot.value.getAbsoluteFile()
) match {
case Some(cov) =>
val coverage = if (isScala3SupportingScoverage(scalaVersion.value)) {
filterCoverage(
cov,
coverageExcludedFiles.value,
coverageExcludedPackages.value,
log
)
} else cov

writeReports(
target,
(Compile / sourceDirectories).value,
cov,
coverage,
coverageOutputCobertura.value,
coverageOutputXML.value,
coverageOutputHTML.value,
Expand All @@ -239,7 +251,7 @@ object ScoverageSbtPlugin extends AutoPlugin {
)

CoverageMinimum.all.value
.checkCoverage(cov, coverageFailOnMinimum.value)
.checkCoverage(coverage, coverageFailOnMinimum.value)
case None => log.warn("No coverage data, skipping reports")
}
}
Expand All @@ -256,10 +268,19 @@ object ScoverageSbtPlugin extends AutoPlugin {

CoverageAggregator.aggregate(dataDirs, coverageSourceRoot.value) match {
case Some(cov) =>
val coverage = if (isScala3SupportingScoverage(scalaVersion.value)) {
filterCoverage(
cov,
coverageExcludedFiles.value,
coverageExcludedPackages.value,
log
)
} else cov

writeReports(
coverageDataDir.value,
sourceDirectories.all(aggregateFilter).value.flatten,
cov,
coverage,
coverageOutputCobertura.value,
coverageOutputXML.value,
coverageOutputHTML.value,
Expand All @@ -268,16 +289,83 @@ object ScoverageSbtPlugin extends AutoPlugin {
sourceEncoding((Compile / scalacOptions).value),
log
)
val cfmt = cov.statementCoverageFormatted
val cfmt = coverage.statementCoverageFormatted
log.info(s"Aggregation complete. Coverage was [$cfmt]")

CoverageMinimum.all.value
.checkCoverage(cov, coverageFailOnMinimum.value)
.checkCoverage(coverage, coverageFailOnMinimum.value)
case None =>
log.info("No subproject data to aggregate, skipping reports")
}
}

private def compileRegex(regexString: String): Option[Regex] =
Try(regexString.r).toOption

private def asRegexList(strSetting: String) = strSetting
.split(';')
.map(_.trim)
.filter(_.nonEmpty)
.flatMap(compileRegex)

private def filterStatements(
statements: Iterable[Statement],
excludedFiles: Array[Regex],
excludedPackages: Array[Regex],
log: Logger
) = statements
.groupBy { statement =>
(statement.location.sourcePath, statement.location.fullClassName)
}
.filterKeys { case t @ (sourcePath, fullClassName) =>
val preserveSource = excludedFiles.isEmpty || excludedFiles.forall(
_.findFirstMatchIn(sourcePath).isEmpty
)
val preservePackage = excludedPackages.isEmpty || excludedPackages
.forall(_.findFirstMatchIn(fullClassName).isEmpty)
if (!preserveSource) log.info(s"Excluded file from report: $sourcePath")
if (!preservePackage)
log.info(s"Excluded package from report: $fullClassName")
preserveSource && preservePackage
}
.values
.flatten

private def filterCoverage(
originalCoverage: Coverage,
excludedFilesSetting: String,
excludedPackagesSetting: String,
log: Logger
): Coverage = {

val excludedFiles: Array[Regex] = asRegexList(excludedFilesSetting)
val excludedPackages: Array[Regex] = asRegexList(excludedPackagesSetting)

if (excludedFiles.isEmpty && excludedPackages.isEmpty) {
log.debug("Skipping filter")
originalCoverage
} else {
log.debug("Applying filter")
val updatedCoverage = Coverage()

filterStatements(
originalCoverage.statements,
excludedFiles,
excludedPackages,
log
).foreach(updatedCoverage.add)

filterStatements(
originalCoverage.ignoredStatements,
excludedFiles,
excludedPackages,
log
).foreach(updatedCoverage.addIgnoredStatement)

updatedCoverage
}
}

private def writeReports(
crossTarget: File,
compileSourceDirectories: Seq[File],
Expand Down