From 89171f3ec22c26f06191e4de933fb1c5fa52de90 Mon Sep 17 00:00:00 2001 From: Roland Tritsch Date: Tue, 5 Jul 2022 08:51:58 +0100 Subject: [PATCH 1/4] Adding coverageExclude task. --- src/main/scala/scoverage/ScoverageKeys.scala | 1 + .../scala/scoverage/ScoverageSbtPlugin.scala | 63 +++++++++++++++---- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/main/scala/scoverage/ScoverageKeys.scala b/src/main/scala/scoverage/ScoverageKeys.scala index 9cab50e8..438f307a 100644 --- a/src/main/scala/scoverage/ScoverageKeys.scala +++ b/src/main/scala/scoverage/ScoverageKeys.scala @@ -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)") 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") diff --git a/src/main/scala/scoverage/ScoverageSbtPlugin.scala b/src/main/scala/scoverage/ScoverageSbtPlugin.scala index 4a920c1d..25cf22af 100644 --- a/src/main/scala/scoverage/ScoverageSbtPlugin.scala +++ b/src/main/scala/scoverage/ScoverageSbtPlugin.scala @@ -13,6 +13,8 @@ import scoverage.reporter.ScoverageXmlWriter import scoverage.serialize.Serializer import java.time.Instant +import scala.tools.nsc.reporters.NoReporter +import scala.tools.nsc object ScoverageSbtPlugin extends AutoPlugin { @@ -66,6 +68,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 @@ -192,11 +195,45 @@ object ScoverageSbtPlugin extends AutoPlugin { sjsClassifier getOrElse "" } + private lazy val coverageExclude0 = Def.task { + 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 @@ -222,14 +259,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.") } } 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 => @@ -239,7 +279,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, @@ -255,8 +295,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.") } } @@ -272,7 +313,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" @@ -337,8 +378,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") } @@ -386,10 +427,10 @@ object ScoverageSbtPlugin extends AutoPlugin { sourceRoot: File ): Option[Coverage] = { - val dataDir = crossTarget / "/scoverage-data" + val dataDir = crossTarget / "scoverage-data" val coverageFile = Serializer.coverageFile(dataDir) - log.info(s"Reading scoverage instrumentation [$coverageFile]") + log.info(s"Reading scoverage instrumentation [$coverageFile] ...") if (coverageFile.exists) { @@ -398,7 +439,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) From deeceeb635b654eaa0b7607a4117aeb225c30022 Mon Sep 17 00:00:00 2001 From: Roland Tritsch Date: Tue, 5 Jul 2022 09:06:28 +0100 Subject: [PATCH 2/4] Update README (do document new task). --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7a14dad3..c688f800 100644 --- a/README.md +++ b/README.md @@ -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 ``` @@ -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 @@ -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: From bf7ac46ba43d26c926394ea579526f0ffe500a2d Mon Sep 17 00:00:00 2001 From: Roland Tritsch Date: Tue, 5 Jul 2022 09:07:03 +0100 Subject: [PATCH 3/4] Adding/Fixing test (to use new coverageExclude task). --- src/sbt-test/scoverage/scala3-coverage-excluded-files/test | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sbt-test/scoverage/scala3-coverage-excluded-files/test b/src/sbt-test/scoverage/scala3-coverage-excluded-files/test index b19476ca..bce56006 100644 --- a/src/sbt-test/scoverage/scala3-coverage-excluded-files/test +++ b/src/sbt-test/scoverage/scala3-coverage-excluded-files/test @@ -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 From f5d4004a975e90997561baa25a2a05b78f860073 Mon Sep 17 00:00:00 2001 From: Roland Tritsch Date: Tue, 5 Jul 2022 09:28:17 +0100 Subject: [PATCH 4/4] Remove unneeded import. --- src/main/scala/scoverage/ScoverageSbtPlugin.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/scoverage/ScoverageSbtPlugin.scala b/src/main/scala/scoverage/ScoverageSbtPlugin.scala index 25cf22af..8c43e58b 100644 --- a/src/main/scala/scoverage/ScoverageSbtPlugin.scala +++ b/src/main/scala/scoverage/ScoverageSbtPlugin.scala @@ -13,7 +13,6 @@ import scoverage.reporter.ScoverageXmlWriter import scoverage.serialize.Serializer import java.time.Instant -import scala.tools.nsc.reporters.NoReporter import scala.tools.nsc object ScoverageSbtPlugin extends AutoPlugin {