+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
$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";
# 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}) {
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);
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(@)
--- /dev/null
+#!/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.");
+}