From 3bd52576975d6d182580b314dee898cb76f97f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20R=C3=BCegg?= Date: Wed, 13 Apr 2016 14:04:05 +0200 Subject: [PATCH] Fixed #13 --- .../sonar/client/BitbucketClient.scala | 51 +++-- .../bitbucket/sonar/diff/GitDiffParser.scala | 4 +- .../diff/IssuesOnChangedLinesFilter.scala | 17 +- .../sonar/review/ReviewCommentsCreator.scala | 17 +- .../sonar/review/SonarReviewPostJob.scala | 4 +- .../bitbucket/sonar/utils/JsonUtils.scala | 2 + src/test/resources/diffs/7diffs-example.diff | 180 ++++++++++++++++++ .../sonar/diff/GitDiffParserSpec.scala | 28 +-- 8 files changed, 250 insertions(+), 53 deletions(-) create mode 100644 src/test/resources/diffs/7diffs-example.diff diff --git a/src/main/scala/ch/mibex/bitbucket/sonar/client/BitbucketClient.scala b/src/main/scala/ch/mibex/bitbucket/sonar/client/BitbucketClient.scala index 7d47bf2..d326695 100644 --- a/src/main/scala/ch/mibex/bitbucket/sonar/client/BitbucketClient.scala +++ b/src/main/scala/ch/mibex/bitbucket/sonar/client/BitbucketClient.scala @@ -3,10 +3,10 @@ package ch.mibex.bitbucket.sonar.client import javax.ws.rs.core.MediaType import ch.mibex.bitbucket.sonar.{SonarBBPlugin, SonarBBPluginConfig} -import ch.mibex.bitbucket.sonar.utils.{LogUtils, JsonUtils} +import ch.mibex.bitbucket.sonar.utils.{JsonUtils, LogUtils} import com.sun.jersey.api.client.config.{ClientConfig, DefaultClientConfig} import com.sun.jersey.api.client.filter.LoggingFilter -import com.sun.jersey.api.client.{Client, ClientResponse, UniformInterfaceException} +import com.sun.jersey.api.client.{Client, ClientResponse, UniformInterfaceException, WebResource} import org.slf4j.LoggerFactory import org.sonar.api.BatchComponent import org.sonar.api.batch.InstantiationStrategy @@ -75,6 +75,7 @@ class BitbucketClient(config: SonarBBPluginConfig) extends BatchComponent { } + // see https://bitbucket.org/site/master/issues/12567/amount-of-pull-request-comments-returned def findOwnPullRequestComments(pullRequest: PullRequest): Seq[PullRequestComment] = { def isFromUs(comment: Map[String, Any]): Boolean = { @@ -82,34 +83,30 @@ class BitbucketClient(config: SonarBBPluginConfig) extends BatchComponent { case Some(_) => config.teamName() case None => config.accountName() } - comment("user").asInstanceOf[Map[String, Any]]("username").asInstanceOf[String] equals userName + comment("author_info").asInstanceOf[Map[String, Any]]("username").asInstanceOf[String] equals userName } - - def fetchCommentsPage(start: Int): (Option[Int], Seq[PullRequestComment]) = - fetchPage(s"/pullrequests/${pullRequest.id}/comments", - response => - for (comment <- response("values").asInstanceOf[Seq[Map[String, Any]]] if isFromUs(comment)) - yield { - val pathAndLine = for { - inline <- comment.get("inline") map { _.asInstanceOf[Map[String, Any]] } - filePath <- inline.get("path") map { _.asInstanceOf[String] } - line <- inline.get("to") map { _.asInstanceOf[Int] } - } yield (filePath, line) - PullRequestComment( - commentId = comment("id").asInstanceOf[Int], - content = comment("content").asInstanceOf[Map[String, Any]]("raw").asInstanceOf[String], - filePath = pathAndLine.map { _._1 }, - line = pathAndLine.map { _._2 } - ) - }, - pageNr = start - ) - forEachResultPage(Seq[PullRequestComment](), (pageStart, comments: Seq[PullRequestComment]) => { - val (nextPageStart, newComments) = fetchCommentsPage(pageStart) - (nextPageStart, comments ++ newComments) - }) + val response = v1Api.path(s"/pullrequests/${pullRequest.id}/comments") + .accept(MediaType.APPLICATION_JSON) + .`type`(MediaType.APPLICATION_JSON) + .get(classOf[String]) + for { + comment <- JsonUtils.seqFromJson(response) if isFromUs(comment) + filePath <- comment.get("filename") map { _.asInstanceOf[String] } + line <- comment.get("line_to") map { _.asInstanceOf[Int] } + commentId <- comment.get("comment_id") map { _.asInstanceOf[Int] } + content <- comment.get("content").map { _.asInstanceOf[String] } + } yield PullRequestComment( + commentId = commentId, + content = content, + filePath = Option(filePath), + line = Option(line) + ) } + // create manually by + // curl -v -u YOUR_BITBUCKET_USER https://api.bitbucket.org/2.0/repositories/YOUR_USER_NAME/REPO_SLUG/pullrequests/PULL_REQUEST_ID/diff + // then copy the URL from the Location Header field in the HTTP response (LOCATION_URL below) and use that with the appended “?context=0” parameter for the second cURL: + // curl -u YOUR_BITBUCKET_USER LOCATION_URL?context=0 def getPullRequestDiff(pullRequest: PullRequest): String = { // we do not want to use GET and Jersey's auto-redirect here because otherwise the context param // is not passed to the new location diff --git a/src/main/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParser.scala b/src/main/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParser.scala index e04f238..c24c9f4 100644 --- a/src/main/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParser.scala +++ b/src/main/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParser.scala @@ -37,7 +37,7 @@ object GitDiffParser extends RegexParsers { } } - case class FromToRange(fromLineStart: Int, fromNumLines: Int = 0, toLineStart: Int, toNumLines: Int = 0) + case class FromToRange(fromLineStart: Int, fromNumLines: Option[Int], toLineStart: Int, toNumLines: Option[Int]) case class HunkHeader(fromToRange: FromToRange, context: Option[CtxLine]) @@ -129,7 +129,7 @@ object GitDiffParser extends RegexParsers { def fromToRange: Parser[FromToRange] = ("@@ " ~> "-" ~> num) ~ opt("," ~> num) ~ (" +" ~> num) ~ opt("," ~> num) <~ " @@" ^^ { case startLineFrom ~ optNumLinesFrom ~ startLineTo ~ optNumLinesTo => - FromToRange(startLineFrom, optNumLinesFrom.getOrElse(0), startLineTo, optNumLinesTo.getOrElse(0)) + FromToRange(startLineFrom, optNumLinesFrom, startLineTo, optNumLinesTo) } // @@ from-file-range to-file-range @@ [header] diff --git a/src/main/scala/ch/mibex/bitbucket/sonar/diff/IssuesOnChangedLinesFilter.scala b/src/main/scala/ch/mibex/bitbucket/sonar/diff/IssuesOnChangedLinesFilter.scala index bacd918..70ebf2e 100644 --- a/src/main/scala/ch/mibex/bitbucket/sonar/diff/IssuesOnChangedLinesFilter.scala +++ b/src/main/scala/ch/mibex/bitbucket/sonar/diff/IssuesOnChangedLinesFilter.scala @@ -4,7 +4,7 @@ import ch.mibex.bitbucket.sonar.SonarBBPlugin import ch.mibex.bitbucket.sonar.cache.InputFileCache import ch.mibex.bitbucket.sonar.client.{BitbucketClient, PullRequest} import ch.mibex.bitbucket.sonar.diff.GitDiffParser.{BinaryDiff, Diff, GitDiff} -import ch.mibex.bitbucket.sonar.utils.LogUtils +import org.slf4j.LoggerFactory import org.sonar.api.BatchComponent import org.sonar.api.batch.InstantiationStrategy import org.sonar.api.issue.Issue @@ -13,6 +13,7 @@ import org.sonar.api.issue.Issue @InstantiationStrategy(InstantiationStrategy.PER_BATCH) class IssuesOnChangedLinesFilter(bitbucketClient: BitbucketClient, inputFileCache: InputFileCache) extends BatchComponent { + private val logger = LoggerFactory.getLogger(getClass) def filter(pullRequest: PullRequest, newIssues: Seq[Issue]): Seq[Issue] = { val pullRequestDiff = bitbucketClient.getPullRequestDiff(pullRequest) @@ -20,6 +21,7 @@ class IssuesOnChangedLinesFilter(bitbucketClient: BitbucketClient, val issuesOnChangedLines = newIssues filter { i => val lineNr = Option(i.line()).flatMap(l => Option(l.toInt)).getOrElse(0) + inputFileCache.resolveRepoRelativePath(i.componentKey()) match { case Some(filePath) => val isIssueOnChangedLines = (diff: Diff) => diff match { @@ -40,12 +42,21 @@ class IssuesOnChangedLinesFilter(bitbucketClient: BitbucketClient, private def isOnChangedLine(lineNr: Int, diff: GitDiff) = diff.hunks.exists(c => lineNr >= c.hunkHeader.fromToRange.toLineStart - && lineNr <= c.hunkHeader.fromToRange.toLineStart + c.hunkHeader.fromToRange.toNumLines + //@@ -12 +11,0 @@ public class App + // - double d = Double.longBitsToDouble(i); // Noncompliant + //@@ -16 +14,0 @@ public class App + // - System.out.println( "Hello World! " + d); + //@@ -26,2 +27 @@ public class App + // - int i = 100023; + // - System.exit(-1); + // + int i = 100023; + && c.hunkHeader.fromToRange.toNumLines.getOrElse(1) > 0 + && lineNr <= c.hunkHeader.fromToRange.toLineStart + c.hunkHeader.fromToRange.toNumLines.getOrElse(0) ) private def parseOrFail(diff: String) = GitDiffParser.parse(diff) match { case Left(parsingFailure) => - throw new RuntimeException(s"${SonarBBPlugin.PluginLogPrefix} Failed to parse git diff: ${parsingFailure.reason}") + throw new RuntimeException(s"${SonarBBPlugin.PluginLogPrefix} Failed to parse diff: ${parsingFailure.reason}") case Right(gitDiffs) => gitDiffs } diff --git a/src/main/scala/ch/mibex/bitbucket/sonar/review/ReviewCommentsCreator.scala b/src/main/scala/ch/mibex/bitbucket/sonar/review/ReviewCommentsCreator.scala index 4e2c642..faee2fc 100644 --- a/src/main/scala/ch/mibex/bitbucket/sonar/review/ReviewCommentsCreator.scala +++ b/src/main/scala/ch/mibex/bitbucket/sonar/review/ReviewCommentsCreator.scala @@ -26,18 +26,18 @@ class ReviewCommentsCreator(projectIssues: ProjectIssues, def createOrUpdateComments(pullRequest: PullRequest, existingReviewComments: Seq[PullRequestComment], - pullRequestResults: PullRequestReviewResults) = { + pullRequestResults: PullRequestReviewResults): Map[Int, PullRequestComment] = { val commentsToBeAdded = processIssues(pullRequest, pullRequestResults) val (commentsByPathAndLine, commentsToDelete) = processExistingComments(existingReviewComments) commentsToBeAdded foreach { case (file, issuesByLine) => issuesByLine foreach { case (line, issues) => - commentsByPathAndLine.get(file) match { - case Some(x) if x.contains(line) => + case Some(commentsByLine) if commentsByLine.contains(line) => // already a comment on this line + val comment = commentsByPathAndLine(file)(line) - if (comment.content != issues.toString()) { + if (comment.content != issues.toString()) { // only update if comment is not equal to existing one updateComment(pullRequest, issues.toString(), comment) } @@ -57,6 +57,12 @@ class ReviewCommentsCreator(projectIssues: ProjectIssues, .withDefaultValue(new mutable.HashMap[Int, PullRequestComment]()) val reviewCommentsToBeDeleted = new mutable.HashMap[Int, PullRequestComment]() val inlineComments = existingReviewComments filter { _.isInline } + if (logger.isDebugEnabled) { + logger.debug(LogUtils.f(s"Found ${inlineComments.size} existing inline comments:")) + inlineComments foreach { c => + logger.debug(LogUtils.f(s" - ${c.filePath}:${c.line}: ${c.content}")) + } + } inlineComments foreach { c => reviewCommentsToBeDeleted += c.commentId -> c @@ -95,11 +101,12 @@ class ReviewCommentsCreator(projectIssues: ProjectIssues, } commentsToBeAdded(repoRelPath)(lineNr).append("\n\n" + SonarUtils.renderAsMarkdown(i, settings)) + + reviewResults.issueFound(i) case None => logger.debug(LogUtils.f(s"No path resolved for ${i.componentKey()}")) } - reviewResults.issueFound(i) } commentsToBeAdded.toMap diff --git a/src/main/scala/ch/mibex/bitbucket/sonar/review/SonarReviewPostJob.scala b/src/main/scala/ch/mibex/bitbucket/sonar/review/SonarReviewPostJob.scala index da7ebe7..53c7138 100644 --- a/src/main/scala/ch/mibex/bitbucket/sonar/review/SonarReviewPostJob.scala +++ b/src/main/scala/ch/mibex/bitbucket/sonar/review/SonarReviewPostJob.scala @@ -19,7 +19,7 @@ class SonarReviewPostJob(bitbucketClient: BitbucketClient, val ourComments = bitbucketClient.findOwnPullRequestComments(p) val report = new PullRequestReviewResults(pluginConfig) val commentsToDelete = reviewCommentsUpdater.createOrUpdateComments(p, ourComments, report) - deleteOutdatedComments(p, commentsToDelete) + deletePreviousComments(p, commentsToDelete) deletePreviousGlobalComments(p, ourComments) createGlobalComment(p, report) approveOrUnapproveIfEnabled(p, report) @@ -65,7 +65,7 @@ class SonarReviewPostJob(bitbucketClient: BitbucketClient, } } - private def deleteOutdatedComments(pullRequest: PullRequest, commentsToDelete: Map[Int, PullRequestComment]) { + private def deletePreviousComments(pullRequest: PullRequest, commentsToDelete: Map[Int, PullRequestComment]) { commentsToDelete foreach { case (commentId, comment) => if (comment.content.startsWith(SonarUtils.sonarMarkdownPrefix())) { bitbucketClient.deletePullRequestComment(pullRequest, commentId) diff --git a/src/main/scala/ch/mibex/bitbucket/sonar/utils/JsonUtils.scala b/src/main/scala/ch/mibex/bitbucket/sonar/utils/JsonUtils.scala index 4059a8a..0ffafa0 100644 --- a/src/main/scala/ch/mibex/bitbucket/sonar/utils/JsonUtils.scala +++ b/src/main/scala/ch/mibex/bitbucket/sonar/utils/JsonUtils.scala @@ -36,4 +36,6 @@ object JsonUtils { def mapFromJson(json: String): Map[String, Any] = json.parseJson.convertTo[Map[String, Any]] + def seqFromJson(json: String): Seq[Map[String, Any]] = json.parseJson.convertTo[Seq[Map[String, Any]]] + } diff --git a/src/test/resources/diffs/7diffs-example.diff b/src/test/resources/diffs/7diffs-example.diff new file mode 100644 index 0000000..6e87467 --- /dev/null +++ b/src/test/resources/diffs/7diffs-example.diff @@ -0,0 +1,180 @@ +diff --git a/README.txt b/README.txt +index 3853a30..9ba12b7 100755 +--- a/README.txt ++++ b/README.txt +@@ -1,0 +2 @@ Maven multi-module project. created a la http://stackoverflow.com/questions/6328 ++test +diff --git a/multimod/src/db/src/main/java/ch/mycompany/test/db/App.java b/multimod/src/db/src/main/java/ch/mycompany/test/db/App.java +index 57b70a8..492f073 100755 +--- a/multimod/src/db/src/main/java/ch/mycompany/test/db/App.java ++++ b/multimod/src/db/src/main/java/ch/mycompany/test/db/App.java +@@ -12 +11,0 @@ public class App +- double d = Double.longBitsToDouble(i); // Noncompliant +@@ -16 +14,0 @@ public class App +- System.out.println( "Hello World! " + d); +@@ -17,0 +16,7 @@ public class App ++ ++ public void foo () { ++ } ++ ++ private void foo3() { ++ // no idea why this is empty ++ } +diff --git a/multimod/src/db/src/main/java/ch/mycompany/test/db/App2.java b/multimod/src/db/src/main/java/ch/mycompany/test/db/App2.java +new file mode 100755 +index 0000000..66c3bc7 +--- /dev/null ++++ b/multimod/src/db/src/main/java/ch/mycompany/test/db/App2.java +@@ -0,0 +1,19 @@ ++package ch.mycompany.test.db; ++ ++/** ++ * Hello world! ++ * ++ */ ++public class App2 ++{ ++ public static void main( String[] args ) ++ { ++ new App2().foo3(); ++ } ++ ++ public void foo () { } ++ ++ private void foo3() { ++ // intentionally ++ } ++} +diff --git a/multimod/src/db/src/main/java/ch/mycompany/test/db/App3.java b/multimod/src/db/src/main/java/ch/mycompany/test/db/App3.java +new file mode 100755 +index 0000000..491e4e5 +--- /dev/null ++++ b/multimod/src/db/src/main/java/ch/mycompany/test/db/App3.java +@@ -0,0 +1,21 @@ ++package ch.mycompany.test.db; ++ ++/** ++ * Hello world! ++ */ ++public class App3 ++{ ++ public static void main( String[] args ) ++ { ++ int i = 42; ++ double g = Double.longBitsToDouble(i); // Noncompliant ++ System.out.println( "Hello World! " + g); ++ } ++ ++ public void foo () { ++ foo3(); ++ } ++ ++ private void foo3() { ++ } ++} +diff --git a/multimod/src/db/src/test/java/ch/mycompany/test/db/AppTest.java b/multimod/src/db/src/test/java/ch/mycompany/test/db/AppTest.java +deleted file mode 100755 +index 4545ecc..0000000 +--- a/multimod/src/db/src/test/java/ch/mycompany/test/db/AppTest.java ++++ /dev/null +@@ -1,40 +0,0 @@ +-package ch.mycompany.test.db; +- +-import junit.framework.Test; +-import junit.framework.TestCase; +-import junit.framework.TestSuite; +- +- +-/** +- * Unit test for simple App. +- */ +-public class AppTest +- extends TestCase +-{ +- /** +- * Create the test case +- * +- * @param testName name of the test case +- */ +- public AppTest( String testName ) +- { +- super( testName ); +- } +- +- /** +- * @return the suite of tests being tested +- */ +- public static Test suite() +- { +- return new TestSuite( AppTest.class ); +- } +- +- /** +- * Rigourous Test :-) +- */ +- public void testApp() +- { +- ch.mycompany.test.db.App.main(new String[]{}); +- assertTrue( true ); +- } +-} +diff --git a/multimod/src/gui/src/main/java/ch/mycompany/test/gui/App.java b/multimod/src/gui/src/main/java/ch/mycompany/test/gui/App.java +index 356f4b7..f7ee1b2 100755 +--- a/multimod/src/gui/src/main/java/ch/mycompany/test/gui/App.java ++++ b/multimod/src/gui/src/main/java/ch/mycompany/test/gui/App.java +@@ -8,0 +9 @@ public class App ++ double d = Double.longBitsToDouble(42); // Noncompliant +@@ -26,2 +27 @@ public class App +- int i = 100023; +- System.exit(-1); ++ int i = 100023; +@@ -31,2 +30,0 @@ public class App +- int baba = 5555; +- //TODO fix this +@@ -39 +37 @@ public class App +- double e = 2.718; ++ double e = 2.718; +@@ -40,0 +39 @@ public class App ++ +diff --git a/multimod/src/gui/src/main/java/ch/mycompany/test/gui/StashTag.java b/multimod/src/gui/src/main/java/ch/mycompany/test/gui/StashTag.java +new file mode 100755 +index 0000000..4ca9a70 +--- /dev/null ++++ b/multimod/src/gui/src/main/java/ch/mycompany/test/gui/StashTag.java +@@ -0,0 +1,24 @@ ++package ch.mycompany.test.gui; ++ ++public class StashTag { ++ private final String id; ++ private final String displayId; ++ ++ public StashTag(String id, String displayId) { ++ this.id = id; ++ this.displayId = displayId; ++ } ++ ++ // value is being used by Velocity template ++ @SuppressWarnings("UnusedDeclaration") ++ public String getId() { ++ return id; ++ } ++ ++ // value is being used by Velocity template ++ @SuppressWarnings("UnusedDeclaration") ++ public String getDisplayId() { ++ int i = 42; ++ return displayId; ++ } ++} +diff --git a/sonar.json b/sonar.json +index 69165e1..8713f0a 100644 +--- a/sonar.json ++++ b/sonar.json +@@ -0,0 +1 @@ ++<<<<<<< destination:d657f52f8f651d212c3399785bff035ae29bdd99 +@@ -3,0 +5,5 @@ ++======= ++{ ++ 'sonarHost': 'https://mrueegg.sonar.cloudbees.com', ++ 'sonarProjectKey': 'ch.mycompany.test:multimod:develop' ++>>>>>>> source:5079373fc88df023581a4982244c28a773824ec9 \ No newline at end of file diff --git a/src/test/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParserSpec.scala b/src/test/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParserSpec.scala index 0768781..c7c7bce 100644 --- a/src/test/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParserSpec.scala +++ b/src/test/scala/ch/mibex/bitbucket/sonar/diff/GitDiffParserSpec.scala @@ -177,7 +177,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat "@@ -1,8 +1,9 @@") .withResult( HunkHeader( - fromToRange = FromToRange(fromLineStart = 1, fromNumLines = 8, toLineStart = 1, toNumLines = 9), + fromToRange = FromToRange(fromLineStart = 1, fromNumLines = Some(8), toLineStart = 1, toNumLines = Some(9)), context = None ) ) @@ -188,7 +188,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat "@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)\n") .withResult( HunkHeader( - fromToRange = FromToRange(fromLineStart = 18, fromNumLines = 6, toLineStart = 19, toNumLines = 8), + fromToRange = FromToRange(fromLineStart = 18, fromNumLines = Some(6), toLineStart = 19, toNumLines = Some(8)), context = Some(CtxLine(line = "int cmd_http_fetch(int argc, const char **argv, const char *prefix)")) ) ) @@ -288,7 +288,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat index = Option(Index(fromHash = "57b70a8", toHash = "fc8f0d5", mode = Option(100755)))), hunks = List(Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 12, fromNumLines = 0, toLineStart = 12, toNumLines = 0), + fromToRange = FromToRange(fromLineStart = 12, fromNumLines = None, toLineStart = 12, toNumLines = None), context = Some(CtxLine("public class App")) ), changedLines = List( @@ -297,7 +297,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat ) ), Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 16, fromNumLines = 0, toLineStart = 16, toNumLines = 0), + fromToRange = FromToRange(fromLineStart = 16, fromNumLines = None, toLineStart = 16, toNumLines = None), context = Some(CtxLine("public class App")) ), changedLines = List( @@ -306,7 +306,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat ) ), Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 17, fromNumLines = 0, toLineStart = 18, toNumLines = 8), + fromToRange = FromToRange(fromLineStart = 17, fromNumLines = Some(0), toLineStart = 18, toNumLines = Some(8)), context = Some(CtxLine("public class App")) ), changedLines = List( @@ -384,7 +384,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat index = Option(Index(fromHash = "4545ecc", toHash = "0000000", mode = None))), hunks = List(Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 1, fromNumLines = 40, toLineStart = 0, toNumLines = 0), + fromToRange = FromToRange(fromLineStart = 1, fromNumLines = Some(40), toLineStart = 0, toNumLines = Some(0)), context = None ), changedLines = List( @@ -465,7 +465,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat index = Option(Index(fromHash = "356f4b7", toHash = "f5b8743", mode = Some(100755)))), hunks = List(Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 26, fromNumLines = 2, toLineStart = 26, toNumLines = 2), + fromToRange = FromToRange(fromLineStart = 26, fromNumLines = Some(2), toLineStart = 26, toNumLines = Some(2)), context = Some(CtxLine("public class App")) ), changedLines = List( @@ -476,7 +476,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat ) ), Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 40, fromNumLines = 0, toLineStart = 41, toNumLines = 7), + fromToRange = FromToRange(fromLineStart = 40, fromNumLines = Some(0), toLineStart = 41, toNumLines = Some(7)), context = Some(CtxLine("public class App")) ), changedLines = List( @@ -535,7 +535,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat index = Option(Index(fromHash = "f3e63d7", toHash = "e8f44ba", mode = Some(100644)))), hunks = List(Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 1, fromNumLines = 8, toLineStart = 1, toNumLines = 9), + fromToRange = FromToRange(fromLineStart = 1, fromNumLines = Some(8), toLineStart = 1, toNumLines = Some(9)), context = None ), changedLines = List( @@ -551,7 +551,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat ) ), Hunk( hunkHeader = HunkHeader( - fromToRange = FromToRange(fromLineStart = 18, fromNumLines = 6, toLineStart = 19, toNumLines = 8), + fromToRange = FromToRange(fromLineStart = 18, fromNumLines = Some(6), toLineStart = 19, toNumLines = Some(8)), context = Some(CtxLine("int cmd_http_fetch(int argc, const char **argv, const char *prefix)")) ), changedLines = List( @@ -606,7 +606,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat "Github issue #10 carriage return issue" in { allDiffs must succeedOn(readFile("/diffs/diff_pr_153_ko.diff.txt")) } - + "Github issue #8 failing diff" in { allDiffs must succeedOn(readFile("/diffs/failing-diff.txt")).withResult( List( @@ -616,7 +616,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat ExtendedDiffHeader(List(NewFileMode(100644)), Option(Index("0000000", "a244253", None))), List( Hunk( - HunkHeader(FromToRange(0, 0, 1, 5), None), + HunkHeader(FromToRange(0, Some(0), 1, Some(5)), None), List( AddedLine(""), AddedLine("lorealprotogoconnector.key=value"), @@ -640,7 +640,7 @@ class GitDiffParserSpec extends Specification with ParserMatchers with StringMat header = ExtendedDiffHeader(List(DeletedFileMode(100644)), Option(Index("27e8967", "0000000", None))), hunks = List( Hunk( - HunkHeader(FromToRange(1, 42, 0, 0), None), + HunkHeader(FromToRange(1, Some(42), 0, Some(0)), None), List( RemovedLine(""""), AddedLine(""""""),