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

Added feature to exclude files from coverage analysis #68

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ project/sbt_project_definition.iml
lib_managed/
target/
self-report/
.idea
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ organization := "reaktor"

name := "scct"

version := "0.2-SNAPSHOT"
version := "0.3-SNAPSHOT"

scalaVersion := "2.10.0-RC3"

Expand Down
10 changes: 9 additions & 1 deletion src/main/scala/reaktor/scct/Coverage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,21 @@ object Coverage {
}

def report = {
val projectData = new ProjectData(env, dataValues)
val filtered: List[CoveredBlock] = filteredData

val projectData = new ProjectData(env, filtered)
val writer = new HtmlReportWriter(env.reportDir)
new HtmlReporter(projectData, writer).report
new CoberturaReporter(projectData, writer).report
BinaryReporter.report(projectData, env.reportDir)
}

private def filteredData: List[CoveredBlock] = {
val excludedPaths = System.getProperty("scct.excluded.paths.regex", "").split(",").filter(_.length > 0).map(_.r)
val filter = new CoverageFilter(excludedPaths)
filter.filter(dataValues)
}

private def setupShutdownHook {
Runtime.getRuntime.addShutdownHook(new Thread {
override def run = {
Expand Down
18 changes: 18 additions & 0 deletions src/main/scala/reaktor/scct/CoverageFilter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package reaktor.scct

import scala.util.matching.Regex

class CoverageFilter(excludeFiles : Array[Regex]) {

private var debug = System.getProperty("scct.debug") == "true"

private def isIncluded(block: CoveredBlock) = {
val isMatched = excludeFiles.filter( _.findFirstIn(block.name.sourceFile).isDefined).size > 0

if (debug && isMatched) println("scct : excluding " + block.name.sourceFile)

!isMatched
}

def filter(data: List[CoveredBlock]): List[CoveredBlock] = data.filter( isIncluded )
}
20 changes: 14 additions & 6 deletions src/main/scala/reaktor/scct/ScctInstrumentPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import tools.nsc.plugins.{PluginComponent, Plugin}
import java.io.File
import tools.nsc.transform.{Transform, TypingTransformers}
import tools.nsc.symtab.Flags
import tools.nsc.{Phase, Global}
import tools.nsc.Global
import util.Random
import scala.util.matching.Regex

class ScctInstrumentPlugin(val global: Global) extends Plugin {
val name = "scct"
Expand All @@ -19,18 +20,20 @@ class ScctInstrumentPlugin(val global: Global) extends Plugin {
options.projectId = opt.substring("projectId:".length)
} else if (opt.startsWith("basedir:")) {
options.baseDir = new File(opt.substring("basedir:".length))
} else if (opt.startsWith("excludePackages:")) {
options.excludePackages = opt.substring("excludePackages:".length).split(",").filter(_.length > 0).map(_.r)
} else {
error("Unknown option: "+opt)
}
}
}
override val optionsHelp: Option[String] = Some(
" -P:scct:projectId:<name> identify compiled classes under project <name>\n" +
" -P:scct:basedir:<dir> set the root dir of the project being compiled"
" -P:scct:projectId:<name> identify compiled classes under project <name>\n" +
" -P:scct:basedir:<dir> set the root dir of the project being compiled\n"
)
}

class ScctInstrumentPluginOptions(val compilationId:String, var projectId:String, var baseDir:File) {
class ScctInstrumentPluginOptions(val compilationId:String, var projectId:String, var baseDir:File, var excludePackages: Array[Regex] = Array()) {
def this() = this(System.currentTimeMillis.toString + Random.nextLong().toString, ScctInstrumentPluginOptions.defaultProjectName, ScctInstrumentPluginOptions.defaultBasedir)
}

Expand All @@ -45,14 +48,15 @@ object ScctInstrumentPluginOptions {

class ScctTransformComponent(val global: Global, val opts:ScctInstrumentPluginOptions) extends PluginComponent with TypingTransformers with Transform {
import global._
import global.definitions._

val runsAfter = List[String]("typer")
override val runsBefore = List[String]("patmat")

val phaseName = "scctInstrumentation"
def newTransformer(unit: CompilationUnit) = new Instrumenter(unit)

val filter = new CoverageFilter(opts.excludePackages)

var debug = System.getProperty("scct.debug") == "true"
var saveData = true
var counter = 0
Expand All @@ -70,11 +74,15 @@ class ScctTransformComponent(val global: Global, val opts:ScctInstrumentPluginOp
super.run
saveMetadata
}


private def saveMetadata {
if (saveData) {
val filtered = filter.filter(data)

println("scct: [" + opts.projectId + "] Saving coverage data.")
if (coverageFile.exists) coverageFile.delete
MetadataPickler.toFile(data, coverageFile)
MetadataPickler.toFile(filtered, coverageFile)
}
}
}
Expand Down
35 changes: 35 additions & 0 deletions src/test/scala/reaktor/scct/CoverageFilterSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package reaktor.scct

import org.specs2.mutable._
import reaktor.scct.ClassTypes.ClassType

class CoverageFilterSpec extends Specification {

"empty filter does pass thru" in {
val data = List(block( "packageName" ))

new CoverageFilter(Array()).filter(data) mustEqual data
}

"non matching filter does pass thru" in {
val data = List(block( "packageName" ))
new CoverageFilter(Array("nonMatching".r)).filter(data) mustEqual data
}

"filter excludes matches" in {
val blockMatching = block( "MATCHINGPART" )
val blockNonMatching = block( "nonmatching" )

new CoverageFilter(Array("MATCHINGPART".r)).filter(List(blockMatching, blockNonMatching)) mustEqual List(blockNonMatching)
}

"multiple filters" in {
val blockMatching1 = block( "MATCH1.suffix" )
val blockMatching2 = block( "MATCH2.suffix" )
val blockNonMatching = block( "nonmatching" )

new CoverageFilter(Array("^MATCH1".r, "^MATCH2".r)).filter(List(blockMatching1, blockMatching2, blockNonMatching)) mustEqual List(blockNonMatching)
}

def block(packageName: String) = new CoveredBlock("c1", 1, Name("file1", ClassTypes.Class, packageName, "className", "projectName"), 1, false)
}
4 changes: 2 additions & 2 deletions src/test/scala/reaktor/scct/ScctInstrumentPluginSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package reaktor.scct
import org.specs2.mutable._
import org.specs2.mock._
import tools.nsc.Global
import java.io.File

class ScctInstrumentPluginSpec extends Specification with Mockito {
val sut = new ScctInstrumentPlugin(smartMock[Global])
Expand All @@ -30,9 +29,10 @@ class ScctInstrumentPluginSpec extends Specification with Mockito {
sut.options.baseDir.getName must not be empty
}
"be settable" in {
sut.processOptions(List("basedir:/base/dir", "projectId:myProject"), s => ())
sut.processOptions(List("basedir:/base/dir", "projectId:myProject", "excludePackages:myRegex,yourRegex"), s => ())
sut.options.projectId mustEqual "myProject"
sut.options.baseDir.getAbsolutePath mustEqual "/base/dir"
sut.options.excludePackages mustEqual Array("myRegex".r, "yourRegex".r)
}
"report error" in {
var err = ""
Expand Down