Title: [87641] trunk/Tools
Revision
87641
Author
[email protected]
Date
2011-05-29 12:38:46 -0700 (Sun, 29 May 2011)

Log Message

2011-05-29  Daniel Bates  <[email protected]>

        Reviewed by David Kilzer.

        REGRESSION (r86515): svn-apply ignores diffs that omit line count in chunk range
        https://bugs.webkit.org/show_bug.cgi?id=61162

        Fixes an issue where svn-apply may ignore a diff that contains a chunk range line
        that omits a line count. In particular, the chunk range regular _expression_ does
        not match a chunk range line that omits a line count. GNU diff(1) will omit the
        line count in the chunk range if the line count is exactly 1. For example, appending
        a new line to the end of an existing file F that contains exactly one line of text will
        be represented in a diff with a chunk range line that omits the line count for F.

        * Scripts/VCSUtils.pm:
          (parseChunkRange): Added.
        * Scripts/webkitperl/VCSUtils_unittest/parseChunkRange.pl: Added.
        * Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl:
          - Added unit test "Git: Append new line to the end of an existing file".

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (87640 => 87641)


--- trunk/Tools/ChangeLog	2011-05-29 19:13:46 UTC (rev 87640)
+++ trunk/Tools/ChangeLog	2011-05-29 19:38:46 UTC (rev 87641)
@@ -1,3 +1,23 @@
+2011-05-29  Daniel Bates  <[email protected]>
+
+        Reviewed by David Kilzer.
+
+        REGRESSION (r86515): svn-apply ignores diffs that omit line count in chunk range
+        https://bugs.webkit.org/show_bug.cgi?id=61162
+
+        Fixes an issue where svn-apply may ignore a diff that contains a chunk range line
+        that omits a line count. In particular, the chunk range regular _expression_ does
+        not match a chunk range line that omits a line count. GNU diff(1) will omit the
+        line count in the chunk range if the line count is exactly 1. For example, appending
+        a new line to the end of an existing file F that contains exactly one line of text will
+        be represented in a diff with a chunk range line that omits the line count for F.
+
+        * Scripts/VCSUtils.pm:
+          (parseChunkRange): Added.
+        * Scripts/webkitperl/VCSUtils_unittest/parseChunkRange.pl: Added.
+        * Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl:
+          - Added unit test "Git: Append new line to the end of an existing file".
+
 2011-05-28  Adam Barth  <[email protected]>
 
         Reviewed by Eric Seidel.

Modified: trunk/Tools/Scripts/VCSUtils.pm (87640 => 87641)


--- trunk/Tools/Scripts/VCSUtils.pm	2011-05-29 19:13:46 UTC (rev 87640)
+++ trunk/Tools/Scripts/VCSUtils.pm	2011-05-29 19:38:46 UTC (rev 87641)
@@ -68,6 +68,7 @@
         &makeFilePathRelative
         &mergeChangeLogs
         &normalizePath
+        &parseChunkRange
         &parseFirstEOL
         &parsePatch
         &pathRelativeToSVNRepositoryRootForPath
@@ -98,7 +99,6 @@
 # Project time zone for Cupertino, CA, US
 my $changeLogTimeZone = "PST8PDT";
 
-my $chunkRangeRegEx = qr#^\@\@ -(\d+),(\d+) \+\d+,(\d+) \@\@#; # e.g. "@@ -2,6 +2,18 @@" or "@@ -2,6 +2,18 @@ foo()"
 my $gitDiffStartRegEx = qr#^diff --git (\w/)?(.+) (\w/)?([^\r\n]+)#;
 my $svnDiffStartRegEx = qr#^Index: ([^\r\n]+)#;
 my $svnPropertiesStartRegEx = qr#^Property changes on: ([^\r\n]+)#; # $1 is normally the same as the index path.
@@ -485,6 +485,42 @@
     return $eol;
 }
 
+# Parses a chunk range line into its components.
+#
+# A chunk range line has the form: @@ -L_1,N_1 +L_2,N_2 @@, where the pairs (L_1, N_1),
+# (L_2, N_2) are ranges that represent the starting line number and line count in the
+# original file and new file, respectively.
+#
+# Note, some versions of GNU diff may omit the comma and trailing line count (e.g. N_1),
+# in which case the omitted line count defaults to 1. For example, GNU diff may output
+# @@ -1 +1 @@, which is equivalent to @@ -1,1 +1,1 @@.
+#
+# This subroutine returns undef if given an invalid or malformed chunk range.
+#
+# Args:
+#   $line: the line to parse.
+#
+# Returns $chunkRangeHashRef
+#   $chunkRangeHashRef: a hash reference representing the parts of a chunk range, as follows--
+#     startingLine: the starting line in the original file.
+#     lineCount: the line count in the original file.
+#     newStartingLine: the new starting line in the new file.
+#     newLineCount: the new line count in the new file.
+sub parseChunkRange($)
+{
+    my ($line) = @_;
+    my $chunkRangeRegEx = qr#^\@\@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? \@\@#;
+    if ($line !~ /$chunkRangeRegEx/) {
+        return;
+    }
+    my %chunkRange;
+    $chunkRange{startingLine} = $1;
+    $chunkRange{lineCount} = defined($2) ? $3 : 1;
+    $chunkRange{newStartingLine} = $4;
+    $chunkRange{newLineCount} = defined($5) ? $6 : 1;
+    return \%chunkRange;
+}
+
 sub svnStatus($)
 {
     my ($fullPath) = @_;
@@ -905,8 +941,9 @@
         }
         if ($line !~ $headerStartRegEx) {
             # Then we are in the body of the diff.
-            $numTextChunks += $line =~ /$chunkRangeRegEx/;
-            if ($indexPathEOL && $line !~ /$chunkRangeRegEx/) {
+            my $isChunkRange = defined(parseChunkRange($line));
+            $numTextChunks += 1 if $isChunkRange;
+            if ($indexPathEOL && !$isChunkRange) {
                 # The chunk range is part of the body of the diff, but its line endings should't be
                 # modified or patch(1) will complain. So, we only modify non-chunk range lines.
                 $line =~ s/\r\n|\r|\n/$indexPathEOL/g;
@@ -1500,15 +1537,16 @@
     $deletedLineCount += $dateStartIndex - $chunkStartIndex;
 
     # Update the initial chunk range.
-    if ($lines[$chunkStartIndex - 1] !~ /$chunkRangeRegEx/) {
+    my $chunkRangeHashRef = parseChunkRange($lines[$chunkStartIndex - 1]);
+    if (!$chunkRangeHashRef) {
         # FIXME: Handle errors differently from ChangeLog files that
         # are okay but should not be altered. That way we can find out
         # if improvements to the script ever become necessary.
         $changeLogHashRef{patch} = $patch; # Error: unexpected patch string format.
         return \%changeLogHashRef;
     }
-    my $oldSourceLineCount = $2;
-    my $oldTargetLineCount = $3;
+    my $oldSourceLineCount = $chunkRangeHashRef->{lineCount};
+    my $oldTargetLineCount = $chunkRangeHashRef->{newLineCount};
 
     my $sourceLineCount = $oldSourceLineCount + @overlappingLines - $deletedLineCount;
     my $targetLineCount = $oldTargetLineCount + @overlappingLines - $deletedLineCount;

Added: trunk/Tools/Scripts/webkitperl/VCSUtils_unittest/parseChunkRange.pl (0 => 87641)


--- trunk/Tools/Scripts/webkitperl/VCSUtils_unittest/parseChunkRange.pl	                        (rev 0)
+++ trunk/Tools/Scripts/webkitperl/VCSUtils_unittest/parseChunkRange.pl	2011-05-29 19:38:46 UTC (rev 87641)
@@ -0,0 +1,237 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2011 Research In Motion Limited. All rights reserved.
+# Copyright (C) 2010 Chris Jerdonek ([email protected])
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+# its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Unit tests of VCSUtils::parseChunkRange().
+
+use strict;
+use warnings;
+
+use Test::More;
+use VCSUtils;
+
+my @testCaseHashRefs = (
+###
+# Invalid and malformed chunk range
+##
+# FIXME: We should make this set of tests more comprehensive.
+{   # New test
+    testName => "[invalid] Empty string",
+    inputText => "",
+    expectedReturn => []
+},
+{   # New test
+    testName => "[invalid] Bogus chunk range",
+    inputText => "@@ this is not valid @@",
+    expectedReturn => []
+},
+{   # New test
+    testName => "[invalid] Chunk range missing -/+ prefix",
+    inputText => "@@ 0,0 1,4 @@",
+    expectedReturn => []
+},
+{   # New test
+    testName => "[invalid] Chunk range missing commas",
+    inputText => "@@ -0 0 +1 4 @@",
+    expectedReturn => []
+},
+{   # New test
+    testName => "[invalid] Chunk range with swapped old and rew ranges",
+    inputText => "@@ +0,0 -1,4 @@",
+    expectedReturn => []
+},
+{   # New test
+    testName => "[invalid] Chunk range with leading junk",
+    inputText => "leading junk @@ -0,0 +1,4 @@",
+    expectedReturn => []
+},
+###
+#  Simple test cases
+##
+{   # New test
+    testName => "Line count is 0",
+    inputText => "@@ -0,0 +1,4 @@",
+    expectedReturn => [
+{
+    startingLine => 0,
+    lineCount => 0,
+    newStartingLine => 1,
+    newLineCount => 4,
+}
+]
+},
+{   # New test
+    testName => "Line count is 1",
+    inputText => "@@ -1 +1,4 @@",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 1,
+    newStartingLine => 1,
+    newLineCount => 4,
+}
+]
+},
+{   # New test
+    testName => "Both original and new line count is 1",
+    inputText => "@@ -1 +1 @@",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 1,
+    newStartingLine => 1,
+    newLineCount => 1,
+}
+]
+},
+{   # New test
+    testName => "Line count and new line count > 1",
+    inputText => "@@ -1,2 +1,4 @@",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 2,
+    newStartingLine => 1,
+    newLineCount => 4,
+}
+]
+},
+{   # New test
+    testName => "New line count is 0",
+    inputText => "@@ -1,4 +0,0 @@",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 4,
+    newStartingLine => 0,
+    newLineCount => 0,
+}
+]
+},
+{   # New test
+    testName => "New line count is 1",
+    inputText => "@@ -1,4 +1 @@",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 4,
+    newStartingLine => 1,
+    newLineCount => 1,
+}
+]
+},
+###
+#  Chunk range followed by ending junk
+##
+{   # New test
+    testName => "Line count is 0 and chunk range has ending junk",
+    inputText => "@@ -0,0 +1,4 @@ foo()",
+    expectedReturn => [
+{
+    startingLine => 0,
+    lineCount => 0,
+    newStartingLine => 1,
+    newLineCount => 4,
+}
+]
+},
+{   # New test
+    testName => "Line count is 1 and chunk range has ending junk",
+    inputText => "@@ -1 +1,4 @@ foo()",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 1,
+    newStartingLine => 1,
+    newLineCount => 4,
+}
+]
+},
+{   # New test
+    testName => "Both original and new line count is 1 and chunk range has ending junk",
+    inputText => "@@ -1 +1 @@ foo()",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 1,
+    newStartingLine => 1,
+    newLineCount => 1,
+}
+]
+},
+{   # New test
+    testName => "Line count and new line count > 1 and chunk range has ending junk",
+    inputText => "@@ -1,2 +1,4 @@ foo()",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 2,
+    newStartingLine => 1,
+    newLineCount => 4,
+}
+]
+},
+{   # New test
+    testName => "New line count is 0 and chunk range has ending junk",
+    inputText => "@@ -1,4 +0,0 @@ foo()",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 4,
+    newStartingLine => 0,
+    newLineCount => 0,
+}
+]
+},
+{   # New test
+    testName => "New line count is 1 and chunk range has ending junk",
+    inputText => "@@ -1,4 +1 @@ foo()",
+    expectedReturn => [
+{
+    startingLine => 1,
+    lineCount => 4,
+    newStartingLine => 1,
+    newLineCount => 1,
+}
+]
+},
+);
+
+my $testCasesCount = @testCaseHashRefs;
+plan(tests => $testCasesCount);
+
+foreach my $testCase (@testCaseHashRefs) {
+    my $testNameStart = "parseChunkRange(): $testCase->{testName}: comparing";
+
+    my @got = VCSUtils::parseChunkRange($testCase->{inputText});
+    my $expectedReturn = $testCase->{expectedReturn};
+
+    is_deeply(\@got, $expectedReturn, "$testNameStart return value.");
+}

Modified: trunk/Tools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl (87640 => 87641)


--- trunk/Tools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl	2011-05-29 19:13:46 UTC (rev 87640)
+++ trunk/Tools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl	2011-05-29 19:38:46 UTC (rev 87641)
@@ -994,6 +994,36 @@
 undef],
     expectedNextLine => undef,
 },
+{
+    # New test
+    diffName => "Git: Append new line to the end of an existing file",
+    inputText => <<'END',
+diff --git a/foo b/foo
+index 863339f..db418b2 100644
+--- a/foo
++++ b/foo
+@@ -1 +1,2 @@
+ Passed
++
+END
+    expectedReturn => [
+[{
+    svnConvertedText =>  <<'END',
+Index: foo
+index 863339f..db418b2 100644
+--- foo
++++ foo
+@@ -1 +1,2 @@
+ Passed
++
+END
+    indexPath => "foo",
+    isGit => 1,
+    numTextChunks => 1,
+}],
+undef],
+    expectedNextLine => undef,
+},
 {   # New test
     diffName => "Git: new file",
     inputText => <<'END',
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to