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

Add coverageExclude task #445

Closed
Closed
Show file tree
Hide file tree
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
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ false`.
By default, scoverage will generate reports for each project separately. You can
merge them into an aggregated report by using the following:

```
```bash
$ sbt coverageAggregate
```

Expand Down Expand Up @@ -83,9 +83,6 @@ coverageExcludedFiles := ".*\\/two\\/GoodCoverage;.*\\/three\\/.*"

Note: The `.scala` file extension needs to be omitted from the filename, if one is given.

Note: These two options only work for Scala2. Right now Scala3 does not support
a way to exclude packages or files from being instrumented.

You can also mark sections of code with comments like:

```scala
Expand All @@ -97,6 +94,16 @@ You can also mark sections of code with comments like:
Any code between two such comments will not be instrumented or included in the
coverage report.

Note: This only works for scala2. To make this work for scala3 you have to use
the `coverageExclude` task.

```bash
$ sbt clean coverage test coverageExclude coverageReport
```

Note: This will take `excludedPackages` and `excludedFiles` into consideration,
but not lines of code that are excluded with `COVERAGE-OFF/-ON`.

### Minimum coverage

Based on minimum coverage, you can fail the build with the following keys:
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/scoverage/ScoverageKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ object ScoverageKeys {
"controls whether code instrumentation is enabled or not"
)
lazy val coverageReport = taskKey[Unit]("run report generation")
lazy val coverageExclude = taskKey[Unit]("exclude/remove files and packages from coverage file (after the fact)")
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 is the task that we add.

lazy val coverageAggregate = taskKey[Unit]("aggregate reports from subprojects")
lazy val coverageExcludedPackages = settingKey[String]("regex for excluded packages")
lazy val coverageExcludedFiles = settingKey[String]("regex for excluded file paths")
Expand Down
62 changes: 51 additions & 11 deletions src/main/scala/scoverage/ScoverageSbtPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import scoverage.reporter.ScoverageXmlWriter
import scoverage.serialize.Serializer

import java.time.Instant
import scala.tools.nsc

object ScoverageSbtPlugin extends AutoPlugin {

Expand Down Expand Up @@ -66,6 +67,7 @@ object ScoverageSbtPlugin extends AutoPlugin {
override def projectSettings: Seq[Setting[_]] = Seq(
ivyConfigurations += ScoveragePluginConfig,
coverageReport := coverageReport0.value,
coverageExclude := coverageExclude0.value,
coverageAggregate := coverageAggregate0.value,
coverageAggregate / aggregate := false,
coverageDataDir := crossTarget.value
Expand Down Expand Up @@ -192,11 +194,45 @@ object ScoverageSbtPlugin extends AutoPlugin {
sjsClassifier getOrElse ""
}

private lazy val coverageExclude0 = Def.task {
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 is the implementation of the task we add.

implicit val log = streams.value.log

val dataDir = coverageDataDir.value / "scoverage-data"
val coverageFile = Serializer.coverageFile(dataDir)

if (!coverageFile.exists) {
log.warn(s"No coverage data found. [$coverageFile] does not exits. Run >sbt coverage test< first.")
} else {
log.info(s"Reading scoverage instrumentation [$coverageFile] ...")
val sourceRoot = coverageSourceRoot.value.getAbsoluteFile()
val coverage = Serializer.deserialize(coverageFile, sourceRoot)

val filter = new RegexCoverageFilter(
coverageExcludedPackages.value.split(";"),
coverageExcludedFiles.value.split(";"),
ScoverageOptions.default().excludedSymbols,
new nsc.reporters.ConsoleReporter(new nsc.Settings())
)

val statements = coverage.statements
log.info(s"Filtering [${statements.size}] statements ...")
val statementsToBeExcluded = statements.filter(
s => !filter.isClassIncluded(s.location.fullClassName) || !filter.isFileIncluded(s.location.sourcePath) || !filter.isSymbolIncluded(s.symbolName)
)

log.info(s"Statements to be excluded [${statementsToBeExcluded.size}] ...")
statementsToBeExcluded.foreach(s => coverage.remove(s.id))

log.info(s"(Re)Writing scoverage instrumentation [$coverageFile] ...")
Serializer.serialize(coverage, coverageFile, sourceRoot)
}
}

private lazy val coverageReport0 = Def.task {
val target = coverageDataDir.value
implicit val log = streams.value.log

log.info(s"Waiting for measurement data to sync...")
log.info(s"Waiting for measurement data to sync ...")
Thread.sleep(
1000
) // have noticed some delay in writing on windows, hacky but works
Expand All @@ -222,14 +258,17 @@ object ScoverageSbtPlugin extends AutoPlugin {

CoverageMinimum.all.value
.checkCoverage(cov, coverageFailOnMinimum.value)
case None => log.warn("No coverage data, skipping reports")

case None =>
log.warn(s"No coverage data found in [$target]. Run >sbt coverage test< first.")
Copy link
Member Author

Choose a reason for hiding this comment

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

Making the warning more expressive.

}
}

private lazy val coverageAggregate0 = Def.task {
val target = coverageDataDir.value
implicit val log = streams.value.log
log.info(s"Aggregating coverage from subprojects...")

log.info(s"Aggregating coverage from subprojects ...")
val dataDirs = coverageDataDir.?.all(aggregateFilter).value
.collect {
case Some(file) if (file / Constants.DataDir).isDirectory =>
Expand All @@ -239,7 +278,7 @@ object ScoverageSbtPlugin extends AutoPlugin {
CoverageAggregator.aggregate(dataDirs, coverageSourceRoot.value) match {
case Some(cov) =>
writeReports(
coverageDataDir.value,
target,
sourceDirectories.all(aggregateFilter).value.flatten,
cov,
coverageOutputCobertura.value,
Expand All @@ -255,8 +294,9 @@ object ScoverageSbtPlugin extends AutoPlugin {

CoverageMinimum.all.value
.checkCoverage(cov, coverageFailOnMinimum.value)

case None =>
log.info("No subproject data to aggregate, skipping reports")
log.warn(s"No coverage data found to aggregate in [$target]. Run >sbt coverage test< first.")
Copy link
Member Author

Choose a reason for hiding this comment

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

Turning it into a warning (because the other log messages are warnings) and making it more expressive

}
}

Expand All @@ -272,7 +312,7 @@ object ScoverageSbtPlugin extends AutoPlugin {
coverageSourceEncoding: Option[String],
log: Logger
): Unit = {
log.info(s"Generating scoverage reports...")
log.info(s"Generating scoverage reports ...")

val coberturaDir = crossTarget / "coverage-report"
val reportDir = crossTarget / "scoverage-report"
Expand Down Expand Up @@ -337,8 +377,8 @@ object ScoverageSbtPlugin extends AutoPlugin {
log.info("Written coverage report to TeamCity")
}

log.info(s"Statement coverage.: ${coverage.statementCoverageFormatted}%")
log.info(s"Branch coverage....: ${coverage.branchCoverageFormatted}%")
log.info(s"Statement coverage: ${coverage.statementCoverageFormatted}%")
log.info(s"Branch coverage: ${coverage.branchCoverageFormatted}%")
log.info("Coverage reports completed")
}

Expand Down Expand Up @@ -386,10 +426,10 @@ object ScoverageSbtPlugin extends AutoPlugin {
sourceRoot: File
): Option[Coverage] = {

val dataDir = crossTarget / "/scoverage-data"
val dataDir = crossTarget / "scoverage-data"
Copy link
Member Author

Choose a reason for hiding this comment

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

Fixing minor broken window.

val coverageFile = Serializer.coverageFile(dataDir)

log.info(s"Reading scoverage instrumentation [$coverageFile]")
log.info(s"Reading scoverage instrumentation [$coverageFile] ...")

if (coverageFile.exists) {

Expand All @@ -398,7 +438,7 @@ object ScoverageSbtPlugin extends AutoPlugin {
sourceRoot
)

log.info(s"Reading scoverage measurements...")
log.info(s"Reading scoverage measurements ...")
val measurementFiles = IOUtils.findMeasurementFiles(dataDir)
val measurements = IOUtils.invoked(measurementFiles)
coverage.apply(measurements)
Expand Down
5 changes: 2 additions & 3 deletions src/sbt-test/scoverage/scala3-coverage-excluded-files/test
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
> clean
> coverage
> test
> coverageExclude
> coverageReport
# There should be no directory for the excluded files
#-$ exists target/scala-3.2.0-RC1/scoverage-report/two
# But right now there is, because Scala3 does not support excluding files
$ exists target/scala-3.2.0-RC1/scoverage-report/two
-$ exists target/scala-3.2.0-RC1/scoverage-report/two