Skip to content

Add excludedSymbols with reasonable defaults #178

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

Merged
merged 1 commit into from Sep 25, 2016
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ trait CoverageFilter {
def isClassIncluded(className: String): Boolean
def isFileIncluded(file: SourceFile): Boolean
def isLineIncluded(position: Position): Boolean
def isSymbolIncluded(symbolName: String): Boolean
def getExcludedLineNumbers(sourceFile: SourceFile): List[Range]
}

Expand All @@ -20,13 +21,16 @@ object AllCoverageFilter extends CoverageFilter {
override def isLineIncluded(position: Position): Boolean = true
override def isClassIncluded(className: String): Boolean = true
override def isFileIncluded(file: SourceFile): Boolean = true
override def isSymbolIncluded(symbolName: String): Boolean = true
}

class RegexCoverageFilter(excludedPackages: Seq[String],
excludedFiles: Seq[String]) extends CoverageFilter {
excludedFiles: Seq[String],
excludedSymbols: Seq[String]) extends CoverageFilter {

val excludedClassNamePatterns = excludedPackages.map(_.r.pattern)
val excludedFilePatterns = excludedFiles.map(_.r.pattern)
val excludedSymbolPatterns = excludedSymbols.map(_.r.pattern)

/**
* We cache the excluded ranges to avoid scanning the source code files
Expand Down Expand Up @@ -64,6 +68,10 @@ class RegexCoverageFilter(excludedPackages: Seq[String],
}
}

override def isSymbolIncluded(symbolName: String): Boolean = {
excludedSymbolPatterns.isEmpty || !excludedSymbolPatterns.exists(_.matcher(symbolName).matches)
}

/**
* Checks the given sourceFile for any magic comments which exclude lines
* from coverage. Returns a list of Ranges of lines that should be excluded.
Expand Down
9 changes: 7 additions & 2 deletions scalac-scoverage-plugin/src/main/scala/scoverage/plugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class ScoveragePlugin(val global: Global) extends Plugin {
options.excludedPackages = opt.substring("excludedPackages:".length).split(";").map(_.trim).filterNot(_.isEmpty)
} else if (opt.startsWith("excludedFiles:")) {
options.excludedFiles = opt.substring("excludedFiles:".length).split(";").map(_.trim).filterNot(_.isEmpty)
} else if (opt.startsWith("excludedSymbols:")) {
options.excludedSymbols = opt.substring("excludedSymbols:".length).split(";").map(_.trim).filterNot(_.isEmpty)
} else if (opt.startsWith("dataDir:")) {
options.dataDir = opt.substring("dataDir:".length)
} else if (opt.startsWith("extraAfterPhase:") || opt.startsWith("extraBeforePhase:")){
Expand All @@ -42,6 +44,7 @@ class ScoveragePlugin(val global: Global) extends Plugin {
"-P:scoverage:dataDir:<pathtodatadir> where the coverage files should be written\n",
"-P:scoverage:excludedPackages:<regex>;<regex> semicolon separated list of regexs for packages to exclude",
"-P:scoverage:excludedFiles:<regex>;<regex> semicolon separated list of regexs for paths to exclude",
"-P:scoverage:excludedSymbols:<regex>;<regex> semicolon separated list of regexs for symbols to exclude",
"-P:scoverage:extraAfterPhase:<phaseName> phase after which scoverage phase runs (must be after typer phase)",
"-P:scoverage:extraBeforePhase:<phaseName> phase before which scoverage phase runs (must be before patmat phase)",
" Any classes whose fully qualified name matches the regex will",
Expand Down Expand Up @@ -73,6 +76,7 @@ class ScoveragePlugin(val global: Global) extends Plugin {
class ScoverageOptions {
var excludedPackages: Seq[String] = Nil
var excludedFiles: Seq[String] = Nil
var excludedSymbols: Seq[String] = Seq("scala.reflect.api.Exprs.Expr", "scala.reflect.api.Trees.Tree", "scala.reflect.macros.Universe.Tree")
var dataDir: String = IOUtils.getTempPath
}

Expand Down Expand Up @@ -101,7 +105,7 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt

def setOptions(options: ScoverageOptions): Unit = {
this.options = options
coverageFilter = new RegexCoverageFilter(options.excludedPackages, options.excludedFiles)
coverageFilter = new RegexCoverageFilter(options.excludedPackages, options.excludedFiles, options.excludedSymbols)
new File(options.dataDir).mkdirs() // ensure data directory is created
}

Expand Down Expand Up @@ -221,6 +225,7 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
def isClassIncluded(symbol: Symbol): Boolean = coverageFilter.isClassIncluded(symbol.fullNameString)
def isFileIncluded(source: SourceFile): Boolean = coverageFilter.isFileIncluded(source)
def isStatementIncluded(pos: Position): Boolean = coverageFilter.isLineIncluded(pos)
def isSymbolIncluded(symbol: Symbol): Boolean = coverageFilter.isSymbolIncluded(symbol.fullNameString)

def updateLocation(t: Tree) {
Location(global)(t) match {
Expand Down Expand Up @@ -371,7 +376,7 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt

// will catch macro implementations, as they must end with Expr, however will catch
// any method that ends in Expr. // todo add way of allowing methods that return Expr
case d: DefDef if d.symbol != null && d.tpt.symbol.fullNameString == "scala.reflect.api.Exprs.Expr" =>
case d: DefDef if d.symbol != null && !isSymbolIncluded(d.tpt.symbol) =>
tree

// we can ignore primary constructors because they are just empty at this stage, the body is added later.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,89 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
"isClassIncluded" - {

"should return true for empty excludes" in {
assert(new RegexCoverageFilter(Nil, Nil).isClassIncluded("x"))
assert(new RegexCoverageFilter(Nil, Nil, Nil).isClassIncluded("x"))
}

"should not crash for empty input" in {
assert(new RegexCoverageFilter(Nil, Nil).isClassIncluded(""))
assert(new RegexCoverageFilter(Nil, Nil, Nil).isClassIncluded(""))
}

"should exclude scoverage -> scoverage" in {
assert(!new RegexCoverageFilter(Seq("scoverage"), Nil).isClassIncluded("scoverage"))
assert(!new RegexCoverageFilter(Seq("scoverage"), Nil, Nil).isClassIncluded("scoverage"))
}

"should include scoverage -> scoverageeee" in {
assert(new RegexCoverageFilter(Seq("scoverage"), Nil).isClassIncluded("scoverageeee"))
assert(new RegexCoverageFilter(Seq("scoverage"), Nil, Nil).isClassIncluded("scoverageeee"))
}

"should exclude scoverage* -> scoverageeee" in {
assert(!new RegexCoverageFilter(Seq("scoverage*"), Nil).isClassIncluded("scoverageeee"))
assert(!new RegexCoverageFilter(Seq("scoverage*"), Nil, Nil).isClassIncluded("scoverageeee"))
}

"should include eee -> scoverageeee" in {
assert(new RegexCoverageFilter(Seq("eee"), Nil).isClassIncluded("scoverageeee"))
assert(new RegexCoverageFilter(Seq("eee"), Nil, Nil).isClassIncluded("scoverageeee"))
}

"should exclude .*eee -> scoverageeee" in {
assert(!new RegexCoverageFilter(Seq(".*eee"), Nil).isClassIncluded("scoverageeee"))
assert(!new RegexCoverageFilter(Seq(".*eee"), Nil, Nil).isClassIncluded("scoverageeee"))
}
}
"isFileIncluded" - {
val abstractFile = mock[AbstractFile]
Mockito.when(abstractFile.path).thenReturn("sammy.scala")
"should return true for empty excludes" in {
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
new RegexCoverageFilter(Nil, Nil).isFileIncluded(file) shouldBe true
new RegexCoverageFilter(Nil, Nil, Nil).isFileIncluded(file) shouldBe true
}
"should exclude by filename" in {
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
new RegexCoverageFilter(Nil, Seq("sammy")).isFileIncluded(file) shouldBe false
new RegexCoverageFilter(Nil, Seq("sammy"), Nil).isFileIncluded(file) shouldBe false
}
"should exclude by regex wildcard" in {
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
new RegexCoverageFilter(Nil, Seq("sam.*")).isFileIncluded(file) shouldBe false
new RegexCoverageFilter(Nil, Seq("sam.*"), Nil).isFileIncluded(file) shouldBe false
}
"should not exclude non matching regex" in {
val file = new BatchSourceFile(abstractFile, Array.emptyCharArray)
new RegexCoverageFilter(Nil, Seq("qweqeqwe")).isFileIncluded(file) shouldBe true
new RegexCoverageFilter(Nil, Seq("qweqeqwe"), Nil).isFileIncluded(file) shouldBe true
}
}
"isSymbolIncluded" - {
val options = new ScoverageOptions()
"should return true for empty excludes" in {
assert(new RegexCoverageFilter(Nil, Nil, Nil).isSymbolIncluded("x"))
}

"should not crash for empty input" in {
assert(new RegexCoverageFilter(Nil, Nil, Nil).isSymbolIncluded(""))
}

"should exclude scoverage -> scoverage" in {
assert(!new RegexCoverageFilter(Nil, Nil, Seq("scoverage")).isSymbolIncluded("scoverage"))
}

"should include scoverage -> scoverageeee" in {
assert(new RegexCoverageFilter(Nil, Nil, Seq("scoverage")).isSymbolIncluded("scoverageeee"))
}
"should exclude scoverage* -> scoverageeee" in {
assert(!new RegexCoverageFilter(Nil, Nil, Seq("scoverage*")).isSymbolIncluded("scoverageeee"))
}

"should include eee -> scoverageeee" in {
assert(new RegexCoverageFilter(Nil, Nil, Seq("eee")).isSymbolIncluded("scoverageeee"))
}

"should exclude .*eee -> scoverageeee" in {
assert(!new RegexCoverageFilter(Nil, Nil, Seq(".*eee")).isSymbolIncluded("scoverageeee"))
}
"should exclude scala.reflect.api.Exprs.Expr" in {
assert(!new RegexCoverageFilter(Nil, Nil, options.excludedSymbols).isSymbolIncluded("scala.reflect.api.Exprs.Expr"))
}
"should exclude scala.reflect.macros.Universe.Tree" in {
assert(!new RegexCoverageFilter(Nil, Nil, options.excludedSymbols).isSymbolIncluded("scala.reflect.macros.Universe.Tree"))
}
"should exclude scala.reflect.api.Trees.Tree" in {
assert(!new RegexCoverageFilter(Nil, Nil, options.excludedSymbols).isSymbolIncluded("scala.reflect.api.Trees.Tree"))
}
}
"getExcludedLineNumbers" - {
Expand All @@ -72,7 +110,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
|8
""".stripMargin

val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
numbers === List.empty
}
"should exclude lines between magic comments" in {
Expand All @@ -95,7 +133,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
|16
""".stripMargin

val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
numbers === List(Range(4, 9), Range(12, 14))
}
"should exclude all lines after an upaired magic comment" in {
Expand All @@ -117,7 +155,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
|15
""".stripMargin

val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
numbers === List(Range(4, 9), Range(12, 16))
}
"should allow text comments on the same line as the markers" in {
Expand All @@ -139,7 +177,7 @@ class RegexCoverageFilterTest extends FreeSpec with Matchers with MockitoSugar {
|15
""".stripMargin

val numbers = new RegexCoverageFilter(Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
val numbers = new RegexCoverageFilter(Nil, Nil, Nil).getExcludedLineNumbers(mockSourceFile(file))
numbers === List(Range(4, 9), Range(12, 16))
}
}
Expand Down