From 451889565afb8c1b8ad5702f9fb9914e1708f4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Santos?= <97224990+rubencodacy@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:55:01 +0000 Subject: [PATCH] Revert "IO-142-Remove total field from coverage parser implementation " --- build.sbt | 2 +- .../parsers/implementation/CloverParser.scala | 26 ++++++- .../implementation/CoberturaParser.scala | 12 +++- .../implementation/DotcoverParser.scala | 13 +++- .../parsers/implementation/GoParser.scala | 22 +++++- .../parsers/implementation/JacocoParser.scala | 61 ++++++++++++----- .../parsers/implementation/LCOVParser.scala | 24 +++++-- .../implementation/OpenCoverParser.scala | 27 ++++++-- .../implementation/PhpUnitXmlParser.scala | 12 +++- .../com/codacy/parsers/CloverParserTest.scala | 45 ++++++++++++ .../codacy/parsers/CoberturaParserTest.scala | 24 ++++++- .../parsers/CoverageParserFactoryTest.scala | 18 ++++- .../codacy/parsers/DotCoverParserTest.scala | 18 ++++- .../com/codacy/parsers/GoParserTest.scala | 2 + .../com/codacy/parsers/JacocoParserTest.scala | 11 ++- .../com/codacy/parsers/LCOVParserTest.scala | 8 ++- .../codacy/parsers/OpenCoverParserTest.scala | 13 +++- .../codacy/parsers/PhpUnitXmlParserTest.scala | 28 ++++++++ integration-tests/mock-server-config.json | 68 +------------------ .../com/codacy/rules/ReportRulesSpec.scala | 8 +-- .../transformation/PathPrefixerSpec.scala | 3 +- 21 files changed, 321 insertions(+), 124 deletions(-) diff --git a/build.sbt b/build.sbt index c3cbb527..ea90fd43 100644 --- a/build.sbt +++ b/build.sbt @@ -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 diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CloverParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CloverParser.scala index e9d80906..2a60e120 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CloverParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CloverParser.scala @@ -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} @@ -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())) { @@ -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] = { @@ -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]] = { diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CoberturaParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CoberturaParser.scala index 36c3ba7c..3568de3b 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CoberturaParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/CoberturaParser.scala @@ -31,6 +31,8 @@ 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 { @@ -38,16 +40,22 @@ object CoberturaParser extends CoverageParser with XmlReportParser { 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) } } diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/DotcoverParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/DotcoverParser.scala index 57dbdb45..6dfbfe97 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/DotcoverParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/DotcoverParser.scala @@ -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" @@ -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 @@ -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) = { diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/GoParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/GoParser.scala index 3d1b1db5..ddd3fa41 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/GoParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/GoParser.scala @@ -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} @@ -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] = { diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/JacocoParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/JacocoParser.scala index 1a6fd6cd..cb3f69da 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/JacocoParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/JacocoParser.scala @@ -1,12 +1,15 @@ 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" @@ -14,34 +17,56 @@ object JacocoParser extends CoverageParser with XmlReportParser { 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 => @@ -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) } } diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/LCOVParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/LCOVParser.scala index 1aae471f..f486cfdb 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/LCOVParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/LCOVParser.scala @@ -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} @@ -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) => @@ -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) } } } diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/OpenCoverParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/OpenCoverParser.scala index 444707fe..5de6c921 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/OpenCoverParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/OpenCoverParser.scala @@ -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} @@ -43,13 +43,18 @@ 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 @@ -57,4 +62,16 @@ object OpenCoverParser extends CoverageParser with XmlReportParser { 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) + } } diff --git a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/PhpUnitXmlParser.scala b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/PhpUnitXmlParser.scala index cee5ae2b..62688b4f 100644 --- a/coverage-parser/src/main/scala/com/codacy/parsers/implementation/PhpUnitXmlParser.scala +++ b/coverage-parser/src/main/scala/com/codacy/parsers/implementation/PhpUnitXmlParser.scala @@ -14,6 +14,7 @@ object PhpUnitXmlParser extends CoverageParser with XmlReportParser { private val PhpUnitTag = "phpunit" private val ProjectTag = "project" private val DirectoryTag = "directory" + private val TotalsTag = "totals" private val XmlParseErrorMessage = s"Could not find top level <$PhpUnitTag> tag"; override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] = @@ -35,7 +36,8 @@ object PhpUnitXmlParser extends CoverageParser with XmlReportParser { val codeDirectory = report \ ProjectTag \ DirectoryTag \@ "name" val fileReports = makeFileReports(fileNodes, projectRootPath, codeDirectory, reportRootPath) - fileReports.map(CoverageReport(_)) + val totalPercentage = getTotalsCoveragePercentage(report \ ProjectTag \ DirectoryTag \ TotalsTag) + fileReports.map(CoverageReport(totalPercentage, _)) } private def makeFileReports( @@ -48,9 +50,10 @@ object PhpUnitXmlParser extends CoverageParser with XmlReportParser { for (f <- fileNodes) { val reportFileName = f \@ "href" val fileName = getSourceFileName(projectRootPath, codeDirectory, reportFileName) + val coveragePercentage = getTotalsCoveragePercentage(f \ TotalsTag) getLineCoverage(reportRootPath, reportFileName) match { case Right(lineCoverage) => - builder += CoverageFileReport(fileName, lineCoverage) + builder += CoverageFileReport(fileName, coveragePercentage, lineCoverage) case Left(message) => return Left(message) } } @@ -69,6 +72,11 @@ object PhpUnitXmlParser extends CoverageParser with XmlReportParser { lineCoverage } + private def getTotalsCoveragePercentage(totals: NodeSeq) = { + val percentageStr = (totals \ "lines" \@ "percent").dropRight(1) + scala.math.round(percentageStr.toFloat) + } + private def getSourceFileName(pathToRemove: String, codeRootDirectory: String, reportRelativePath: String) = { new File(codeRootDirectory, reportRelativePath).getAbsolutePath .stripPrefix(pathToRemove) diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/CloverParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/CloverParserTest.scala index 7037fc85..8376b98c 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/CloverParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/CloverParserTest.scala @@ -35,6 +35,19 @@ class CloverParserTest extends WordSpec with Matchers with EitherValues { parseResult shouldBe Left("Invalid report. Could not find tag hierarchy tags.") } + "the report is missing the statements attribute in the file metrics tag" in { + // Arrange + val invalidCloverReportPath = "coverage-parser/src/test/resources/test_invalid_clover.xml" + + // Act + val parseResult = CloverParser.parse(new File("."), new File(invalidCloverReportPath)) + + // Assert + parseResult shouldBe Left( + "Could not retrieve file coverage from metrics tag for file 'home/codacy-php/src/Codacy/Coverage/Parser/Parser.php': Could not find attribute with name 'statements'" + ) + } + } val cloverReportPath = "coverage-parser/src/test/resources/test_clover.xml" @@ -105,6 +118,18 @@ class CloverParserTest extends WordSpec with Matchers with EitherValues { fileReports should have length expectedNumberOfFiles } + "return a report with the expected total coverage" in { + // Arrange + val expectedTotalCoverage = 38 + + // Act + val coverageTotal = + CloverParser.parse(new File("/home/codacy-php/"), new File(cloverReportPath)).right.value.total + + // Assert + coverageTotal shouldBe expectedTotalCoverage + } + "return a report with the expected relative file paths" in { // Arrange val expectedFilePaths = Seq( @@ -128,6 +153,26 @@ class CloverParserTest extends WordSpec with Matchers with EitherValues { parsedReportFilePaths should contain theSameElementsAs expectedFilePaths } + "return a report with the expected file coverage" in { + // Arrange + val filePath = "src/Codacy/Coverage/Parser/Parser.php" + val expectedFileCoverage = 33 + + // Act + val fileTotalCoverage = + CloverParser + .parse(new File("/home/codacy-php/"), new File(cloverReportPath)) + .right + .value + .fileReports + .find(_.filename == filePath) + .getOrElse(fail(s"Could not find report for file:$filePath")) + .total + + // Assert + fileTotalCoverage shouldBe expectedFileCoverage + } + "return a report with the expected file line coverage" in { // Arrange val filePath = "src/Codacy/Coverage/Report/CoverageReport.php" diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/CoberturaParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/CoberturaParserTest.scala index d62aab5b..540d198e 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/CoberturaParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/CoberturaParserTest.scala @@ -26,10 +26,16 @@ class CoberturaParserTest extends WordSpec with BeforeAndAfterAll with Matchers CoberturaParser.parse(new File("."), new File("coverage-parser/src/test/resources/test_cobertura.xml")) val testReport = CoverageReport( + 87, List( - CoverageFileReport("coverage-parser/src/test/resources/TestSourceFile2.scala", Map(1 -> 1, 2 -> 1, 3 -> 1)), + CoverageFileReport( + "coverage-parser/src/test/resources/TestSourceFile2.scala", + 87, + Map(1 -> 1, 2 -> 1, 3 -> 1) + ), CoverageFileReport( "coverage-parser/src/test/resources/TestSourceFile.scala", + 87, Map(5 -> 1, 10 -> 1, 6 -> 2, 9 -> 1, 3 -> 0, 4 -> 1) ) ) @@ -43,10 +49,16 @@ class CoberturaParserTest extends WordSpec with BeforeAndAfterAll with Matchers CoberturaParser.parse(new File("."), new File("coverage-parser/src/test/resources/thousand_sep_cobertura.xml")) val testReport = CoverageReport( + 87, List( - CoverageFileReport("coverage-parser/src/test/resources/TestSourceFile2.scala", Map(1 -> 1, 2 -> 1, 3 -> 1)), + CoverageFileReport( + "coverage-parser/src/test/resources/TestSourceFile2.scala", + 87, + Map(1 -> 1, 2 -> 1, 3 -> 1) + ), CoverageFileReport( "coverage-parser/src/test/resources/TestSourceFile.scala", + 87, Map(5 -> 1, 10 -> 1, 6 -> 2, 9 -> 1, 9 -> 0, 8 -> 1, 4 -> 1) ) ) @@ -60,10 +72,16 @@ class CoberturaParserTest extends WordSpec with BeforeAndAfterAll with Matchers CoberturaParser.parse(new File("."), new File("coverage-parser/src/test/resources/windows_paths_cobertura.xml")) val testReport = CoverageReport( + 87, List( - CoverageFileReport("coverage-parser/src/test/resources/TestSourceFile2.scala", Map(1 -> 1, 2 -> 1, 3 -> 1)), + CoverageFileReport( + "coverage-parser/src/test/resources/TestSourceFile2.scala", + 87, + Map(1 -> 1, 2 -> 1, 3 -> 1) + ), CoverageFileReport( "coverage-parser/src/test/resources/TestSourceFile.scala", + 87, Map(5 -> 1, 10 -> 1, 6 -> 2, 9 -> 1, 3 -> 0, 4 -> 1) ) ) diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/CoverageParserFactoryTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/CoverageParserFactoryTest.scala index dad07919..b89a1085 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/CoverageParserFactoryTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/CoverageParserFactoryTest.scala @@ -11,10 +11,16 @@ class CoverageParserFactoryTest extends WordSpec with BeforeAndAfterAll with Mat "get report with unspecified parser" in { val expectedReport = CoverageReport( + 87, List( - CoverageFileReport("coverage-parser/src/test/resources/TestSourceFile2.scala", Map(1 -> 1, 2 -> 1, 3 -> 1)), + CoverageFileReport( + "coverage-parser/src/test/resources/TestSourceFile2.scala", + 87, + Map(1 -> 1, 2 -> 1, 3 -> 1) + ), CoverageFileReport( "coverage-parser/src/test/resources/TestSourceFile.scala", + 87, Map(5 -> 1, 10 -> 1, 6 -> 2, 9 -> 1, 3 -> 0, 4 -> 1) ) ) @@ -28,12 +34,18 @@ class CoverageParserFactoryTest extends WordSpec with BeforeAndAfterAll with Mat "get report with jacoco parser" in { val expectedReport = CoverageReport( + 73, List( CoverageFileReport( "org/eluder/coverage/sample/InnerClassCoverage.java", + 81, Map(10 -> 1, 6 -> 1, 9 -> 1, 13 -> 1, 22 -> 1, 27 -> 0, 12 -> 1, 3 -> 1, 16 -> 1, 26 -> 0, 19 -> 1) ), - CoverageFileReport("org/eluder/coverage/sample/SimpleCoverage.java", Map(3 -> 1, 6 -> 1, 10 -> 0, 11 -> 0)) + CoverageFileReport( + "org/eluder/coverage/sample/SimpleCoverage.java", + 50, + Map(3 -> 1, 6 -> 1, 10 -> 0, 11 -> 0) + ) ) ) @@ -48,5 +60,7 @@ class CoverageParserFactoryTest extends WordSpec with BeforeAndAfterAll with Mat "Could not parse report, unrecognized report format (tried: Cobertura, Jacoco, Clover, OpenCover, DotCover, PHPUnit, LCOV, Go)" ) } + } + } diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/DotCoverParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/DotCoverParserTest.scala index 37ca2d63..ce5808f8 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/DotCoverParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/DotCoverParserTest.scala @@ -42,17 +42,25 @@ class DotCoverParserTest extends WordSpec with BeforeAndAfterAll with Matchers w ).sorted } + "return the expected total coverage" in { + val reader = DotcoverParser.parse(new File("."), new File(dotCoverReport)) + reader.right.value.total shouldBe 72 + } + "return the expected coverage report" in { val reader = DotcoverParser.parse(new File("."), new File(dotCoverReport)) reader.right.value shouldBe CoverageReport( + 72, List( CoverageFileReport( "src/Coverage/FooBar.cs", + 57, Map(10 -> 1, 21 -> 1, 9 -> 1, 13 -> 0, 17 -> 1, 19 -> 0, 15 -> 0) ), CoverageFileReport( "src/Tests/FooBarTests.cs", + 100, Map( 14 -> 1, 20 -> 1, @@ -68,9 +76,13 @@ class DotCoverParserTest extends WordSpec with BeforeAndAfterAll with Matchers w 15 -> 1 ) ), - CoverageFileReport("src/Coverage/Program.cs", Map(8 -> 0, 9 -> 0, 10 -> 0)), - CoverageFileReport("src/Coverage/Bar.cs", Map(10 -> 0, 14 -> 1, 9 -> 1, 12 -> 0, 11 -> 0, 8 -> 1, 15 -> 1)), - CoverageFileReport("src/Coverage/Foo.cs", Map(8 -> 1, 9 -> 1, 10 -> 1)) + CoverageFileReport("src/Coverage/Program.cs", 0, Map(8 -> 0, 9 -> 0, 10 -> 0)), + CoverageFileReport( + "src/Coverage/Bar.cs", + 57, + Map(10 -> 0, 14 -> 1, 9 -> 1, 12 -> 0, 11 -> 0, 8 -> 1, 15 -> 1) + ), + CoverageFileReport("src/Coverage/Foo.cs", 100, Map(8 -> 1, 9 -> 1, 10 -> 1)) ) ) } diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/GoParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/GoParserTest.scala index c2906035..8ad524f0 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/GoParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/GoParserTest.scala @@ -29,9 +29,11 @@ class GoParserTest extends WordSpec with Matchers with EitherValues { val reader = GoParser.parse(new File("."), new File("coverage-parser/src/test/resources/test_go.out")) val testReport = CoverageReport( + 75, List( CoverageFileReport( "example.com/m/v2/hello.go", + 75, Map(5 -> 0, 14 -> 1, 6 -> 0, 13 -> 1, 17 -> 1, 12 -> 1, 7 -> 0, 18 -> 1, 11 -> 1, 19 -> 1) ) ) diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/JacocoParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/JacocoParserTest.scala index c4df5a28..6fd54920 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/JacocoParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/JacocoParserTest.scala @@ -24,15 +24,24 @@ class JacocoParserTest extends WordSpec with BeforeAndAfterAll with Matchers wit val reader = JacocoParser.parse(new File("."), new File("coverage-parser/src/test/resources/test_jacoco.xml")) val testReport = CoverageReport( + 73, List( CoverageFileReport( "org/eluder/coverage/sample/InnerClassCoverage.java", + 81, Map(10 -> 1, 6 -> 1, 9 -> 1, 13 -> 1, 22 -> 1, 27 -> 0, 12 -> 1, 3 -> 1, 16 -> 1, 26 -> 0, 19 -> 1) ), - CoverageFileReport("org/eluder/coverage/sample/SimpleCoverage.java", Map(3 -> 1, 6 -> 1, 10 -> 0, 11 -> 0)) + CoverageFileReport( + "org/eluder/coverage/sample/SimpleCoverage.java", + 50, + Map(3 -> 1, 6 -> 1, 10 -> 0, 11 -> 0) + ) ) ) + reader.right.value should equal(testReport) } + } + } diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/LCOVParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/LCOVParserTest.scala index 9fae5854..65582f51 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/LCOVParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/LCOVParserTest.scala @@ -34,10 +34,16 @@ class LCOVParserTest extends WordSpec with BeforeAndAfterAll with Matchers with val reader = LCOVParser.parse(new File("."), new File("coverage-parser/src/test/resources/test_lcov.lcov")) val testReport = CoverageReport( + 86, List( - CoverageFileReport("coverage-parser/src/test/resources/TestSourceFile2.scala", Map(1 -> 1, 2 -> 1, 3 -> 1)), + CoverageFileReport( + "coverage-parser/src/test/resources/TestSourceFile2.scala", + 100, + Map(1 -> 1, 2 -> 1, 3 -> 1) + ), CoverageFileReport( "coverage-parser/src/test/resources/TestSourceFile.scala", + 75, Map(3 -> 0, 4 -> 1, 5 -> 1, 6 -> 2) ) ) diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/OpenCoverParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/OpenCoverParserTest.scala index 66e0c4b1..26ee962b 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/OpenCoverParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/OpenCoverParserTest.scala @@ -34,14 +34,21 @@ class OpenCoverParserTest extends WordSpec with BeforeAndAfterAll with Matchers reader.right.value.fileReports.map(_.filename).sorted shouldBe Seq("bar.cs", "foo.cs", "foobar.cs").sorted } + "return the expected total coverage" in { + val reader = OpenCoverParser.parse(new File("."), new File(openCoverReportPath)) + + reader.right.value.total shouldBe 50 + } + "return the expected report" in { val reader = OpenCoverParser.parse(new File("."), new File(openCoverReportPath)) reader.right.value shouldBe CoverageReport( + 50, List( - CoverageFileReport("bar.cs", Map(10 -> 0)), - CoverageFileReport("foo.cs", Map(10 -> 1)), - CoverageFileReport("foobar.cs", Map(10 -> 0, 20 -> 1)) + CoverageFileReport("bar.cs", 0, Map(10 -> 0)), + CoverageFileReport("foo.cs", 100, Map(10 -> 1)), + CoverageFileReport("foobar.cs", 50, Map(10 -> 0, 20 -> 1)) ) ) } diff --git a/coverage-parser/src/test/scala/com/codacy/parsers/PhpUnitXmlParserTest.scala b/coverage-parser/src/test/scala/com/codacy/parsers/PhpUnitXmlParserTest.scala index 5ddca647..131d80a7 100644 --- a/coverage-parser/src/test/scala/com/codacy/parsers/PhpUnitXmlParserTest.scala +++ b/coverage-parser/src/test/scala/com/codacy/parsers/PhpUnitXmlParserTest.scala @@ -44,6 +44,15 @@ class PhpUnitXmlParserTest extends WordSpec with BeforeAndAfterAll with Matchers reader shouldBe 'right } + "return a report with the expected total coverage" in { + val report = PhpUnitXmlParser + .parse(new File(rootPath), new File(validReport)) + .right + .value + + report.total shouldBe 69 + } + "return a report with the expected number of files" in { val report = PhpUnitXmlParser .parse(new File(rootPath), new File(validReport)) @@ -73,6 +82,25 @@ class PhpUnitXmlParserTest extends WordSpec with BeforeAndAfterAll with Matchers ).sorted } + "return a report with the expected file coverage" in { + val report = PhpUnitXmlParser + .parse(new File(rootPath), new File(validReport)) + .right + .value + + report.fileReports.find(_.filename.endsWith(configPhpFile)) match { + case None => fail(configPhpFile + " file is not present in the list of file reports") + case Some(fileReport) => + fileReport.total shouldBe 86 + } + + report.fileReports.find(_.filename.endsWith("CloverParser.php")) match { + case None => fail("CloverParser.php is not present in the list of file reports") + case Some(fileReport) => + fileReport.total shouldBe 95 + } + } + "return a report with the expected line coverage" in { val report = PhpUnitXmlParser .parse(new File(rootPath), new File(validReport)) diff --git a/integration-tests/mock-server-config.json b/integration-tests/mock-server-config.json index e08fbe46..55c56af7 100644 --- a/integration-tests/mock-server-config.json +++ b/integration-tests/mock-server-config.json @@ -1,73 +1,9 @@ { "httpRequest": { - "body": { - "fileReports": [ - { - "filename": "app/Experiment/FooBar.cs", - "coverage": { - "10": 1, - "21": 1, - "9": 1, - "13": 1, - "17": 1, - "19": 0, - "15": 0 - } - }, - { - "filename": "app/Tests/FooBarTests.cs", - "coverage": { - "10": 1, - "14": 1, - "29": 1, - "21": 1, - "13": 1, - "22": 1, - "27": 1, - "18": 1, - "31": 1, - "11": 1, - "26": 1, - "30": 1, - "19": 1 - } - }, - { - "filename": "app/Experiment/Program.cs", - "coverage": { - "8": 0, - "9": 0, - "10": 0 - } - }, - { - "filename": "app/Experiment/Bar.cs", - "coverage": { - "10": 0, - "14": 1, - "9": 1, - "12": 0, - "11": 0, - "8": 1, - "15": 1 - } - }, - { - "filename": "app/Experiment/Foo.cs", - "coverage": { - "8": 1, - "9": 1, - "10": 1, - "11": 1 - } - } - ] - } + "body": {"total":76,"fileReports":[{"filename":"app/Experiment/FooBar.cs","total":71,"coverage":{"10":1,"21":1,"9":1,"13":1,"17":1,"19":0,"15":0}},{"filename":"app/Tests/FooBarTests.cs","total":100,"coverage":{"10":1,"14":1,"29":1,"21":1,"13":1,"22":1,"27":1,"18":1,"31":1,"11":1,"26":1,"30":1,"19":1}},{"filename":"app/Experiment/Program.cs","total":0,"coverage":{"8":0,"9":0,"10":0}},{"filename":"app/Experiment/Bar.cs","total":57,"coverage":{"10":0,"14":1,"9":1,"12":0,"11":0,"8":1,"15":1}},{"filename":"app/Experiment/Foo.cs","total":100,"coverage":{"8":1,"9":1,"10":1,"11":1}}]} }, "httpResponse": { "statusCode": 200, - "body": { - "success": "Report sent!" - } + "body": {"success": "Report sent!"} } } diff --git a/src/test/scala/com/codacy/rules/ReportRulesSpec.scala b/src/test/scala/com/codacy/rules/ReportRulesSpec.scala index d88e697d..ce1a40a2 100644 --- a/src/test/scala/com/codacy/rules/ReportRulesSpec.scala +++ b/src/test/scala/com/codacy/rules/ReportRulesSpec.scala @@ -22,8 +22,8 @@ class ReportRulesSpec extends WordSpec with Matchers with PrivateMethodTester wi val conf = Report(baseConf, Some("Scala"), coverageReports = Some(coverageFiles), prefix = None, forceCoverageParser = None) - val coverageReport: CoverageReport = CoverageReport(Seq(CoverageFileReport("file.scala", Map(10 -> 1)))) - val noLanguageReport = CoverageReport(Seq.empty[CoverageFileReport]) + val coverageReport = CoverageReport(100, Seq(CoverageFileReport("file.scala", 100, Map(10 -> 1)))) + val noLanguageReport = CoverageReport(0, Seq.empty[CoverageFileReport]) val configRules = new ConfigurationRules(conf, sys.env) val validatedConfig = configRules.validatedConfig.right.value @@ -231,7 +231,7 @@ class ReportRulesSpec extends WordSpec with Matchers with PrivateMethodTester wi "storeReport" should { "not store report" in { - val emptyReport = CoverageReport(Seq.empty[CoverageFileReport]) + val emptyReport = CoverageReport(0, Seq.empty[CoverageFileReport]) val tempFile = File.createTempFile("storeReport", "not-store") val result = components.reportRules.storeReport(emptyReport, tempFile) @@ -240,7 +240,7 @@ class ReportRulesSpec extends WordSpec with Matchers with PrivateMethodTester wi "successfully store report" when { def storeValidReport() = { - val emptyReport = CoverageReport(List(CoverageFileReport("file-name", Map.empty))) + val emptyReport = CoverageReport(0, List(CoverageFileReport("file-name", 0, Map.empty))) val tempFile = File.createTempFile("storeReport", "not-store") components.reportRules.storeReport(emptyReport, tempFile) } diff --git a/src/test/scala/com/codacy/transformation/PathPrefixerSpec.scala b/src/test/scala/com/codacy/transformation/PathPrefixerSpec.scala index 45b44b15..7d7405a0 100644 --- a/src/test/scala/com/codacy/transformation/PathPrefixerSpec.scala +++ b/src/test/scala/com/codacy/transformation/PathPrefixerSpec.scala @@ -6,7 +6,8 @@ import org.scalatest._ class PathPrefixerSpec extends WordSpec with Matchers { val report = CoverageReport( - Seq(CoverageFileReport("Filename.scala", Map.empty), CoverageFileReport("OtherFile.scala", Map.empty)) + 83, + Seq(CoverageFileReport("Filename.scala", 24, Map.empty), CoverageFileReport("OtherFile.scala", 74, Map.empty)) ) "PathPrefixer" should {