1#!/usr/bin/perl -w
2#
3# Copyright (C) 2011 Research In Motion Limited. All rights reserved.
4# Copyright (C) 2013 Apple Inc. All rights reserved.
5#
6# This library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# This library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with this library; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
20# Unit tests of parseDiff() with mock files; test override of patch EOL with EOL of target file.
21
22use strict;
23use warnings;
24
25use File::Temp;
26use POSIX qw/getcwd/;
27use Test::More;
28use VCSUtils;
29
30# We should consider moving escapeNewLineCharacters() and toMacLineEndings()
31# to VCSUtils.pm if they're useful in other places.
32sub escapeNewLineCharacters($)
33{
34    my ($text) = @_;
35    my @characters = split(//, $text);
36    my $result = "";
37    foreach (@characters) {
38        if (/^\r$/) {
39            $result .= '\r';
40            next;
41        }
42        if (/^\n$/) {
43            $result .= '\n';
44        }
45        $result .= $_;
46    }
47    return $result;
48}
49
50sub toMacLineEndings($)
51{
52    my ($text) = @_;
53    $text =~ s/\n/\r/g;
54    return $text;
55}
56
57my $gitDiffHeaderForNewFile = <<EOF;
58diff --git a/Makefile b/Makefile
59new file mode 100644
60index 0000000..756e864
61--- /dev/null
62+++ b/Makefile
63@@ -0,0 +1,17 @@
64EOF
65
66my $gitDiffHeader = <<EOF;
67diff --git a/Makefile b/Makefile
68index 756e864..04d2ae1 100644
69--- a/Makefile
70+++ b/Makefile
71@@ -1,3 +1,4 @@
72EOF
73
74my $svnConvertedGitDiffHeader = <<EOF;
75Index: Makefile
76index 756e864..04d2ae1 100644
77--- Makefile
78+++ Makefile
79@@ -1,3 +1,4 @@
80EOF
81
82my $svnConvertedGitDiffHeaderForNewFile = <<EOF;
83Index: Makefile
84new file mode 100644
85index 0000000..756e864
86--- Makefile
87+++ Makefile
88@@ -0,0 +1,17 @@
89EOF
90
91my $svnDiffHeaderForNewFile = <<EOF;
92Index: Makefile
93===================================================================
94--- Makefile	(revision 0)
95+++ Makefile	(revision 0)
96@@ -0,0 +1,17 @@
97EOF
98
99my $svnDiffHeader = <<EOF;
100Index: Makefile
101===================================================================
102--- Makefile	(revision 53052)
103+++ Makefile	(working copy)
104@@ -1,3 +1,4 @@
105EOF
106
107my $diffBody = <<EOF;
108+
109 MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools
110
111 all:
112EOF
113
114my $MakefileContents = <<EOF;
115MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools
116
117all:
118EOF
119
120my $mockDir = File::Temp->tempdir("parseDiffXXXX", CLEANUP => 1);
121writeToFile(File::Spec->catfile($mockDir, "MakefileWithUnixEOL"), $MakefileContents);
122writeToFile(File::Spec->catfile($mockDir, "MakefileWithWindowsEOL"), toWindowsLineEndings($MakefileContents));
123writeToFile(File::Spec->catfile($mockDir, "MakefileWithMacEOL"), toMacLineEndings($MakefileContents));
124
125# The array of test cases.
126my @testCaseHashRefs = (
127###
128# SVN test cases
129##
130{
131    # New test
132    diffName => "SVN: Patch with Unix line endings and IndexPath has Unix line endings",
133    inputText => substituteString($svnDiffHeader, "Makefile", "MakefileWithUnixEOL") . $diffBody,
134    expectedReturn => [
135[{
136    svnConvertedText => substituteString($svnDiffHeader, "Makefile", "MakefileWithUnixEOL") . $diffBody, # Same as input text
137    indexPath => "MakefileWithUnixEOL",
138    isSvn => 1,
139    numTextChunks => 1,
140    sourceRevision => "53052",
141}],
142undef],
143    expectedNextLine => undef,
144},
145{
146    # New test
147    diffName => "SVN: Patch with Windows line endings and IndexPath has Unix line endings",
148    inputText => substituteString($svnDiffHeader, "Makefile", "MakefileWithUnixEOL") . toWindowsLineEndings($diffBody),
149    expectedReturn => [
150[{
151    svnConvertedText => substituteString($svnDiffHeader, "Makefile", "MakefileWithUnixEOL") . $diffBody,
152    indexPath => "MakefileWithUnixEOL",
153    isSvn => 1,
154    numTextChunks => 1,
155    sourceRevision => "53052",
156}],
157undef],
158    expectedNextLine => undef,
159},
160{
161    # New test
162    diffName => "SVN: Patch with Windows line endings and IndexPath has Windows line endings",
163    inputText => substituteString($svnDiffHeader, "Makefile", "MakefileWithWindowsEOL") . toWindowsLineEndings($diffBody),
164    expectedReturn => [
165[{
166    svnConvertedText => substituteString($svnDiffHeader, "Makefile", "MakefileWithWindowsEOL") . toWindowsLineEndings($diffBody), # Same as input text
167    indexPath => "MakefileWithWindowsEOL",
168    isSvn => 1,
169    numTextChunks => 1,
170    sourceRevision => "53052",
171}],
172undef],
173    expectedNextLine => undef,
174},
175{
176    # New test
177    diffName => "SVN: Patch adds Windows newline to EOF and IndexPath has Windows line endings",
178    inputText => <<"EOF",
179Index: MakefileWithWindowsEOL
180===================================================================
181--- MakefileWithWindowsEOL	(revision 53052)
182+++ MakefileWithWindowsEOL	(working copy)
183@@ -1,3 +1,4 @@\r
184 MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r
185 \r
186-all:
187\\ No newline at end of file
188+all:\r
189+\r
190EOF
191    expectedReturn => [
192[{
193    # Same as input text
194    svnConvertedText => <<"EOF",
195Index: MakefileWithWindowsEOL
196===================================================================
197--- MakefileWithWindowsEOL	(revision 53052)
198+++ MakefileWithWindowsEOL	(working copy)
199@@ -1,3 +1,4 @@\r
200 MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r
201 \r
202-all:
203\\ No newline at end of file
204+all:\r
205+\r
206EOF
207    indexPath => "MakefileWithWindowsEOL",
208    isSvn => 1,
209    numTextChunks => 1,
210    sourceRevision => 53052
211}],
212undef],
213    expectedNextLine => undef,
214},
215{
216    # New test
217    diffName => "SVN: Patch adds Mac newline to EOF and IndexPath has Mac line endings",
218    inputText => <<"EOF",
219Index: MakefileWithMacEOL
220===================================================================
221--- MakefileWithMacEOL	(revision 53052)
222+++ MakefileWithMacEOL	(working copy)
223@@ -1,3 +1,4 @@\r MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r \r-all:
224\\ No newline at end of file
225+all:\r+\r
226EOF
227    expectedReturn => [
228[{
229    # Same as input text
230    svnConvertedText => q(Index: MakefileWithMacEOL
231===================================================================
232--- MakefileWithMacEOL	(revision 53052)
233+++ MakefileWithMacEOL	(working copy)
234@@ -1,3 +1,4 @@\r MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r \r-all:
235\\ No newline at end of file
236+all:\r+\r),
237    indexPath => "MakefileWithMacEOL",
238    isSvn => 1,
239    numTextChunks => 1,
240    sourceRevision => 53052
241}],
242undef],
243    expectedNextLine => undef,
244},
245{
246    # New test
247    diffName => "SVN: Patch with Unix line endings and IndexPath has Windows line endings",
248    inputText => substituteString($svnDiffHeader, "Makefile", "MakefileWithWindowsEOL") . $diffBody,
249    expectedReturn => [
250[{
251    svnConvertedText => substituteString($svnDiffHeader, "Makefile", "MakefileWithWindowsEOL") . toWindowsLineEndings($diffBody),
252    indexPath => "MakefileWithWindowsEOL",
253    isSvn => 1,
254    numTextChunks => 1,
255    sourceRevision => "53052",
256}],
257undef],
258    expectedNextLine => undef,
259},
260{
261    # New test
262    diffName => "SVN: Patch with Unix line endings and nonexistent IndexPath",
263    inputText => substituteString($svnDiffHeaderForNewFile, "Makefile", "NonexistentFile") . $diffBody,
264    expectedReturn => [
265[{
266    svnConvertedText => substituteString($svnDiffHeaderForNewFile, "Makefile", "NonexistentFile") . $diffBody, # Same as input text
267    indexPath => "NonexistentFile",
268    isSvn => 1,
269    isNew => 1,
270    numTextChunks => 1,
271}],
272undef],
273    expectedNextLine => undef,
274},
275{
276    # New test
277    diffName => "SVN: Patch with Windows line endings and nonexistent IndexPath",
278    inputText => substituteString($svnDiffHeaderForNewFile, "Makefile", "NonexistentFile") . toWindowsLineEndings($diffBody),
279    expectedReturn => [
280[{
281    svnConvertedText => substituteString($svnDiffHeaderForNewFile, "Makefile", "NonexistentFile") . toWindowsLineEndings($diffBody), # Same as input text
282    indexPath => "NonexistentFile",
283    isSvn => 1,
284    isNew => 1,
285    numTextChunks => 1,
286}],
287undef],
288    expectedNextLine => undef,
289},
290###
291# Git test cases
292##
293{
294    # New test
295    diffName => "Git: Patch with Unix line endings and IndexPath has Unix line endings",
296    inputText => substituteString($gitDiffHeader, "Makefile", "MakefileWithUnixEOL") . $diffBody,
297    expectedReturn => [
298[{
299    svnConvertedText => substituteString($svnConvertedGitDiffHeader, "Makefile", "MakefileWithUnixEOL") . $diffBody, # Same as input text
300    indexPath => "MakefileWithUnixEOL",
301    isGit => 1,
302    numTextChunks => 1,
303}],
304undef],
305    expectedNextLine => undef,
306},
307{
308    # New test
309    diffName => "Git: Patch with Windows line endings and IndexPath has Unix line endings",
310    inputText => substituteString($gitDiffHeader, "Makefile", "MakefileWithUnixEOL") . toWindowsLineEndings($diffBody),
311    expectedReturn => [
312[{
313    svnConvertedText => substituteString($svnConvertedGitDiffHeader, "Makefile", "MakefileWithUnixEOL") . $diffBody,
314    indexPath => "MakefileWithUnixEOL",
315    isGit => 1,
316    numTextChunks => 1,
317}],
318undef],
319    expectedNextLine => undef,
320},
321{
322    # New test
323    diffName => "Git: Patch with Windows line endings and IndexPath has Windows line endings",
324    inputText => substituteString($gitDiffHeader, "Makefile", "MakefileWithWindowsEOL") . toWindowsLineEndings($diffBody),
325    expectedReturn => [
326[{
327    svnConvertedText => substituteString($svnConvertedGitDiffHeader, "Makefile", "MakefileWithWindowsEOL") . toWindowsLineEndings($diffBody), # Same as input text
328    indexPath => "MakefileWithWindowsEOL",
329    isGit => 1,
330    numTextChunks => 1,
331}],
332undef],
333    expectedNextLine => undef,
334},
335{
336    # New test
337    diffName => "Git: Patch adds newline to EOF with Windows line endings and IndexPath has Windows line endings",
338    inputText => <<"EOF",
339diff --git a/MakefileWithWindowsEOL b/MakefileWithWindowsEOL
340index e7e8475..ae16fc3 100644
341--- a/MakefileWithWindowsEOL
342+++ b/MakefileWithWindowsEOL
343@@ -1,3 +1,4 @@\r
344 MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r
345 \r
346-all:
347\\ No newline at end of file
348+all:\r
349+\r
350EOF
351    expectedReturn => [
352[{
353    # Same as input text
354    svnConvertedText => <<"EOF",
355Index: MakefileWithWindowsEOL
356index e7e8475..ae16fc3 100644
357--- MakefileWithWindowsEOL
358+++ MakefileWithWindowsEOL
359@@ -1,3 +1,4 @@\r
360 MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r
361 \r
362-all:
363\\ No newline at end of file
364+all:\r
365+\r
366EOF
367    indexPath => "MakefileWithWindowsEOL",
368    isGit => 1,
369    numTextChunks => 1
370}],
371undef],
372    expectedNextLine => undef,
373},
374{
375    # New test
376    diffName => "Git: Patch adds Mac newline to EOF and IndexPath has Mac line endings",
377    inputText => <<"EOF",
378diff --git a/MakefileWithMacEOL b/MakefileWithMacEOL
379index e7e8475..ae16fc3 100644
380--- a/MakefileWithMacEOL
381+++ b/MakefileWithMacEOL
382@@ -1,3 +1,4 @@\r MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r \r-all:
383\\ No newline at end of file
384+all:\r+\r
385EOF
386    expectedReturn => [
387[{
388    # Same as input text
389    svnConvertedText => q(Index: MakefileWithMacEOL
390index e7e8475..ae16fc3 100644
391--- MakefileWithMacEOL
392+++ MakefileWithMacEOL
393@@ -1,3 +1,4 @@\r MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools\r \r-all:
394\\ No newline at end of file
395+all:\r+\r),
396    indexPath => "MakefileWithMacEOL",
397    isGit => 1,
398    numTextChunks => 1
399}],
400undef],
401    expectedNextLine => undef,
402},
403{
404    # New test
405    diffName => "Git: Patch with Unix line endings and IndexPath has Windows line endings",
406    inputText => substituteString($gitDiffHeader, "Makefile", "MakefileWithWindowsEOL") . $diffBody,
407    expectedReturn => [
408[{
409    svnConvertedText => substituteString($svnConvertedGitDiffHeader, "Makefile", "MakefileWithWindowsEOL") . toWindowsLineEndings($diffBody),
410    indexPath => "MakefileWithWindowsEOL",
411    isGit => 1,
412    numTextChunks => 1,
413}],
414undef],
415    expectedNextLine => undef,
416},
417{
418    # New test
419    diffName => "Git: Patch with Unix line endings and nonexistent IndexPath",
420    inputText => substituteString($gitDiffHeaderForNewFile, "Makefile", "NonexistentFile") . $diffBody,
421    expectedReturn => [
422[{
423    svnConvertedText => substituteString($svnConvertedGitDiffHeaderForNewFile, "Makefile", "NonexistentFile") . $diffBody, # Same as input text
424    indexPath => "NonexistentFile",
425    isGit => 1,
426    isNew => 1,
427    numTextChunks => 1,
428}],
429undef],
430    expectedNextLine => undef,
431},
432{
433    # New test
434    diffName => "Git: Patch with Windows line endings and nonexistent IndexPath",
435    inputText => substituteString($gitDiffHeaderForNewFile, "Makefile", "NonexistentFile") . toWindowsLineEndings($diffBody),
436    expectedReturn => [
437[{
438    svnConvertedText => substituteString($svnConvertedGitDiffHeaderForNewFile, "Makefile", "NonexistentFile") . toWindowsLineEndings($diffBody), # Same as input text
439    indexPath => "NonexistentFile",
440    isGit => 1,
441    isNew => 1,
442    numTextChunks => 1,
443}],
444undef],
445    expectedNextLine => undef,
446},
447);
448
449my $testCasesCount = @testCaseHashRefs;
450plan(tests => 2 * $testCasesCount); # Total number of assertions.
451
452my $savedCWD = getcwd();
453chdir($mockDir) or die;
454foreach my $testCase (@testCaseHashRefs) {
455    my $testNameStart = "parseDiff(): $testCase->{diffName}: comparing";
456
457    my $fileHandle;
458    open($fileHandle, "<", \$testCase->{inputText});
459    my $line = <$fileHandle>;
460
461    my @got = VCSUtils::parseDiff($fileHandle, $line);
462    my $expectedReturn = $testCase->{expectedReturn};
463
464    $got[0][0]->{svnConvertedText} = escapeNewLineCharacters($got[0][0]->{svnConvertedText});
465    $expectedReturn->[0][0]->{svnConvertedText} = escapeNewLineCharacters($expectedReturn->[0][0]->{svnConvertedText});
466    is_deeply(\@got, $expectedReturn, "$testNameStart return value.");
467
468    my $gotNextLine = <$fileHandle>;
469    is($gotNextLine, $testCase->{expectedNextLine},  "$testNameStart next read line.");
470}
471chdir($savedCWD);
472
473sub substituteString
474{
475    my ($string, $searchString, $replacementString) = @_;
476    $string =~ s/$searchString/$replacementString/g;
477    return $string;
478}
479
480sub writeToFile
481{
482    my ($file, $text) = @_;
483    open(FILE, ">$file") or die;
484    print FILE $text;
485    close(FILE);
486}
487