prepare-ChangeLog reports function above deleted function as deleted; uninitialized...
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 May 2015 23:56:24 +0000 (23:56 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 May 2015 23:56:24 +0000 (23:56 +0000)
when staged non-empty file for commit
https://bugs.webkit.org/show_bug.cgi?id=145082

Reviewed by Darin Adler.

Fixes two issues when running prepare-ChangeLog:
    1. The function above a deleted function is reported as changed.
    2. With a Git checkout of WebKit, a Perl uninitialized value warning is emitted when
    a new non-empty file is staged for commit (e.g. extractLineRangeBeforeAndAfterChange.pl,
    included in this patch).

Simplify code by using unified diff parsing logic for both SVN and Git support. Currently
prepare-ChangeLog has logic to parse normal diff- and unified diff- chunk range lines as
generated by `svn diff` and `git diff`, respectively. The logic for parsing these formats
has correctness issues. We should make use of the VCSUtil::parseChunkRange() to parse
chunk range lines of a unified diff as opposed to having specialized logic in prepare-ChangeLog.
VCSUtil::parseChunkRange() has existing test coverage.

* Scripts/prepare-ChangeLog:
(generateFunctionLists): Only add a line range to %line_ranges_before_changed, %line_ranges_after_changed
when the beginning line number, ending line number >= 1. Modified for-loop condition to iterate over
all the files represented by %line_ranges_before_changed and %line_ranges_after_changed so that we
examine files that only have deletions. Currently this works as a side effect of the behavior of
extractLineRangeAfterChange(), which always returns a well-formed (though nonsensical) line range for
a change that represents a deletion (e.g. extractLineRangeAfterChange("@@ -166,6 +165,0 @@") => [165, 165]).
(diffCommand): Generate a unified diff instead of a normal diff when using a SVN checkout of WebKit.
(extractLineRangeAfterChange): Remove logic to parse a normal diff chunk range line and write
the logic to parse a unified diff chunk range line in terms of VCSUtil::parseChunkRange().
We return (-1, -1) when the change represents a deletion.
(extractLineRangeBeforeChange): Remove logic to parse a normal diff chunk range line and write
the logic to parse a unified diff chunk range line in terms of VCSUtil::parseChunkRange().
We return (-1, -1) when the change represents an addition.
* Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl: Added;
unit tests.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@184521 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/Scripts/prepare-ChangeLog
Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl [new file with mode: 0644]

index 64cd95d..2e73cd7 100644 (file)
@@ -1,3 +1,41 @@
+2015-05-18  Daniel Bates  <dabates@apple.com>
+
+        prepare-ChangeLog reports function above deleted function as deleted; uninitialized value warning
+        when staged non-empty file for commit
+        https://bugs.webkit.org/show_bug.cgi?id=145082
+
+        Reviewed by Darin Adler.
+
+        Fixes two issues when running prepare-ChangeLog:
+            1. The function above a deleted function is reported as changed.
+            2. With a Git checkout of WebKit, a Perl uninitialized value warning is emitted when
+            a new non-empty file is staged for commit (e.g. extractLineRangeBeforeAndAfterChange.pl,
+            included in this patch).
+
+        Simplify code by using unified diff parsing logic for both SVN and Git support. Currently
+        prepare-ChangeLog has logic to parse normal diff- and unified diff- chunk range lines as
+        generated by `svn diff` and `git diff`, respectively. The logic for parsing these formats
+        has correctness issues. We should make use of the VCSUtil::parseChunkRange() to parse
+        chunk range lines of a unified diff as opposed to having specialized logic in prepare-ChangeLog.
+        VCSUtil::parseChunkRange() has existing test coverage.
+
+        * Scripts/prepare-ChangeLog:
+        (generateFunctionLists): Only add a line range to %line_ranges_before_changed, %line_ranges_after_changed
+        when the beginning line number, ending line number >= 1. Modified for-loop condition to iterate over
+        all the files represented by %line_ranges_before_changed and %line_ranges_after_changed so that we
+        examine files that only have deletions. Currently this works as a side effect of the behavior of
+        extractLineRangeAfterChange(), which always returns a well-formed (though nonsensical) line range for
+        a change that represents a deletion (e.g. extractLineRangeAfterChange("@@ -166,6 +165,0 @@") => [165, 165]).
+        (diffCommand): Generate a unified diff instead of a normal diff when using a SVN checkout of WebKit.
+        (extractLineRangeAfterChange): Remove logic to parse a normal diff chunk range line and write
+        the logic to parse a unified diff chunk range line in terms of VCSUtil::parseChunkRange().
+        We return (-1, -1) when the change represents a deletion.
+        (extractLineRangeBeforeChange): Remove logic to parse a normal diff chunk range line and write
+        the logic to parse a unified diff chunk range line in terms of VCSUtil::parseChunkRange().
+        We return (-1, -1) when the change represents an addition.
+        * Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl: Added;
+        unit tests.
+
 2015-05-18  Anders Carlsson  <andersca@apple.com>
 
         Add ATS keys to MiniBrowser
index 6821aee..9def1af 100755 (executable)
@@ -281,13 +281,13 @@ sub generateFunctionLists($$$$$)
             $file = makeFilePathRelative($1) if $_ =~ diffHeaderFormat();
             if (defined $file) {
                 my ($before_start, $before_end) = extractLineRangeBeforeChange($_);
-                if ($before_start >= 0 && $before_end >= $before_start) {
+                if ($before_start >= 1 && $before_end >= 1) {
                     push @{$line_ranges_before_changed{$file}}, [ $before_start, $before_end ];
                 } elsif (/DO_NOT_COMMIT/) {
                     print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
                 }
                 my ($after_start, $after_end) = extractLineRangeAfterChange($_);
-                if ($after_start >= 0 && $after_end >= 0 && $after_end >= $after_start) {
+                if ($after_start >= 1 && $after_end >= 1) {
                     push @{$line_ranges_after_changed{$file}}, [ $after_start, $after_end ];
                 } elsif (/DO_NOT_COMMIT/) {
                     print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
@@ -299,7 +299,8 @@ sub generateFunctionLists($$$$$)
 
     # For each source file, convert line range to function list.
     print STDERR "  Extracting affected function names from source files.\n";
-    foreach my $file (keys %line_ranges_after_changed) {
+    my %filesToExamine = map { $_ => 1 } (keys(%line_ranges_before_changed), keys(%line_ranges_after_changed));
+    foreach my $file (keys %filesToExamine) {
         my %saw_function;
         # Find all the functions in the file.
         if ($line_ranges_after_changed{$file}) {
@@ -1772,7 +1773,7 @@ sub diffCommand($$$$)
     if (isSVN()) {
         my @escapedPaths = map(escapeSubversionPath($_), @$paths);
         my $escapedPathsString = "'" . join("' '", @escapedPaths) . "'";
-        $command = SVN . " diff --diff-cmd diff -x -N $escapedPathsString";
+        $command = SVN . " diff --diff-cmd diff -x -u $escapedPathsString";
     } elsif (isGit()) {
         my $pathsString = "'" . join("' '", @$paths) . "'"; 
         $command = GIT . " diff --no-ext-diff -U0 " . diffFromToString($gitCommit, $gitIndex, $mergeBase);
@@ -2100,35 +2101,23 @@ sub propertyChangeDescription($)
 sub extractLineRangeAfterChange($)
 {
     my ($string) = @_;
-
-    my ($start, $end) = (-1, -1);
-
-    if (isSVN() && $string =~ /^\d+(,\d+)?[acd](\d+)(,(\d+))?/) {
-        $start = $2;
-        $end = $4 || $2;
-    } elsif (isGit() && $string =~ /^@@ -\d+(,\d+)? \+(\d+)(,(\d+))? @@/) {
-        $start = $2;
-        $end = defined($4) ? max($start + $4 - 1, $start) : $start;
+    my $chunkRange = parseChunkRange($string);
+    if (!$chunkRange || !$chunkRange->{newStartingLine} || !$chunkRange->{newLineCount}) {
+         # Deletion; no lines exist after change.
+        return (-1, -1);
     }
-
-    return ($start, $end);
+    return ($chunkRange->{newStartingLine}, $chunkRange->{newStartingLine} + $chunkRange->{newLineCount} - 1);
 }
 
 sub extractLineRangeBeforeChange($)
 {
     my ($string) = @_;
-
-    my ($start, $end) = (-1, -1);
-
-    if (isSVN() && $string =~ /^(\d+)(,(\d+))?([acd])\d+(,\d+)?/) {
-        $start = $1;
-        $end = $3 || $1 if $4 ne "a";
-    } elsif (isGit() && $string =~ /^@@ -(\d+)(,(\d+))? \+\d+(,\d+)? @@/) {
-        $start = $1;
-        $end = defined($3) ? max($start + $3 - 1, $start) : $start;
+    my $chunkRange = parseChunkRange($string);
+    if (!$chunkRange || !$chunkRange->{startingLine} || !$chunkRange->{lineCount}) {
+        # Addition; no lines existed before change.
+        return (-1, -1);
     }
-
-    return ($start, $end);
+    return ($chunkRange->{startingLine}, $chunkRange->{startingLine} + $chunkRange->{lineCount} - 1);
 }
 
 sub testListForChangeLog(@)
diff --git a/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl b/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl
new file mode 100644 (file)
index 0000000..4e69a83
--- /dev/null
@@ -0,0 +1,206 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2015 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  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.
+# 
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+
+use strict;
+use warnings;
+
+use Test::More;
+use FindBin;
+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog);
+
+my @testCaseHashRefs = (
+####
+#  Additions
+##
+{
+    # New test
+    testName => "add single line to the beginning of the file",
+    inputText => "@@ -0,0 +1 @@",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [1, 1],
+    }
+},
+{
+    # New test
+    testName => "add two lines to the beginning of the file",
+    inputText => "@@ -0,0 +1,2 @@",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [1, 2],
+    }
+},
+{
+    # New test
+    testName => "add two lines in the middle of the file",
+    inputText => "@@ -4,0 +5,2 @@",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [5, 6],
+    }
+},
+{
+    # New test
+    testName => "append a single line to the end of the file",
+    inputText => "@@ -1,0 +2 @@",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [2, 2],
+    }
+},
+####
+#  Deletions
+##
+{
+    # New test
+    testName => "remove the first and only line in the file",
+    inputText => "@@ -1 +0,0 @@",
+    expectedResults => {
+        beforeChange => [1, 1],
+        afterChange => [-1, -1],
+    }
+},
+{
+    # New test
+    testName => "remove seven out of seven lines in the file",
+    inputText => "@@ -1,7 +0,0 @@",
+    expectedResults => {
+        beforeChange => [1, 7],
+        afterChange => [-1, -1],
+    }
+},
+{
+    # New test
+    testName => "remove two lines from the middle of the file",
+    inputText => "@@ -4,2 +3,0 @@",
+    expectedResults => {
+        beforeChange => [4, 5],
+        afterChange => [-1, -1],
+    }
+},
+####
+#  Changes
+##
+{
+    # New test
+    testName => "change the first line of the file",
+    inputText => "@@ -1 +1 @@",
+    expectedResults => {
+        beforeChange => [1, 1],
+        afterChange => [1, 1],
+    }
+},
+{
+    # New test
+    testName => "change the first line of the file and then append two more lines",
+    inputText => "@@ -1 +1,3 @@",
+    expectedResults => {
+        beforeChange => [1, 1],
+        afterChange => [1, 3],
+    }
+},
+{
+    # New test
+    testName => "remove seven out of seven lines and then add one line",
+    inputText => "@@ -1,7 +1 @@",
+    expectedResults => {
+        beforeChange => [1, 7],
+        afterChange => [1, 1],
+    }
+},
+{
+    # New test
+    testName => "remove two lines from the middle of the file",
+    inputText => "@@ -4,2 +4,2 @@",
+    expectedResults => {
+        beforeChange => [4, 5],
+        afterChange => [4, 5],
+    }
+},
+{
+    # New test
+    testName => "remove two lines from the middle of the file and then add a line",
+    inputText => "@@ -4,2 +4,3 @@",
+    expectedResults => {
+        beforeChange => [4, 5],
+        afterChange => [4, 6],
+    }
+},
+{
+    # New test
+    testName => "remove two lines from the middle of the file and then delete a line",
+    inputText => "@@ -4,3 +4,2 @@",
+    expectedResults => {
+        beforeChange => [4, 6],
+        afterChange => [4, 5],
+    }
+},
+###
+#  Invalid and malformed chunk ranges
+##
+# FIXME: We should make this set of tests more comprehensive.
+{
+    # New test
+    testName => "[invalid] empty string",
+    inputText => "",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [-1, -1],
+    }
+},
+{
+    # New test
+    testName => "[invalid] bogus chunk range",
+    inputText => "this is not a valid chunk range",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [-1, -1],
+    }
+},
+{
+    # New test
+    testName => "[invalid] chunk range missing leading sentinel",
+    inputText => "-4,3 +4,2 @@",
+    expectedResults => {
+        beforeChange => [-1, -1],
+        afterChange => [-1, -1],
+    }
+},
+);
+
+my $testCasesCount = @testCaseHashRefs;
+plan(tests => 2 * $testCasesCount); # Total number of assertions.
+
+foreach my $testCase (@testCaseHashRefs) {
+    my $expectedResults = $testCase->{expectedResults};
+
+    my $testNameStart = "extractLineRangeBeforeChange(): $testCase->{testName}: comparing";
+    my @got = PrepareChangeLog::extractLineRangeBeforeChange($testCase->{inputText});
+    is_deeply(\@got, $expectedResults->{beforeChange}, "$testNameStart return value.");
+
+    $testNameStart = "extractLineRangeAfterChange(): $testCase->{testName}: comparing";
+    @got = PrepareChangeLog::extractLineRangeAfterChange($testCase->{inputText});
+    is_deeply(\@got, $expectedResults->{afterChange}, "$testNameStart return value.");
+}