Skip to content

Commit

Permalink
Merge pull request #449 from codacy/revert-434-io-142-remove-total-field
Browse files Browse the repository at this point in the history
Revert "IO-142-Remove total field from coverage parser implementation "
  • Loading branch information
rubencodacy committed Feb 23, 2023
2 parents 6d0b6a7 + 4518895 commit fddc44e
Show file tree
Hide file tree
Showing 21 changed files with 321 additions and 124 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ lazy val coverageParser = project
.in(file("coverage-parser"))
.settings(
libraryDependencies ++= Seq(
"com.codacy" %% "codacy-api-scala" % "7.0.8",
"com.codacy" %% "codacy-api-scala" % "7.0.7",
"com.codacy" %% "codacy-plugins-api" % "5.2.0",
"org.scala-lang.modules" %% "scala-xml" % "1.2.0",
"org.scalatest" %% "scalatest" % "3.0.8" % Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.io.File
import java.nio.file.Paths

import com.codacy.api.{CoverageFileReport, CoverageReport}
import com.codacy.parsers.util.TextUtils
import com.codacy.parsers.util.{MathUtils, TextUtils}
import com.codacy.parsers.{CoverageParser, XmlReportParser}

import scala.xml.{Elem, Node, NodeSeq}
Expand All @@ -27,6 +27,10 @@ object CloverParser extends CoverageParser with XmlReportParser {
override def getRootNode(xml: Elem): NodeSeq = xml \\ CoverageTag

private def parseReportNode(rootProject: File, report: NodeSeq): Either[String, CoverageReport] = {
val metricsNode = report \ ProjectTag \ MetricsTag
val totalCoverage = getCoveragePercentage(metricsNode).left
.map(errorMessage => s"Could not retrieve total coverage from metrics tag in project: $errorMessage")

val rootPath = TextUtils.sanitiseFilename(rootProject.getAbsolutePath)

val coverageFiles = (report \\ "file").foldLeft[Either[String, Seq[CoverageFileReport]]](Right(List())) {
Expand All @@ -37,7 +41,17 @@ object CloverParser extends CoverageParser with XmlReportParser {
case (Left(errorMessage), _) => Left(errorMessage)
}

coverageFiles.map(CoverageReport(_))
for {
totalCoverage <- totalCoverage
coverageFiles <- coverageFiles
} yield CoverageReport(totalCoverage, coverageFiles)
}

private def getCoveragePercentage(metrics: NodeSeq): Either[String, Int] = {
for {
totalStatements <- getFirstNonEmptyValueAsInt(metrics, "statements")
coveredStatements <- getFirstNonEmptyValueAsInt(metrics, "coveredstatements")
} yield MathUtils.computePercentage(coveredStatements, totalStatements)
}

private def getCoverageFileReport(rootPath: String, fileNode: Node): Either[String, CoverageFileReport] = {
Expand All @@ -58,9 +72,15 @@ object CloverParser extends CoverageParser with XmlReportParser {

for {
relativeFilePath <- relativeFilePath
metricsNode = fileNode \ MetricsTag
fileCoverage <- getCoveragePercentage(metricsNode).left
.map(
errorMessage =>
s"Could not retrieve file coverage from metrics tag for file '$relativeFilePath': $errorMessage"
)
linesCoverage <- getLinesCoverage(fileNode).left
.map(errorMessage => s"Could not retrieve lines coverage for file '$relativeFilePath': $errorMessage")
} yield CoverageFileReport(relativeFilePath, linesCoverage)
} yield CoverageFileReport(relativeFilePath, fileCoverage, linesCoverage)
}

private def getLinesCoverage(fileNode: Node): Either[String, Map[Int, Int]] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,31 @@ object CoberturaParser extends CoverageParser with XmlReportParser {
private def parseReportNode(projectRoot: File, report: NodeSeq) = {
val projectRootStr: String = TextUtils.sanitiseFilename(projectRoot.getAbsolutePath)

val total = math.round(TextUtils.asFloat(report \\ CoverageTag \@ LineRateAttribute) * 100)

val fileReports: List[CoverageFileReport] = (for {
(filename, classes) <- (report \\ "class").groupBy(c => c \@ "filename")
} yield {
val cleanFilename = TextUtils.sanitiseFilename(filename).stripPrefix(projectRootStr).stripPrefix("/")
lineCoverage(cleanFilename, classes)
})(collection.breakOut)

CoverageReport(fileReports)
CoverageReport(total, fileReports)
}

private def lineCoverage(sourceFilename: String, classes: NodeSeq): CoverageFileReport = {
val classHit = (classes \\ s"@$LineRateAttribute").map { total =>
val totalValue = TextUtils.asFloat(total.text)
math.round(totalValue * 100)
}
val fileHit = if (classHit.nonEmpty) { classHit.sum / classHit.length } else 0

val lineHitMap: Map[Int, Int] =
(for {
xClass <- classes
line <- xClass \\ "line"
} yield (line \@ "number").toInt -> (line \@ "hits").toIntOrMaxValue).toMap

CoverageFileReport(sourceFilename, lineHitMap)
CoverageFileReport(sourceFilename, fileHit, lineHitMap)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package com.codacy.parsers.implementation
import java.io.File

import com.codacy.api.{CoverageFileReport, CoverageReport}
import com.codacy.parsers.util.TextUtils
import com.codacy.parsers.util.{MathUtils, TextUtils}
import com.codacy.parsers.{CoverageParser, XmlReportParser}

import scala.xml.{Elem, NodeSeq}

case class StatementNode(fileIndex: Int, line: Int, covered: Boolean)

object DotcoverParser extends CoverageParser with XmlReportParser {
override val name: String = "DotCover"

Expand All @@ -27,6 +29,8 @@ object DotcoverParser extends CoverageParser with XmlReportParser {
private def parseReportNode(rootProject: File, rootNode: NodeSeq): CoverageReport = {
val projectRootStr: String = TextUtils.sanitiseFilename(rootProject.getAbsolutePath)

val totalCoverage = (rootNode \@ CoverageAttribute).toInt

val fileIndices: Map[Int, String] = (rootNode \ "FileIndices" \ "File").map { x =>
(x \@ "Index").toInt -> (x \@ "Name")
}.toMap
Expand All @@ -37,9 +41,12 @@ object DotcoverParser extends CoverageParser with XmlReportParser {
(fileIndex, statements) <- statementsPerFile
filename = TextUtils.sanitiseFilename(fileIndices(fileIndex)).stripPrefix(projectRootStr).stripPrefix("/")
lineCoverage = getLineCoverage(statements)
} yield CoverageFileReport(filename, lineCoverage)
totalLines = lineCoverage.keys.size
coveredLines = lineCoverage.values.count(_ > 0)
total = MathUtils.computePercentage(coveredLines, totalLines)
} yield CoverageFileReport(filename, total, lineCoverage)

CoverageReport(fileReports.toSeq)
CoverageReport(totalCoverage, fileReports.toSeq)
}

private def getLineCoverage(statementNodes: NodeSeq) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.codacy.parsers.implementation
import com.codacy.parsers.CoverageParser

import java.io.File
import com.codacy.parsers.util.MathUtils

import com.codacy.api.{CoverageFileReport, CoverageReport}

Expand Down Expand Up @@ -62,14 +63,31 @@ object GoParser extends CoverageParser {
acc ++ lineHits(coverageInfo)
}

val totalForFile = calculateTotal(statementsCountForFile)

accum :+ CoverageFileReportWithStatementsCount(
statementsCountForFile,
CoverageFileReport(filename, coverage)
CoverageFileReport(filename, totalForFile, coverage)
)
}
})

CoverageReport(coverageFileReports.map(_.coverageFileReport))
val (covered, total) = coverageFileReports
.foldLeft[(Int, Int)]((0, 0)) {
case ((covered, total), coverageFileReportWithStatementsCount) =>
(
covered + coverageFileReportWithStatementsCount.statementsCount.coveredStatements,
total + coverageFileReportWithStatementsCount.statementsCount.numberStatements
)
}

val totalCoverage = MathUtils.computePercentage(covered, total)

CoverageReport(totalCoverage, coverageFileReports.map(_.coverageFileReport))
}

private def calculateTotal(coverageFileStatements: GoCoverageStatementsCount): Int = {
MathUtils.computePercentage(coverageFileStatements.coveredStatements, coverageFileStatements.numberStatements)
}

private def lineHits(coverageInfo: GoCoverageInfo): Map[Int, Int] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,72 @@
package com.codacy.parsers.implementation

import java.io.File

import com.codacy.api._
import com.codacy.parsers.util.TextUtils
import com.codacy.parsers.{CoverageParser, XmlReportParser}

import scala.xml.{Elem, Node, NodeSeq}

private case class LineCoverage(missedInstructions: Int, coveredInstructions: Int)

object JacocoParser extends CoverageParser with XmlReportParser {

override val name: String = "Jacoco"

private val ReportTag = "report"

override def parse(projectRoot: File, reportFile: File): Either[String, CoverageReport] =
parseReport(reportFile, s"Could not find top level <$ReportTag> tag") { node =>
Right(parseReportNode(projectRoot, node))
parseReport(reportFile, s"Could not find top level <$ReportTag> tag") {
parseReportNode(projectRoot, _)
}

override def validateSchema(xml: Elem): Boolean = getRootNode(xml).nonEmpty

override def getRootNode(xml: Elem): NodeSeq = xml \\ ReportTag

private def parseReportNode(projectRoot: File, report: NodeSeq): CoverageReport = {
private def parseReportNode(projectRoot: File, report: NodeSeq): Either[String, CoverageReport] = {
val projectRootStr: String = TextUtils.sanitiseFilename(projectRoot.getAbsolutePath)
val filesCoverage = for {
pkg <- report \\ "package"
packageName = (pkg \@ "name")
sourceFile <- pkg \\ "sourcefile"
} yield {
val filename =
TextUtils
.sanitiseFilename(s"$packageName/${(sourceFile \@ "name")}")
.stripPrefix(projectRootStr)
.stripPrefix("/")
calculateCoverageFileReport(filename, sourceFile)
totalPercentage(report).map { total =>
val filesCoverage = for {
pkg <- report \\ "package"
packageName = (pkg \@ "name")
sourceFile <- pkg \\ "sourcefile"
} yield {
val filename =
TextUtils
.sanitiseFilename(s"$packageName/${(sourceFile \@ "name")}")
.stripPrefix(projectRootStr)
.stripPrefix("/")
lineCoverage(filename, sourceFile)
}

CoverageReport(total, filesCoverage)
}
CoverageReport(filesCoverage)
}

private def calculateCoverageFileReport(filename: String, fileNode: Node): CoverageFileReport = {
private def totalPercentage(report: NodeSeq): Either[String, Int] = {
(report \\ ReportTag \ "counter")
.collectFirst {
case counter if (counter \@ "type") == "LINE" =>
val covered = TextUtils.asFloat((counter \@ "covered"))
val missed = TextUtils.asFloat((counter \@ "missed"))
Right(((covered / (covered + missed)) * 100).toInt)
}
.getOrElse {
Left("Could not retrieve total percentage of coverage.")
}
}

private def lineCoverage(filename: String, fileNode: Node): CoverageFileReport = {
val lineHit = (fileNode \ "counter").collect {
case counter if (counter \@ "type") == "LINE" =>
val covered = TextUtils.asFloat((counter \@ "covered"))
val missed = TextUtils.asFloat((counter \@ "missed"))
(if ((covered + missed) > 0) (covered / (covered + missed)) * 100 else 0f).toInt
}

case class LineCoverage(missedInstructions: Int, coveredInstructions: Int)
val fileHit = if (lineHit.sum != 0) { lineHit.sum / lineHit.length } else 0

val lineHitMap: Map[Int, Int] = (fileNode \\ "line")
.map { line =>
Expand All @@ -52,6 +77,6 @@ object JacocoParser extends CoverageParser with XmlReportParser {
key -> (if (lineCoverage.coveredInstructions > 0) 1 else 0)
}(collection.breakOut)

CoverageFileReport(filename, lineHitMap)
CoverageFileReport(filename, fileHit, lineHitMap)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.codacy.parsers.util.MathUtils._
import com.codacy.api.{CoverageFileReport, CoverageReport}
import java.io.File

import com.codacy.parsers.util.XMLoader
import com.codacy.parsers.util.{MathUtils, XMLoader}

import scala.io.Source
import scala.util.{Failure, Success, Try}
Expand Down Expand Up @@ -36,7 +36,7 @@ object LCOVParser extends CoverageParser {
(accum, next) =>
accum.flatMap {
case reports if next startsWith SF =>
Right(CoverageFileReport(next stripPrefix SF, Map()) +: reports)
Right(CoverageFileReport(next stripPrefix SF, 0, Map()) +: reports)
case reports if next startsWith DA =>
reports.headOption match {
case Some(value) =>
Expand All @@ -56,9 +56,25 @@ object LCOVParser extends CoverageParser {
)
coverageFileReports.map { fileReports =>
val totalFileReport = fileReports.map { report =>
CoverageFileReport(report.filename, report.coverage)
val coveredLines = report.coverage.count { case (_, hit) => hit > 0 }
val totalLines = report.coverage.size
val fileCoverage =
MathUtils.computePercentage(coveredLines, totalLines)

CoverageFileReport(report.filename, fileCoverage, report.coverage)
}
CoverageReport(totalFileReport)

val (covered, total) = totalFileReport
.map { f =>
(f.coverage.count { case (_, hit) => hit > 0 }, f.coverage.size)
}
.foldLeft(0 -> 0) {
case ((accumCovered, accumTotal), (nextCovered, nextTotal)) =>
(accumCovered + nextCovered, accumTotal + nextTotal)
}

val totalCoverage = MathUtils.computePercentage(covered, total)
CoverageReport(totalCoverage, totalFileReport)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.codacy.parsers.implementation
import java.io.File

import com.codacy.api.{CoverageFileReport, CoverageReport}
import com.codacy.parsers.util.TextUtils
import com.codacy.parsers.util.{MathUtils, TextUtils}
import com.codacy.parsers.{CoverageParser, XmlReportParser}

import scala.xml.{Elem, NodeSeq}
Expand Down Expand Up @@ -43,18 +43,35 @@ object OpenCoverParser extends CoverageParser with XmlReportParser {
filename <- fileIndices.get(fileIndex)

sanitisedFileName = TextUtils.sanitiseFilename(filename).stripPrefix(projectRoot).stripPrefix("/")
lineCoverage = getLineCoverage(methods)
} yield CoverageFileReport(sanitisedFileName, lineCoverage)).toSeq
lineCoverage = getLineCoverage(methods, sanitisedFileName)
totalLines = lineCoverage.size
coveredLines = lineCoverage.count { case (_, visitCount) => visitCount > 0 }
coverage = MathUtils.computePercentage(coveredLines, totalLines)
} yield CoverageFileReport(sanitisedFileName, coverage, lineCoverage)).toSeq

CoverageReport(fileReports)
val totalCoverage = computeTotalCoverage(fileReports)

CoverageReport(totalCoverage, fileReports)
}

private def getLineCoverage(methodNodes: NodeSeq) = {
private def getLineCoverage(methodNodes: NodeSeq, filename: String) = {
val lineCoverage = for {
methodNode <- methodNodes
sequencePoint <- methodNode \\ SequencePointTag
} yield (sequencePoint \@ LineAttribute).toInt -> (sequencePoint \@ VisitCounterAttribute).toInt

lineCoverage.toMap
}

private def computeTotalCoverage(fileReports: Seq[CoverageFileReport]) = {
val (totalLines, coveredLines) = fileReports
.foldLeft((0, 0)) {
case ((total, covered), f) =>
val totalLines = f.coverage.size
val coveredLines = (f.total * totalLines) / 100
(total + totalLines, covered + coveredLines)
}

MathUtils.computePercentage(coveredLines, totalLines)
}
}
Loading

0 comments on commit fddc44e

Please sign in to comment.