1#!/usr/bin/perl -w
2#
3# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8# 1.  Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10# 2.  Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#
14# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
18# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25# Unit tests of parseGitDiffHeader().
26
27use strict;
28use warnings;
29
30use Test::More;
31use VCSUtils;
32
33# The array of test cases.
34my @testCaseHashRefs = (
35{   # New test
36    diffName => "Modified file",
37    inputText => <<'END',
38diff --git a/foo.h b/foo.h
39index f5d5e74..3b6aa92 100644
40--- a/foo.h
41+++ b/foo.h
42@@ -1 +1 @@
43-file contents
44+new file contents
45END
46    expectedReturn => [
47{
48    svnConvertedText => <<'END',
49Index: foo.h
50index f5d5e74..3b6aa92 100644
51--- foo.h
52+++ foo.h
53END
54    indexPath => "foo.h",
55},
56"@@ -1 +1 @@\n"],
57    expectedNextLine => "-file contents\n",
58},
59{   # New test
60    diffName => "new file",
61    inputText => <<'END',
62diff --git a/foo.h b/foo.h
63new file mode 100644
64index 0000000..3c9f114
65--- /dev/null
66+++ b/foo.h
67@@ -0,0 +1,34 @@
68+<html>
69END
70    expectedReturn => [
71{
72    svnConvertedText => <<'END',
73Index: foo.h
74new file mode 100644
75index 0000000..3c9f114
76--- foo.h
77+++ foo.h
78END
79    indexPath => "foo.h",
80    isNew => 1,
81},
82"@@ -0,0 +1,34 @@\n"],
83    expectedNextLine => "+<html>\n",
84},
85{   # New test
86    diffName => "file deletion",
87    inputText => <<'END',
88diff --git a/foo b/foo
89deleted file mode 100644
90index 1e50d1d..0000000
91--- a/foo
92+++ /dev/null
93@@ -1,1 +0,0 @@
94-line1
95diff --git a/configure.ac b/configure.ac
96index d45dd40..3494526 100644
97END
98    expectedReturn => [
99{
100    svnConvertedText => <<'END',
101Index: foo
102deleted file mode 100644
103index 1e50d1d..0000000
104--- foo
105+++ foo
106END
107    indexPath => "foo",
108    isDeletion => 1,
109},
110"@@ -1,1 +0,0 @@\n"],
111    expectedNextLine => "-line1\n",
112},
113{   # New test
114    diffName => "using --no-prefix",
115    inputText => <<'END',
116diff --git foo.h foo.h
117index c925780..9e65c43 100644
118--- foo.h
119+++ foo.h
120@@ -1,3 +1,17 @@
121+contents
122END
123    expectedReturn => [
124{
125    svnConvertedText => <<'END',
126Index: foo.h
127index c925780..9e65c43 100644
128--- foo.h
129+++ foo.h
130END
131    indexPath => "foo.h",
132},
133"@@ -1,3 +1,17 @@\n"],
134    expectedNextLine => "+contents\n",
135},
136####
137#    Copy operations
138##
139{   # New test
140    diffName => "copy (with similarity index 100%)",
141    inputText => <<'END',
142diff --git a/foo b/foo_new
143similarity index 100%
144copy from foo
145copy to foo_new
146diff --git a/bar b/bar
147index d45dd40..3494526 100644
148END
149    expectedReturn => [
150{
151    svnConvertedText => <<'END',
152Index: foo_new
153similarity index 100%
154copy from foo
155copy to foo_new
156END
157    copiedFromPath => "foo",
158    indexPath => "foo_new",
159},
160"diff --git a/bar b/bar\n"],
161    expectedNextLine => "index d45dd40..3494526 100644\n",
162},
163{   # New test
164    diffName => "copy (with similarity index < 100%)",
165    inputText => <<'END',
166diff --git a/foo b/foo_new
167similarity index 99%
168copy from foo
169copy to foo_new
170diff --git a/bar b/bar
171index d45dd40..3494526 100644
172END
173    expectedReturn => [
174{
175    svnConvertedText => <<'END',
176Index: foo_new
177similarity index 99%
178copy from foo
179copy to foo_new
180END
181    copiedFromPath => "foo",
182    indexPath => "foo_new",
183    isCopyWithChanges => 1,
184},
185"diff --git a/bar b/bar\n"],
186    expectedNextLine => "index d45dd40..3494526 100644\n",
187},
188{   # New test
189    diffName => "rename (with similarity index 100%)",
190    inputText => <<'END',
191diff --git a/foo b/foo_new
192similarity index 100%
193rename from foo
194rename to foo_new
195diff --git a/bar b/bar
196index d45dd40..3494526 100644
197END
198    expectedReturn => [
199{
200    svnConvertedText => <<'END',
201Index: foo_new
202similarity index 100%
203rename from foo
204rename to foo_new
205END
206    copiedFromPath => "foo",
207    indexPath => "foo_new",
208    shouldDeleteSource => 1,
209},
210"diff --git a/bar b/bar\n"],
211    expectedNextLine => "index d45dd40..3494526 100644\n",
212},
213{   # New test
214    diffName => "rename (with similarity index < 100%)",
215    inputText => <<'END',
216diff --git a/foo b/foo_new
217similarity index 99%
218rename from foo
219rename to foo_new
220index 1e50d1d..1459d21 100644
221--- a/foo
222+++ b/foo_new
223@@ -15,3 +15,4 @@ release r deployment dep deploy:
224 line1
225 line2
226 line3
227+line4
228diff --git a/bar b/bar
229index d45dd40..3494526 100644
230END
231    expectedReturn => [
232{
233    svnConvertedText => <<'END',
234Index: foo_new
235similarity index 99%
236rename from foo
237rename to foo_new
238index 1e50d1d..1459d21 100644
239--- foo_new
240+++ foo_new
241END
242    copiedFromPath => "foo",
243    indexPath => "foo_new",
244    isCopyWithChanges => 1,
245    shouldDeleteSource => 1,
246},
247"@@ -15,3 +15,4 @@ release r deployment dep deploy:\n"],
248    expectedNextLine => " line1\n",
249},
250{   # New test
251    diffName => "rename (with executable bit change)",
252    inputText => <<'END',
253diff --git a/foo b/foo_new
254old mode 100644
255new mode 100755
256similarity index 100%
257rename from foo
258rename to foo_new
259diff --git a/bar b/bar
260index d45dd40..3494526 100644
261END
262    expectedReturn => [
263{
264    svnConvertedText => <<'END',
265Index: foo_new
266old mode 100644
267new mode 100755
268similarity index 100%
269rename from foo
270rename to foo_new
271END
272    copiedFromPath => "foo",
273    executableBitDelta => 1,
274    indexPath => "foo_new",
275    isCopyWithChanges => 1,
276    shouldDeleteSource => 1,
277},
278"diff --git a/bar b/bar\n"],
279    expectedNextLine => "index d45dd40..3494526 100644\n",
280},
281####
282#    Binary file test cases
283##
284{
285    # New test case
286    diffName => "New binary file",
287    inputText => <<'END',
288diff --git a/foo.gif b/foo.gif
289new file mode 100644
290index 0000000000000000000000000000000000000000..64a9532e7794fcd791f6f12157406d9060151690
291GIT binary patch
292literal 7
293OcmYex&reDa;sO8*F9L)B
294
295literal 0
296HcmV?d00001
297
298END
299    expectedReturn => [
300{
301    svnConvertedText => <<'END',
302Index: foo.gif
303new file mode 100644
304index 0000000000000000000000000000000000000000..64a9532e7794fcd791f6f12157406d9060151690
305GIT binary patch
306END
307    indexPath => "foo.gif",
308    isBinary => 1,
309    isNew => 1,
310},
311"literal 7\n"],
312    expectedNextLine => "OcmYex&reDa;sO8*F9L)B\n",
313},
314{
315    # New test case
316    diffName => "Deleted binary file",
317    inputText => <<'END',
318diff --git a/foo.gif b/foo.gif
319deleted file mode 100644
320index 323fae0..0000000
321GIT binary patch
322literal 0
323HcmV?d00001
324
325literal 7
326OcmYex&reDa;sO8*F9L)B
327
328END
329    expectedReturn => [
330{
331    svnConvertedText => <<'END',
332Index: foo.gif
333deleted file mode 100644
334index 323fae0..0000000
335GIT binary patch
336END
337    indexPath => "foo.gif",
338    isBinary => 1,
339    isDeletion => 1,
340},
341"literal 0\n"],
342    expectedNextLine => "HcmV?d00001\n",
343},
344####
345#    Executable bit test cases
346##
347{
348    # New test case
349    diffName => "Modified executable file",
350    inputText => <<'END',
351diff --git a/foo b/foo
352index d03e242..435ad3a 100755
353--- a/foo
354+++ b/foo
355@@ -1 +1 @@
356-file contents
357+new file contents
358
359END
360    expectedReturn => [
361{
362    svnConvertedText => <<'END',
363Index: foo
364index d03e242..435ad3a 100755
365--- foo
366+++ foo
367END
368    indexPath => "foo",
369},
370"@@ -1 +1 @@\n"],
371    expectedNextLine => "-file contents\n",
372},
373{
374    # New test case
375    diffName => "Making file executable (last diff)",
376    inputText => <<'END',
377diff --git a/foo.exe b/foo.exe
378old mode 100644
379new mode 100755
380END
381    expectedReturn => [
382{
383    svnConvertedText => <<'END',
384Index: foo.exe
385old mode 100644
386new mode 100755
387END
388    executableBitDelta => 1,
389    indexPath => "foo.exe",
390},
391undef],
392    expectedNextLine => undef,
393},
394{
395    # New test case
396    diffName => "Making file executable (not last diff)",
397    inputText => <<'END',
398diff --git a/foo.exe b/foo.exe
399old mode 100644
400new mode 100755
401diff --git a/another_file.txt b/another_file.txt
402index d03e242..435ad3a 100755
403END
404    expectedReturn => [
405{
406    svnConvertedText => <<'END',
407Index: foo.exe
408old mode 100644
409new mode 100755
410END
411    executableBitDelta => 1,
412    indexPath => "foo.exe",
413},
414"diff --git a/another_file.txt b/another_file.txt\n"],
415    expectedNextLine => "index d03e242..435ad3a 100755\n",
416},
417{
418    # New test case
419    diffName => "New executable file",
420    inputText => <<'END',
421diff --git a/foo b/foo
422new file mode 100755
423index 0000000..d03e242
424--- /dev/null
425+++ b/foo
426@@ -0,0 +1 @@
427+file contents
428
429END
430    expectedReturn => [
431{
432    svnConvertedText => <<'END',
433Index: foo
434new file mode 100755
435index 0000000..d03e242
436--- foo
437+++ foo
438END
439    executableBitDelta => 1,
440    indexPath => "foo",
441    isNew => 1,
442},
443"@@ -0,0 +1 @@\n"],
444    expectedNextLine => "+file contents\n",
445},
446{
447    # New test case
448    diffName => "Deleted executable file",
449    inputText => <<'END',
450diff --git a/foo b/foo
451deleted file mode 100755
452index d03e242..0000000
453--- a/foo
454+++ /dev/null
455@@ -1 +0,0 @@
456-file contents
457
458END
459    expectedReturn => [
460{
461    svnConvertedText => <<'END',
462Index: foo
463deleted file mode 100755
464index d03e242..0000000
465--- foo
466+++ foo
467END
468    executableBitDelta => -1,
469    indexPath => "foo",
470    isDeletion => 1,
471},
472"@@ -1 +0,0 @@\n"],
473    expectedNextLine => "-file contents\n",
474},
475);
476
477my $testCasesCount = @testCaseHashRefs;
478plan(tests => 2 * $testCasesCount); # Total number of assertions.
479
480foreach my $testCase (@testCaseHashRefs) {
481    my $testNameStart = "parseGitDiffHeader(): $testCase->{diffName}: comparing";
482
483    my $fileHandle;
484    open($fileHandle, "<", \$testCase->{inputText});
485    my $line = <$fileHandle>;
486
487    my @got = VCSUtils::parseGitDiffHeader($fileHandle, $line);
488    my $expectedReturn = $testCase->{expectedReturn};
489
490    is_deeply(\@got, $expectedReturn, "$testNameStart return value.");
491
492    my $gotNextLine = <$fileHandle>;
493    is($gotNextLine, $testCase->{expectedNextLine},  "$testNameStart next read line.");
494}
495