Skip to content

Commit 4688fc6

Browse files
committed
Merge pull request #23 from MichaelZinsmaier/PR_addingDirectoryCoverage
Adding directory coverage thus supporting the Treemap widget
2 parents 3ed8ee8 + 1a184ee commit 4688fc6

File tree

5 files changed

+87
-34
lines changed

5 files changed

+87
-34
lines changed

plugin/src/main/scala/com/buransky/plugins/scoverage/StatementCoverage.scala

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ sealed trait StatementCoverage {
3838
/**
3939
* Total number of all statements within the source code unit,
4040
*/
41-
val statementCount: Int
41+
def statementCount: Int
4242

4343
/**
4444
* Number of statements covered by unit tests.
4545
*/
46-
val coveredStatementsCount: Int
46+
def coveredStatementsCount: Int
4747

4848
require(statementCount >= 0, "Statements count cannot be negative! [" + statementCount + "]")
4949
require(coveredStatementsCount >= 0, "Statements count cannot be negative! [" +
@@ -57,29 +57,43 @@ sealed trait StatementCoverage {
5757
* Allows to build tree structure from state coverage values.
5858
*/
5959
trait NodeStatementCoverage extends StatementCoverage {
60-
val children: Iterable[StatementCoverage]
61-
val statementCount = children.map(_.statementCount).sum
62-
val coveredStatementsCount = children.map(_.coveredStatementsCount).sum
60+
def name: String
61+
def children: Iterable[NodeStatementCoverage]
62+
def statementSum: Int = children.map(_.statementSum).sum
63+
def coveredStatementsSum: Int = children.map(_.coveredStatementsSum).sum
6364
}
6465

6566
/**
6667
* Root node. In multi-module projects it can contain other ProjectStatementCoverage
6768
* elements as children.
6869
*/
69-
case class ProjectStatementCoverage(name: String, children: Iterable[StatementCoverage])
70-
extends NodeStatementCoverage
70+
case class ProjectStatementCoverage(name: String, children: Iterable[NodeStatementCoverage])
71+
extends NodeStatementCoverage {
72+
// projects' coverage values are defined as sums of their child values
73+
val statementCount = statementSum
74+
val coveredStatementsCount = coveredStatementsSum
75+
}
7176

7277
/**
7378
* Physical directory in file system.
7479
*/
75-
case class DirectoryStatementCoverage(name: String, children: Iterable[StatementCoverage])
76-
extends NodeStatementCoverage
80+
case class DirectoryStatementCoverage(name: String, children: Iterable[NodeStatementCoverage])
81+
extends NodeStatementCoverage {
82+
// directories' coverage values are defined as sums of their DIRECT child values
83+
val statementCount = children.filter(_.isInstanceOf[FileStatementCoverage]).map(_.statementCount).sum
84+
val coveredStatementsCount = children.filter(_.isInstanceOf[FileStatementCoverage]).map(_.coveredStatementsCount).sum
85+
}
7786

7887
/**
7988
* Scala source code file.
8089
*/
8190
case class FileStatementCoverage(name: String, statementCount: Int, coveredStatementsCount: Int,
82-
statements: Iterable[CoveredStatement]) extends StatementCoverage
91+
statements: Iterable[CoveredStatement]) extends NodeStatementCoverage {
92+
// leaf implementation sums==values
93+
val children = List.empty[NodeStatementCoverage]
94+
override val statementSum = statementCount
95+
override val coveredStatementsSum = coveredStatementsCount
96+
}
8397

8498
/**
8599
* Position a Scala source code file.

plugin/src/main/scala/com/buransky/plugins/scoverage/measure/ScalaMetrics.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,5 @@ object ScalaMetrics {
5353
.setDirection(Metric.DIRECTION_BETTER)
5454
.setQualitative(false)
5555
.setDomain(CoreMetrics.DOMAIN_SIZE)
56-
.setFormula(new org.sonar.api.measures.SumChildValuesFormula(false))
5756
.create[java.lang.Integer]()
5857
}

plugin/src/main/scala/com/buransky/plugins/scoverage/sensor/ScoverageSensor.scala

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ import com.buransky.plugins.scoverage.language.Scala
2525
import com.buransky.plugins.scoverage.measure.ScalaMetrics
2626
import com.buransky.plugins.scoverage.util.LogUtil
2727
import com.buransky.plugins.scoverage.xml.XmlScoverageReportParser
28-
import com.buransky.plugins.scoverage.{CoveredStatement, DirectoryStatementCoverage, FileStatementCoverage, _}
29-
import org.sonar.api.batch.fs.{FileSystem, InputFile}
30-
import org.sonar.api.batch.{CoverageExtension, Sensor, SensorContext}
28+
import com.buransky.plugins.scoverage.{ CoveredStatement, DirectoryStatementCoverage, FileStatementCoverage, _ }
29+
import org.sonar.api.batch.fs.{ FileSystem, InputFile, InputDir, InputPath }
30+
import org.sonar.api.batch.{ CoverageExtension, Sensor, SensorContext }
3131
import org.sonar.api.config.Settings
32-
import org.sonar.api.measures.{CoreMetrics, CoverageMeasuresBuilder, Measure}
33-
import org.sonar.api.resources.{File, Project, Resource}
32+
import org.sonar.api.measures.{ CoreMetrics, CoverageMeasuresBuilder, Measure }
33+
import org.sonar.api.resources.{ File, Project, Directory, Resource }
3434
import org.sonar.api.scan.filesystem.PathResolver
3535
import org.sonar.api.utils.log.Loggers
3636

@@ -151,33 +151,55 @@ class ScoverageSensor(settings: Settings, pathResolver: PathResolver, fileSystem
151151
processChildren(projectCoverage.children, context, sonarSources)
152152
}
153153

154-
private def processDirectory(directoryCoverage: DirectoryStatementCoverage, context: SensorContext,
155-
parentDirectory: String) {
154+
private def processDirectory(directoryCoverage: DirectoryStatementCoverage, context: SensorContext, parentDirectory: String) {
155+
// save measures if any
156+
if (directoryCoverage.statementCount > 0) {
157+
val path = appendFilePath(parentDirectory, directoryCoverage.name)
158+
159+
getResource(path, context, false) match {
160+
case Some(srcDir) => {
161+
// Save directory measures
162+
saveMeasures(context, srcDir, directoryCoverage)
163+
}
164+
case None =>
165+
}
166+
}
156167
// Process children
157168
processChildren(directoryCoverage.children, context, appendFilePath(parentDirectory, directoryCoverage.name))
158169
}
159170

160171
private def processFile(fileCoverage: FileStatementCoverage, context: SensorContext, directory: String) {
161172
val path = appendFilePath(directory, fileCoverage.name)
162-
val p = fileSystem.predicates()
163-
164-
val files = fileSystem.inputFiles(p.and(
165-
p.hasRelativePath(path),
166-
p.hasLanguage(scala.getKey),
167-
p.hasType(InputFile.Type.MAIN))).toList
168-
169-
files.headOption match {
170-
case Some(file) =>
171-
val scalaSourceFile = File.create(file.relativePath())
172173

174+
getResource(path, context, true) match {
175+
case Some(scalaSourceFile) => {
173176
// Save measures
174177
saveMeasures(context, scalaSourceFile, fileCoverage)
175-
176178
// Save line coverage. This is needed just for source code highlighting.
177179
saveLineCoverage(fileCoverage.statements, scalaSourceFile, context)
180+
}
181+
case None =>
182+
}
183+
}
178184

185+
private def getResource(path: String, context: SensorContext, isFile: Boolean): Option[Resource] = {
186+
187+
val inputOption: Option[InputPath] = if (isFile) {
188+
val p = fileSystem.predicates()
189+
Option(fileSystem.inputFile(p.and(
190+
p.hasRelativePath(path),
191+
p.hasLanguage(scala.getKey),
192+
p.hasType(InputFile.Type.MAIN))))
193+
} else {
194+
Option(fileSystem.inputDir(pathResolver.relativeFile(fileSystem.baseDir(), path)))
195+
}
196+
197+
inputOption match {
198+
case Some(path: InputPath) =>
199+
Some(context.getResource(path))
179200
case None => {
180-
log.warn(s"File not found in file system! [$path]")
201+
log.warn(s"File or directory not found in file system! ${path}")
202+
None
181203
}
182204
}
183205
}

plugin/src/main/scala/com/buransky/plugins/scoverage/xml/XmlScoverageReportConstructingParser.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class XmlScoverageReportConstructingParser(source: Source, pathSanitizer: PathSa
139139
}
140140
}
141141

142-
def toStatementCoverage: StatementCoverage = {
142+
def toStatementCoverage: NodeStatementCoverage = {
143143
val childNodes = children.map(_.toStatementCoverage)
144144

145145
childNodes match {
@@ -151,7 +151,6 @@ class XmlScoverageReportConstructingParser(source: Source, pathSanitizer: PathSa
151151
def toProjectStatementCoverage: ProjectStatementCoverage = {
152152
toStatementCoverage match {
153153
case node: NodeStatementCoverage => ProjectStatementCoverage("", node.children)
154-
case file: FileStatementCoverage => ProjectStatementCoverage("", List(file))
155154
case _ => throw new ScoverageException("Illegal statement coverage!")
156155
}
157156
}

plugin/src/test/scala/com/buransky/plugins/scoverage/xml/XmlScoverageReportConstructingParserSpec.scala

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import com.buransky.plugins.scoverage.xml.data.XmlReportFile1
2727
import scala._
2828
import com.buransky.plugins.scoverage.{ProjectStatementCoverage, FileStatementCoverage, DirectoryStatementCoverage}
2929
import com.buransky.plugins.scoverage.pathcleaner.PathSanitizer
30+
import com.buransky.plugins.scoverage.StatementCoverage
31+
import com.buransky.plugins.scoverage.NodeStatementCoverage
3032

3133
@RunWith(classOf[JUnitRunner])
3234
class XmlScoverageReportConstructingParserSpec extends FlatSpec with Matchers {
@@ -52,9 +54,16 @@ class XmlScoverageReportConstructingParserSpec extends FlatSpec with Matchers {
5254
assertReportFile(XmlReportFile1.scoverage104Data, 50.0, sanitizer) { projectCoverage =>
5355
assert(projectCoverage.name === "")
5456
assert(projectCoverage.children.size.toInt === 1)
57+
5558
projectCoverage.children.head match {
56-
case rootDir: DirectoryStatementCoverage =>
57-
assert(rootDir.name == "com")
59+
case rootDir: DirectoryStatementCoverage => {
60+
val rr = checkNode(rootDir, "com", 0, 0, 0.0).head
61+
val test = checkNode(rr, "rr", 0, 0, 0.0).head
62+
val sonar = checkNode(test, "test", 0, 0, 0.0).head
63+
val mainClass = checkNode(sonar, "sonar", 2, 1, 50.0).head
64+
65+
checkNode(mainClass, "MainClass.scala", 2, 1, 50.0)
66+
}
5867
case other => fail(s"This is not a directory statement coverage! [$other]")
5968
}
6069
}
@@ -111,4 +120,14 @@ class XmlScoverageReportConstructingParserSpec extends FlatSpec with Matchers {
111120
private def checkRate(expected: Double, real: Double) {
112121
BigDecimal(real).setScale(2, BigDecimal.RoundingMode.HALF_UP).should(equal(BigDecimal(expected)))
113122
}
123+
124+
private def checkNode(node: NodeStatementCoverage, name: String, count: Int, covered: Int, rate: Double): Iterable[NodeStatementCoverage] = {
125+
node.name shouldEqual name
126+
node.statementCount shouldEqual count
127+
node.coveredStatementsCount shouldEqual covered
128+
129+
checkRate(rate, node.rate)
130+
131+
node.children
132+
}
114133
}

0 commit comments

Comments
 (0)