WebKitTools/ChangeLog

 12010-04-10 Daniel Bates <dbates@rim.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 https://bugs.webkit.org/show_bug.cgi?id=27204
 6
 7 Implement support for changing the executable bit of a file.
 8 The executable bit is among the most changed file properties.
 9 Future support can include other property changes.
 10
 11 Currently, if a patch changes the executable bit of a file
 12 it is not respected by svn-apply or svn-unapply. Since the
 13 commit-queue bot uses these tools as part of its workflow,
 14 such patches cannot be committed by it. That is, such patches
 15 need to be committed by hand. Instead, we should add support
 16 for the executable bit so that such patches can be committed
 17 by the commit-queue bot.
 18
 19 * Scripts/VCSUtils.pm: Also change reference to Apple Computer, Inc.
 20 in copyright to Apple, Inc.
 21 * Scripts/svn-apply:
 22 * Scripts/svn-unapply:
 23 * Scripts/webkitperl/VCSUtils_unittest/appendSVNExecutableBitChangeToPatch.pl: Added.
 24 * Scripts/webkitperl/VCSUtils_unittest/parseGitFileMode.pl: Added.
 25 * Scripts/webkitperl/VCSUtils_unittest/parseStartOfPatchOrPropertyChangeAndEndOfPropertyChange.pl: Added.
 26
1272010-04-10 Robert Hogan <robert@webkit.org>
228
329 Unreviewed fix to regressions in r57416.
57418

WebKitTools/Scripts/VCSUtils.pm

11# Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
22# Copyright (C) 2009, 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
 3# Copyright (C) Research in Motion Limited 2010. All rights reserved.
34#
45# Redistribution and use in source and binary forms, with or without
56# modification, are permitted provided that the following conditions

1011# 2. Redistributions in binary form must reproduce the above copyright
1112# notice, this list of conditions and the following disclaimer in the
1213# documentation and/or other materials provided with the distribution.
13 # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 14# 3. Neither the name of Apple Inc. ("Apple") nor the names of
1415# its contributors may be used to endorse or promote products derived
1516# from this software without specific prior written permission.
1617#

@@BEGIN {
4344 $VERSION = 1.00;
4445 @ISA = qw(Exporter);
4546 @EXPORT = qw(
 47 &appendSVNExecutableBitChangeToPatch
4648 &canonicalizePath
4749 &changeLogEmailAddress
4850 &changeLogName

@@BEGIN {
5456 &fixChangeLogPatch
5557 &gitBranch
5658 &gitdiff2svndiff
 59 &isEndOfPropertyChange
5760 &isGit
5861 &isGitBranchBuild
5962 &isGitDirectory
6063 &isSVN
6164 &isSVNDirectory
 65 &isSVNProperty
6266 &isSVNVersion16OrNewer
6367 &makeFilePathRelative
6468 &mergeChangeLogs
6569 &normalizePath
 70 &parseGitFileMode
6671 &parsePatch
 72 &parseStartOfPatchOrPropertyChange
6773 &pathRelativeToSVNRepositoryRootForPath
6874 &runPatchCommand
 75 &scmAddExecutableProperty
 76 &scmRemoveExecutableProperty
6977 &svnRevisionForDirectory
7078 &svnStatus
 79 &togglePropertyChange
7180 );
7281 %EXPORT_TAGS = ( );
7382 @EXPORT_OK = ();

@@sub isGit()
110119 return $isGit;
111120}
112121
 122sub parseGitFileMode($)
 123{
 124 my ($patch) = @_;
 125 return ($patch =~ /^new (file )?mode ([0-9]{6})$/) ? $2 : 0;
 126}
 127
 128sub isSVNProperty($)
 129{
 130 my ($patch) = @_;
 131 # FIXME: We should make this more generic and support additional SVN properties.
 132 return $patch =~ /\n(Added|Deleted): svn:executable\n/;
 133}
 134
 135sub parseStartOfPatchOrPropertyChange($)
 136{
 137 my ($patch) = @_;
 138 return $1 if ($patch =~ /^Index: ([^\r\n]+)/ || $patch =~ /^Property changes on: ([^\r\n]+)/);
 139 return 0;
 140}
 141
 142sub isEndOfPropertyChange($$)
 143{
 144 my ($patch, $propertyChangePath) = @_;
 145 return ($propertyChangePath && $patch =~ /^ (\+|-) \*$/);
 146}
 147
 148sub togglePropertyChange
 149{
 150 my ($patch, $fullPath, $shouldUnapply) = @_;
 151 # Change executable bit.
 152 if ($patch =~ /Added: svn:executable\n/) {
 153 if (!defined($shouldUnapply)) {
 154 scmAddExecutableProperty($fullPath);
 155 } else {
 156 scmRemoveExecutableProperty($fullPath);
 157 }
 158 } elsif ($patch =~ /Deleted: svn:executable\n/) {
 159 if (!defined($shouldUnapply)) {
 160 scmRemoveExecutableProperty($fullPath);
 161 } else {
 162 scmAddExecutableProperty($fullPath);
 163 }
 164 }
 165}
 166
 167sub appendSVNExecutableBitChangeToPatch($$$)
 168{
 169 my ($indexPath, $fileMode, $patchRef) = @_;
 170 # Note, we don't need to include either the divider line or the " + *" line,
 171 # but these are left in so as to produce a proper SVN property change entry.
 172 $$patchRef .= "Property changes on: $indexPath\n";
 173 $$patchRef .= "___________________________________________________________________\n";
 174 # FIXME: These file modes are fragile and based on observation.
 175 $$patchRef .= "Added: svn:executable\n" if $fileMode eq "100755";
 176 $$patchRef .= "Deleted: svn:executable\n" if $fileMode eq "100644";
 177 $$patchRef .= " + *\n";
 178}
 179
 180sub scmAddExecutableProperty($)
 181{
 182 my ($path) = @_;
 183 if (isSVN()) {
 184 system("svn", "propset", "svn:executable", "on", $path) == 0 or die "Failed to run 'svn propset svn:executable on $path'.";
 185 } elsif (isGit()) {
 186 chmod(0755, $path);
 187 }
 188}
 189
 190sub scmRemoveExecutableProperty($)
 191{
 192 my ($path) = @_;
 193 if (isSVN()) {
 194 system("svn", "propdel", "svn:executable", $path) == 0 or die "Failed to run 'svn propdel svn:executable $path'.";
 195 } elsif (isGit()) {
 196 chmod(0664, $path);
 197 }
 198}
 199
113200sub gitBranch()
114201{
115202 unless (defined $gitBranch) {
57418

WebKitTools/Scripts/svn-apply

5050#
5151# Missing features:
5252#
53 # Handle property changes.
 53# Handle property changes other than the executable bit.
5454# Handle copied and moved directories (would require patches made by svn-create-patch).
5555# When doing a removal, check that old file matches what's being removed.
5656# Notice a patch that's being applied at the "wrong level" and make it work anyway.

@@my $copiedFromPath;
130130my $filter;
131131my $indexPath;
132132my $patch;
 133my $propertyChangePath;
 134my $isGitFormattedPatch = 0;
133135while (<>) {
134136 s/([\n\r]+)$//mg;
135137 my $eol = $1;
136138 if (!defined($indexPath) && m#^diff --git \w/#) {
137139 $filter = \&gitdiff2svndiff;
 140 $isGitFormattedPatch = 1;
138141 }
139142 $_ = &$filter($_) if $filter;
140  if (/^Index: (.+)/) {
141  $indexPath = $1;
 143 if (my $filePath = parseStartOfPatchOrPropertyChange($_)) {
 144 if (/^Index: (.+)/) {
 145 $indexPath = $filePath;
 146 } else {
 147 $propertyChangePath = $filePath;
 148 }
142149 if ($patch) {
143150 if (!$copiedFromPath) {
144151 push @patches, $patch;

@@while (<>) {
151158 # Fix paths on diff, ---, and +++ lines to match preceding Index: line.
152159 s/\S+$/$indexPath/ if /^diff/;
153160 s/^--- \S+/--- $indexPath/;
 161
 162 if ($isGitFormattedPatch && (my $fileMode = parseGitFileMode($_))) {
 163 # The gitdiff2svndiff filter only operates on a single, but the equivalent
 164 # property change entry in SVN spans multiple lines which we need to parse.
 165 appendSVNExecutableBitChangeToPatch($indexPath, $fileMode, \$patch);
 166 next;
 167 }
 168
154169 if (/^--- .+\(from (\S+):(\d+)\)$/) {
155170 $copiedFromPath = $1;
156171 $copiedFiles{$indexPath} = $copiedFromPath;

@@while (<>) {
163178 $indexPath = "";
164179 }
165180 }
 181 $propertyChangePath = "" if isEndOfPropertyChange($_, $propertyChangePath);
166182 $patch .= $_;
167183 $patch .= $eol;
168184}

@@sub patch($)
330346 my ($patch) = @_;
331347 return if !$patch;
332348
333  unless ($patch =~ m|^Index: ([^\r\n]+)|) {
 349 unless ($patch =~ m|^Index: ([^\r\n]+)| || $patch =~ m|^Property changes on: ([^\r\n]+)|) {
334350 my $separator = '-' x 67;
335  warn "Failed to find 'Index:' in:\n$separator\n$patch\n$separator\n";
 351 warn "Failed to find 'Index:' or 'Property changes on:' in:\n$separator\n$patch\n$separator\n";
336352 die unless $force;
337353 return;
338354 }

@@sub patch($)
342358 my $addition = 0;
343359 my $isBinary = 0;
344360 my $isGitBinary = 0;
 361 my $isSVNPropertyChange = 0;
345362
346363 $addition = 1 if ($patch =~ /\n--- .+\(revision 0\)\r?\n/ || $patch =~ /\n@@ -0,0 .* @@/) && !exists($copiedFiles{$fullPath});
347364 $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/;
348365 $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./;
349366 $isGitBinary = 1 if $patch =~ /\nGIT binary patch\n/;
 367 $isSVNPropertyChange = isSVNProperty($patch);
350368
351  if (!$addition && !$deletion && !$isBinary && !$isGitBinary) {
 369 if (!$addition && !$deletion && !$isBinary && !$isGitBinary && !$isSVNPropertyChange) {
352370 # Standard patch, patch tool can handle this.
353371 if (basename($fullPath) eq "ChangeLog") {
354372 my $changeLogDotOrigExisted = -f "${fullPath}.orig";

@@sub patch($)
372390 # Deletion
373391 applyPatch($patch, $fullPath, ["--force"]);
374392 scmRemove($fullPath);
 393 } elsif ($isSVNPropertyChange) {
 394 # Change executable bit.
 395 togglePropertyChange($patch, $fullPath);
375396 } else {
376397 # Addition
377398 rename($fullPath, "$fullPath.orig") if -e $fullPath;
57418

WebKitTools/Scripts/svn-unapply

4747#
4848# Missing features:
4949#
50 # Handle property changes.
 50# Handle property changes other than the executable bit.
5151# Handle copied and moved directories (would require patches made by svn-create-patch).
5252# Use version numbers in the patch file and do a 3-way merge.
5353# When reversing an addition, check that the file matches what's being removed.

@@my $copiedFromPath;
101101my $filter;
102102my $indexPath;
103103my $patch;
 104my $propertyChangePath;
 105my $isGitFormattedPatch = 0;
104106while (<>) {
105107 s/([\n\r]+)$//mg;
106108 my $eol = $1;

@@while (<>) {
108110 $filter = \&gitdiff2svndiff;
109111 }
110112 $_ = &$filter($_) if $filter;
111  if (/^Index: (.+)/) {
112  $indexPath = $1;
 113 if (my $filePath = parseStartOfPatchOrPropertyChange($_)) {
 114 if (/^Index: (.+)/) {
 115 $indexPath = $filePath;
 116 $isGitFormattedPatch = $filePath;
 117 } else {
 118 $propertyChangePath = $filePath;
 119 }
113120 if ($patch) {
114121 if ($copiedFromPath) {
115122 push @copiedFiles, $patch;

@@while (<>) {
123130 if ($indexPath) {
124131 # Fix paths on diff, ---, and +++ lines to match preceding Index: line.
125132 s/^--- \S+/--- $indexPath/;
 133
 134 if ($isGitFormattedPatch && (my $fileMode = parseGitFileMode($_))) {
 135 # The gitdiff2svndiff filter only operates on a single, but the equivalent
 136 # property change entry in SVN spans multiple lines which we need to parse.
 137 appendSVNExecutableBitChangeToPatch($indexPath, $fileMode, \$patch);
 138 next;
 139 }
 140
126141 if (/^--- .+\(from (\S+):\d+\)$/) {
127142 $copiedFromPath = $1;
128143 }

@@while (<>) {
130145 $indexPath = "";
131146 }
132147 }
 148 $propertyChangePath = "" if isEndOfPropertyChange($_, $propertyChangePath);
133149 $patch .= $_;
134150 $patch .= $eol;
135151}

@@sub patch($)
168184 my ($patch) = @_;
169185 return if !$patch;
170186
171  unless ($patch =~ m|^Index: ([^\r\n]+)|) {
 187 my $fullPath;
 188 unless (defined($fullPath = parseStartOfPatchOrPropertyChange($patch))) {
172189 my $separator = '-' x 67;
173  warn "Failed to find 'Index:' in:\n$separator\n$patch\n$separator\n";
 190 warn "Failed to find 'Index:' or 'Property changes on:' in:\n$separator\n$patch\n$separator\n";
174191 return;
175192 }
176  my $fullPath = $1;
177193 $directoriesToCheck{dirname($fullPath)} = 1;
178194
179195 my $deletion = 0;
180196 my $addition = 0;
181197 my $isBinary = 0;
 198 my $isSVNPropertyChange = 0;
182199
183200 $addition = 1 if ($patch =~ /\n--- .+\(revision 0\)\n/ || $patch =~ /\n@@ -0,0 .* @@/);
184201 $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/;
185202 $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./;
 203 $isSVNPropertyChange = isSVNProperty($patch);
186204
187  if (!$addition && !$deletion && !$isBinary) {
 205 if (!$addition && !$deletion && !$isBinary && !$isSVNPropertyChange) {
188206 # Standard patch, patch tool can handle this.
189207 if (basename($fullPath) eq "ChangeLog") {
190208 my $changeLogDotOrigExisted = -f "${fullPath}.orig";

@@sub patch($)
226244
227245 # Show status if the file is modifed
228246 system "svn", "stat", $fullPath;
 247 } elsif ($isSVNPropertyChange) {
 248 # Reverse change of executable bit.
 249 togglePropertyChange($patch, $fullPath, 1);
229250 } else {
230251 # Reverse addition
231252 unapplyPatch($patch, $fullPath, ["--force"]);
57418

WebKitTools/Scripts/webkitperl/VCSUtils_unittest/appendSVNExecutableBitChangeToPatch.pl

 1#!/usr/bin/perl
 2#
 3# Copyright (C) Research in Motion Limited 2010. All rights reserved.
 4#
 5# Redistribution and use in source and binary forms, with or without
 6# modification, are permitted provided that the following conditions are
 7# met:
 8#
 9# * Redistributions of source code must retain the above copyright
 10# notice, this list of conditions and the following disclaimer.
 11# * Redistributions in binary form must reproduce the above
 12# copyright notice, this list of conditions and the following disclaimer
 13# in the documentation and/or other materials provided with the
 14# distribution.
 15# * Neither the name of Research in Motion Limited nor the names of its
 16# contributors may be used to endorse or promote products derived from
 17# this software without specific prior written permission.
 18#
 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30
 31# Unit tests of VCSUtils::appendSVNExecutableBitChangeToPatch().
 32
 33use Test::Simple tests => 2;
 34use VCSUtils;
 35
 36my $title;
 37my $patch;
 38my $expectedPatch;
 39
 40# New test
 41$title = "appendSVNExecutableBitChangeToPatch: Add executable bit to file.";
 42
 43$expectedPatch = <<'END';
 44Property changes on: file_to_patch.txt
 45___________________________________________________________________
 46Added: svn:executable
 47 + *
 48END
 49
 50$patch = "";
 51appendSVNExecutableBitChangeToPatch("file_to_patch.txt", "100755", \$patch);
 52
 53ok($patch eq $expectedPatch, $title);
 54
 55# New test
 56$title = "appendSVNExecutableBitChangeToPatch: Remove executable bit from file.";
 57
 58$expectedPatch = <<'END';
 59Property changes on: file_to_patch.txt
 60___________________________________________________________________
 61Deleted: svn:executable
 62 + *
 63END
 64
 65$patch = "";
 66appendSVNExecutableBitChangeToPatch("file_to_patch.txt", "100644", \$patch);
 67
 68ok($patch eq $expectedPatch, $title);
0

WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseGitFileMode.pl

 1#!/usr/bin/perl
 2#
 3# Copyright (C) Research in Motion Limited 2010. All rights reserved.
 4#
 5# Redistribution and use in source and binary forms, with or without
 6# modification, are permitted provided that the following conditions are
 7# met:
 8#
 9# * Redistributions of source code must retain the above copyright
 10# notice, this list of conditions and the following disclaimer.
 11# * Redistributions in binary form must reproduce the above
 12# copyright notice, this list of conditions and the following disclaimer
 13# in the documentation and/or other materials provided with the
 14# distribution.
 15# * Neither the name of Research in Motion Limited nor the names of its
 16# contributors may be used to endorse or promote products derived from
 17# this software without specific prior written permission.
 18#
 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30
 31# Unit tests of VCSUtils::parseGitFileMode().
 32
 33use Test::Simple tests => 6;
 34use VCSUtils;
 35
 36my $title;
 37my $patchLine;
 38
 39# New test
 40$title = "parseGitFileMode: Parse a valid Git file mode for new file.";
 41
 42$patchLine = "new file mode 100755";
 43ok(parseGitFileMode($patchLine) == 100755, $title);
 44
 45# New test
 46$title = "parseGitFileMode: Parse a valid Git file mode for an existing file.";
 47
 48$patchLine = "new mode 100755";
 49ok(parseGitFileMode($patchLine) == 100755, $title);
 50
 51# New test
 52$title = "parseGitFileMode: Parse an invalid Git file mode for new file.";
 53
 54$patchLine = "new file mode dummy";
 55ok(!parseGitFileMode($patchLine), $title);
 56
 57# New test
 58$title = "parseGitFileMode: Parse an invalid Git file mode for an existing file.";
 59
 60$patchLine = "new mode dummy";
 61ok(!parseGitFileMode($patchLine), $title);
 62
 63# New test
 64$title = "parseGitFileMode: Ensure that a numeric file mode longer than 6 digits is invalid for a new file.";
 65
 66$patchLine = "new file mode 1007559";
 67ok(!parseGitFileMode($patchLine), $title);
 68
 69# New test
 70$title = "parseGitFileMode: Ensure that a numeric file mode longer than 6 digits is invalid for an existing file.";
 71
 72$patchLine = "new mode 1007559";
 73ok(!parseGitFileMode($patchLine), $title);
0

WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseStartOfPatchOrPropertyChangeAndEndOfPropertyChange.pl

 1#!/usr/bin/perl
 2#
 3# Copyright (C) Research in Motion Limited 2010. All rights reserved.
 4#
 5# Redistribution and use in source and binary forms, with or without
 6# modification, are permitted provided that the following conditions are
 7# met:
 8#
 9# * Redistributions of source code must retain the above copyright
 10# notice, this list of conditions and the following disclaimer.
 11# * Redistributions in binary form must reproduce the above
 12# copyright notice, this list of conditions and the following disclaimer
 13# in the documentation and/or other materials provided with the
 14# distribution.
 15# * Neither the name of Research in Motion Limited nor the names of its
 16# contributors may be used to endorse or promote products derived from
 17# this software without specific prior written permission.
 18#
 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30
 31# Unit tests of VCSUtils::parseStartOfPatchOrPropertyChange() and VCSUtils::isEndOfPropertyChange.
 32
 33use Test::Simple tests => 6;
 34use VCSUtils;
 35
 36my $title;
 37
 38# New test
 39$title = "isStartOfPatchOrPropertyChange: Test start of SVN patch.";
 40ok(parseStartOfPatchOrPropertyChange("Index: file_to_patch.txt\n") eq "file_to_patch.txt", $title);
 41
 42# New test
 43$title = "isStartOfPatchOrPropertyChange: Test start of SVN property change entry.";
 44ok(parseStartOfPatchOrPropertyChange("Property changes on: file_to_patch.txt\r\n") eq "file_to_patch.txt", $title);
 45
 46# New test
 47$title = "isEndOfPropertyChange: Test end of SVN property change entry addition.";
 48ok(isEndOfPropertyChange(" + *\n", "file_to_patch.txt"), $title);
 49
 50# New test
 51$title = "isEndOfPropertyChange: Test end of SVN property change entry deletion.";
 52ok(isEndOfPropertyChange(" - *\n", "file_to_patch.txt"), $title);
 53
 54# New test
 55$title = "isEndOfPropertyChange: Test end of SVN property change entry addition with invalid property change path.";
 56ok(!isEndOfPropertyChange(" + *\n", 0), $title);
 57
 58# New test
 59$title = "isEndOfPropertyChange: Test end of SVN property change entry deletion with invalid property change path.";
 60ok(!isEndOfPropertyChange(" - *\n", 0), $title);
0