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

wip: only files in git will be considered in the report, file name wi… #484

Closed
wants to merge 1 commit into from
Closed
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 @@ -2,9 +2,10 @@ package com.codacy.api.helpers.vcs

import java.io.File
import java.util.Date

import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.{Repository, RepositoryBuilder}
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.treewalk.TreeWalk

import scala.collection.JavaConverters._
import scala.util.Try
Expand Down Expand Up @@ -38,4 +39,27 @@ class GitClient(workDirectory: File) {
}
}

def getRepositoryFileNames(commitSha: String): Try[Seq[String]] = {
repositoryTry.map { rep =>
val git = new Git(rep)
val repo = git.getRepository
val commitId = repo.resolve(commitSha)
val revWalk = new RevWalk(repo)
val commit = revWalk.parseCommit(commitId)
val tree = commit.getTree
val treeWalk = new TreeWalk(repo)
treeWalk.addTree(tree)
treeWalk.setRecursive(true)

val result: Seq[String] =
if (treeWalk.next) {
Stream
.continually(treeWalk.getPathString)
.takeWhile(_ => treeWalk.next)
} else Seq.empty

result
}
}

}
7 changes: 5 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ name := "codacy-coverage-reporter"
libraryDependencies ++= Seq(
"com.github.alexarchambault" %% "case-app" % "1.2.0",
"org.wvlet.airframe" %% "airframe-log" % "22.3.0",
"com.lihaoyi" %% "ujson" % "1.5.0"
"com.lihaoyi" %% "ujson" % "1.5.0",
"commons-io" % "commons-io" % "2.6"
)

// Test dependencies
Expand Down Expand Up @@ -87,7 +88,8 @@ lazy val apiScala = project
libraryDependencies ++= Seq(
"com.typesafe.play" %% "play-json" % "2.8.2",
"org.scalaj" %% "scalaj-http" % "2.4.2",
"org.eclipse.jgit" % "org.eclipse.jgit" % "4.8.0.201706111038-r",
"org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.9.201909030838-r",
"org.wvlet.airframe" %% "airframe-log" % "22.3.0",
"org.scalatest" %% "scalatest" % "3.0.8" % Test
)
)
Expand All @@ -98,6 +100,7 @@ lazy val coverageParser = project
libraryDependencies ++= Seq(
"com.codacy" %% "codacy-plugins-api" % "5.2.0",
"org.scala-lang.modules" %% "scala-xml" % "1.2.0",
"org.wvlet.airframe" %% "airframe-log" % "22.3.0",
"org.scalatest" %% "scalatest" % "3.0.8" % Test
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import scala.util.Try
trait CoverageParser {
val name: String

def parse(rootProject: File, reportFile: File): Either[String, CoverageReport]
def parse(rootProject: File, reportFile: File, acceptedFiles: Seq[String] = Seq.empty): Either[String, CoverageReport]
}

object CoverageParser {
Expand All @@ -29,14 +29,15 @@ object CoverageParser {
GoParser
)

def parse(projectRoot: File, reportFile: File): Either[String, CoverageReport] = {
parse(projectRoot = projectRoot, reportFile = reportFile, None).map(_.report)
def parse(projectRoot: File, reportFile: File, acceptedFiles: Seq[String]): Either[String, CoverageReport] = {
parse(projectRoot = projectRoot, reportFile = reportFile, None, acceptedFiles).map(_.report)
}

def parse(
projectRoot: File,
reportFile: File,
forceParser: Option[CoverageParser]
forceParser: Option[CoverageParser],
acceptedFiles: Seq[String]
): Either[String, CoverageParserResult] = {
val isEmptyReport = {
// Just starting by detecting the simplest case: a single report file
Expand All @@ -50,7 +51,7 @@ object CoverageParser {

object ParsedCoverage {
def unapply(parser: CoverageParser): Option[CoverageParserResult] = {
parser.parse(projectRoot, reportFile).toOption.map(CoverageParserResult(_, parser))
parser.parse(projectRoot, reportFile, acceptedFiles).toOption.map(CoverageParserResult(_, parser))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ object CloverParser extends CoverageParser with XmlReportParser {

override val name: String = "Clover"

override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] = {
override def parse(
rootProject: File,
reportFile: File,
acceptedFiles: Seq[String]
): Either[String, CoverageReport] = {
parseReport(reportFile, s"Could not find tag hierarchy <$CoverageTag> <$ProjectTag> <$MetricsTag> tags") { node =>
parseReportNode(rootProject, node)
}
Expand All @@ -30,9 +34,9 @@ object CloverParser extends CoverageParser with XmlReportParser {
val rootPath = TextUtils.sanitiseFilename(rootProject.getAbsolutePath)

val coverageFiles = (report \\ "file").foldLeft[Either[String, Seq[CoverageFileReport]]](Right(List())) {
case (Right(accomulatedFileReports), fileTag) =>
case (Right(accumulatedFileReports), fileTag) =>
val fileReport = getCoverageFileReport(rootPath, fileTag)
fileReport.right.map(_ +: accomulatedFileReports)
fileReport.right.map(_ +: accumulatedFileReports)

case (Left(errorMessage), _) => Left(errorMessage)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object CoberturaParser extends CoverageParser with XmlReportParser {
private val CoverageTag = "coverage"
private val LineRateAttribute = "line-rate"

override def parse(projectRoot: File, reportFile: File): Either[String, CoverageReport] = {
override def parse(projectRoot: File, reportFile: File, acceptedFiles: Seq[String] = Seq.empty): Either[String, CoverageReport] = {
parseReport(reportFile, s"Could not find top level <$CoverageTag> tag") { node =>
Right(parseReportNode(projectRoot, node))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object DotcoverParser extends CoverageParser with XmlReportParser {
private val CoverageAttribute = "CoveragePercent"
private val CoveredAttribute = "Covered"

override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] =
override def parse(rootProject: File, reportFile: File, acceptedFiles: Seq[String] = Seq.empty): Either[String, CoverageReport] =
parseReport(reportFile, s"Could not find tag <$RootTag $CoverageAttribute=...>") { node =>
Right(parseReportNode(rootProject, node))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object GoParser extends CoverageParser {
//filename.go:lineFrom.column,lineTo.column numberOfStatements countOfStatements
final val regexpString = """([a-zA-Z\/\._\-\d]*):(\d+).*?,(\d+).* (\d+) (\d+)""".r

override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] = {
override def parse(rootProject: File, reportFile: File, acceptedFiles: Seq[String] = Seq.empty): Either[String, CoverageReport] = {
val report = Try(Source.fromFile(reportFile)) match {
case Success(lines) =>
Right(lines.getLines)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,61 +1,46 @@
package com.codacy.parsers.implementation

import java.io.File

import com.codacy.api._
import com.codacy.parsers.util.TextUtils
import com.codacy.parsers.util.FileNameMatcher
import com.codacy.parsers.{CoverageParser, XmlReportParser}
import wvlet.log.LogSupport

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

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

object JacocoParser extends CoverageParser with XmlReportParser {
object JacocoParser extends CoverageParser with XmlReportParser with LogSupport {

override val name: String = "Jacoco"

private val ReportTag = "report"

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

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

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

private def parseReportNode(projectRoot: File, report: NodeSeq): Either[String, CoverageReport] = {
val projectRootStr: String = TextUtils.sanitiseFilename(projectRoot.getAbsolutePath)
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(filesCoverage)
}
}

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 parseReportNode(report: NodeSeq, acceptedFiles: Seq[String]): Either[String, CoverageReport] = {
val filesCoverage = for {
pkg <- report \\ "package"
packageName = pkg \@ "name"
sourceFile <- pkg \\ "sourcefile"
filename = s"$packageName/${sourceFile \@ "name"}"
actualName <- FileNameMatcher
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be moved to a more generic place, after the reports are done, so that we don't have to implement for each parser

.matchAndReturnName(filename, acceptedFiles)
.map(Some(_))
.getOrElse({
logger.warn(s"File: $filename will be discarded and will not be considered for coverage calculation")
None
})
} yield lineCoverage(actualName, sourceFile)

Right(CoverageReport(filesCoverage))
}

private def lineCoverage(filename: String, fileNode: Node): CoverageFileReport = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ object LCOVParser extends CoverageParser {
final val SF = "SF:"
final val DA = "DA:"

override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] = {
override def parse(rootProject: File, reportFile: File, acceptedFiles: Seq[String] = Seq.empty): Either[String, CoverageReport] = {
val report = Try(Source.fromFile(reportFile)) match {
// most reports are XML, and we want to ensure the LCOV parser won't mishandle it and return an empty result
case Success(lines) if Try(XMLoader.loadFile(reportFile)).isSuccess =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ object OpenCoverParser extends CoverageParser with XmlReportParser {

override val name: String = "OpenCover"

override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] =
override def parse(rootProject: File, reportFile: File, acceptedFiles: Seq[String] = Seq.empty): Either[String, CoverageReport] =
parseReport(reportFile, s"Could not find tag <$RootTag>") { node =>
Right(parseReportNode(node, TextUtils.sanitiseFilename(rootProject.getAbsolutePath)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ object PhpUnitXmlParser extends CoverageParser with XmlReportParser {
private val DirectoryTag = "directory"
private val XmlParseErrorMessage = s"Could not find top level <$PhpUnitTag> tag";

override def parse(rootProject: File, reportFile: File): Either[String, CoverageReport] =
override def parse(
rootProject: File,
reportFile: File,
acceptedFiles: Seq[String] = Seq.empty
): Either[String, CoverageReport] =
parseReport(reportFile, XmlParseErrorMessage) {
parseReportNode(rootProject, _, reportFile.getParent)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.codacy.parsers.util

import java.nio.file.Paths
import scala.util.Try

object FileNameMatcher {

def matchAndReturnName(filename: String, fileNames: Seq[String]): Option[String] = {

fileNames.find(name => isTheSameFile(filename, name))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To achieve parity with the already implemented mechanisms in our code base (coverage-service, engine) there is still missing a part where we sort by similarity (see code base)


}

private def getFilenameFromPath(filename: String): String = {
Try(Paths.get(filename).getFileName.toString).getOrElse(filename)
}

private def haveSameName(file: String, covFile: String): Boolean =
getFilenameFromPath(file) == getFilenameFromPath(covFile)

private def haveSamePath(file: String, covFile: String): Boolean =
file == covFile

private def fileEndsWithReportPath(file: String, covFile: String): Boolean =
file.endsWith(covFile)

private def reportEndsWithFilePath(file: String, covFile: String): Boolean =
covFile.endsWith(file)

private def isTheSameFile(file: String, covFile: String): Boolean = {

haveSameName(file, covFile) && (haveSamePath(file, covFile) ||
fileEndsWithReportPath(file, covFile) ||
reportEndsWithFilePath(file, covFile))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CoverageParserFactoryTest extends WordSpec with BeforeAndAfterAll with Mat
)

CoverageParser
.parse(new File("."), new File("coverage-parser/src/test/resources/test_cobertura.xml")) shouldEqual Right(
.parse(new File("."), new File("coverage-parser/src/test/resources/test_cobertura.xml"), Seq.empty) shouldEqual Right(
expectedReport
)
}
Expand All @@ -37,14 +37,17 @@ class CoverageParserFactoryTest extends WordSpec with BeforeAndAfterAll with Mat
)
)

val acceptedFiles =
Seq("org/eluder/coverage/sample/InnerClassCoverage.java", "org/eluder/coverage/sample/SimpleCoverage.java")

CoverageParser
.parse(new File("."), new File("coverage-parser/src/test/resources/test_jacoco.xml")) shouldEqual Right(
.parse(new File("."), new File("coverage-parser/src/test/resources/test_jacoco.xml"), acceptedFiles) shouldEqual Right(
expectedReport
)
}

"fail to get invalid report" in {
CoverageParser.parse(new File("."), new File("invalid_report.xml")) shouldEqual Left(
CoverageParser.parse(new File("."), new File("invalid_report.xml"), Seq.empty) shouldEqual Left(
"Could not parse report, unrecognized report format (tried: Cobertura, Jacoco, Clover, OpenCover, DotCover, PHPUnit, LCOV, Go)"
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,25 @@ class CoverageParserTest extends WordSpec with BeforeAndAfterAll with Matchers w
"parse" should {
"return the specific error" when {
"the file cannot be parsed with a specific parser" in {
val reader = CoverageParser.parse(new File("."), new File(coberturaReportPath), Some(CloverParser))
val reader = CoverageParser.parse(new File("."), new File(coberturaReportPath), Some(CloverParser), Seq.empty)

reader shouldBe 'left
}
"the file cannot be parsed with another specific parser" in {
val reader = CoverageParser.parse(new File("."), new File(coberturaReportPath), Some(LCOVParser))
val reader = CoverageParser.parse(new File("."), new File(coberturaReportPath), Some(LCOVParser), Seq.empty)

reader shouldBe 'left
}
}
"return a valid result" when {
"file and format are matching cobertura" in {
val reader = CoverageParser.parse(new File("."), new File(coberturaReportPath), Some(CoberturaParser))
val reader =
CoverageParser.parse(new File("."), new File(coberturaReportPath), Some(CoberturaParser), Seq.empty)

reader shouldBe 'right
}
"file and format are matching clover" in {
val reader = CoverageParser.parse(new File("."), new File(cloverReportPath), Some(CloverParser))
val reader = CoverageParser.parse(new File("."), new File(cloverReportPath), Some(CloverParser), Seq.empty)

reader shouldBe 'right
}
Expand Down
Loading