1 /*
2 * Regression tests for the diff/diff3 library -- parsing unidiffs
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25 #include "../svn_test.h"
26
27 #include "svn_diff.h"
28 #include "svn_hash.h"
29 #include "svn_mergeinfo.h"
30 #include "svn_pools.h"
31 #include "svn_utf.h"
32
33 /* Used to terminate lines in large multi-line string literals. */
34 #define NL APR_EOL_STR
35
36 static const char *unidiff =
37 "Index: A/mu (deleted)" NL
38 "===================================================================" NL
39 "Index: A/C/gamma" NL
40 "===================================================================" NL
41 "--- A/C/gamma\t(revision 2)" NL
42 "+++ A/C/gamma\t(working copy)" NL
43 "@@ -1 +1,2 @@" NL
44 " This is the file 'gamma'." NL
45 "+some more bytes to 'gamma'" NL
46 "Index: A/D/gamma" NL
47 "===================================================================" NL
48 "--- A/D/gamma.orig" NL
49 "+++ A/D/gamma" NL
50 "@@ -1,2 +1 @@" NL
51 " This is the file 'gamma'." NL
52 "-some less bytes to 'gamma'" NL
53 "" NL
54 "Property changes on: mu-ng" NL
55 "___________________________________________________________________" NL
56 "Name: newprop" NL
57 " + newpropval" NL
58 "Name: svn:mergeinfo" NL
59 "" NL;
60
61 static const char *git_unidiff =
62 "Index: A/mu (deleted)" NL
63 "===================================================================" NL
64 "diff --git a/A/mu b/A/mu" NL
65 "deleted file mode 100644" NL
66 "Index: A/C/gamma" NL
67 "===================================================================" NL
68 "diff --git a/A/C/gamma b/A/C/gamma" NL
69 "old mode 100644" NL
70 "new mode 100755" NL
71 "--- a/A/C/gamma\t(revision 2)" NL
72 "+++ b/A/C/gamma\t(working copy)" NL
73 "@@ -1 +1,2 @@" NL
74 " This is the file 'gamma'." NL
75 "+some more bytes to 'gamma'" NL
76 "Index: iota" NL
77 "===================================================================" NL
78 "diff --git a/iota b/iota.copied" NL
79 "copy from iota" NL
80 "copy to iota.copied" NL
81 "Index: new" NL
82 "===================================================================" NL
83 "diff --git a/new b/new" NL
84 "new file mode 100644" NL
85 "" NL;
86
87 static const char *git_tree_and_text_unidiff =
88 "Index: iota.copied" NL
89 "===================================================================" NL
90 "diff --git a/iota b/iota.copied" NL
91 "old mode 100644" NL
92 "new mode 100755" NL
93 "copy from iota" NL
94 "copy to iota.copied" NL
95 "--- a/iota\t(revision 2)" NL
96 "+++ b/iota.copied\t(working copy)" NL
97 "@@ -1 +1,2 @@" NL
98 " This is the file 'iota'." NL
99 "+some more bytes to 'iota'" NL
100 "Index: A/mu.moved" NL
101 "===================================================================" NL
102 "diff --git a/A/mu b/A/mu.moved" NL
103 "old mode 100644" NL
104 "new mode 100755" NL
105 "rename from A/mu" NL
106 "rename to A/mu.moved" NL
107 "--- a/A/mu\t(revision 2)" NL
108 "+++ b/A/mu.moved\t(working copy)" NL
109 "@@ -1 +1,2 @@" NL
110 " This is the file 'mu'." NL
111 "+some more bytes to 'mu'" NL
112 "Index: new" NL
113 "===================================================================" NL
114 "diff --git a/new b/new" NL
115 "new file mode 100644" NL
116 "--- /dev/null\t(revision 0)" NL
117 "+++ b/new\t(working copy)" NL
118 "@@ -0,0 +1 @@" NL
119 "+This is the file 'new'." NL
120 "Index: A/B/lambda" NL
121 "===================================================================" NL
122 "diff --git a/A/B/lambda b/A/B/lambda" NL
123 "deleted file mode 100755" NL
124 "--- a/A/B/lambda\t(revision 2)" NL
125 "+++ /dev/null\t(working copy)" NL
126 "@@ -1 +0,0 @@" NL
127 "-This is the file 'lambda'." NL
128 "" NL;
129
130 /* Only the last git diff header is valid. The other ones either misses a
131 * path element or has noise between lines that must be continous. See
132 * issue #3809. */
133 static const char *bad_git_diff_header =
134 "Index: iota.copied" NL
135 "===================================================================" NL
136 "diff --git a/foo1 b/" NL
137 "diff --git a/foo2 b" NL
138 "diff --git a/foo3 " NL
139 "diff --git a/foo3 " NL
140 "diff --git foo4 b/foo4" NL
141 "diff --git a/foo5 b/foo5" NL
142 "random noise" NL
143 "diff --git a/foo6 b/foo6" NL
144 "copy from foo6" NL
145 "random noise" NL
146 "copy to foo6" NL
147 "diff --git a/foo6 b/foo6" NL
148 "copy from foo6" NL
149 "diff --git a/iota b/iota.copied" NL
150 "copy from iota" NL
151 "copy to iota.copied" NL
152 "@@ -1 +1,2 @@" NL
153 " This is the file 'iota'." NL
154 "+some more bytes to 'iota'" NL
155 "" NL;
156
157 static const char *property_unidiff =
158 "Index: iota" NL
159 "===================================================================" NL
160 "--- iota" NL
161 "+++ iota" NL
162 "" NL
163 "Property changes on: iota" NL
164 "___________________________________________________________________" NL
165 "Deleted: prop_del" NL
166 "## -1 +0,0 ##" NL
167 "-value" NL
168 "" NL
169 "Property changes on: iota" NL
170 "___________________________________________________________________" NL
171 "Added: prop_add" NL
172 "## -0,0 +1 ##" NL
173 "+value" NL
174 "" NL
175 "Property changes on: iota" NL
176 "___________________________________________________________________" NL
177 "Modified: prop_mod" NL
178 "## -1,4 +1,4 ##" NL
179 "-value" NL
180 "+new value" NL
181 " context" NL
182 " context" NL
183 " context" NL
184 "## -10,4 +10,4 ##" NL
185 " context" NL
186 " context" NL
187 " context" NL
188 "-value" NL
189 "+new value" NL
190 "" NL;
191
192 /* ### Add edge cases like context lines stripped from leading whitespaces
193 * ### that starts with 'Added: ', 'Deleted: ' or 'Modified: '. */
194 static const char *property_and_text_unidiff =
195 "Index: iota" NL
196 "===================================================================" NL
197 "--- iota" NL
198 "+++ iota" NL
199 "@@ -1 +1,2 @@" NL
200 " This is the file 'iota'." NL
201 "+some more bytes to 'iota'" NL
202 "" NL
203 "Property changes on: iota" NL
204 "___________________________________________________________________" NL
205 "Added: prop_add" NL
206 "## -0,0 +1 ##" NL
207 "+value" NL;
208
209 /* A unidiff containing diff symbols in the body of the hunks. */
210 static const char *diff_symbols_in_prop_unidiff =
211 "Index: iota" NL
212 "===================================================================" NL
213 "--- iota" NL
214 "+++ iota" NL
215 "" NL
216 "Property changes on: iota" NL
217 "___________________________________________________________________" NL
218 "Added: prop_add" NL
219 "## -0,0 +1,3 ##" NL
220 "+Added: bogus_prop" NL
221 "+## -0,0 +20 ##" NL
222 "+@@ -1,2 +0,0 @@" NL
223 "Deleted: prop_del" NL
224 "## -1,2 +0,0 ##" NL
225 "---- iota" NL
226 "-+++ iota" NL
227 "Modified: non-existent" NL
228 "blah, just noise - no valid hunk header" NL
229 "Modified: prop_mod" NL
230 "## -1,4 +1,4 ##" NL
231 "-## -1,2 +1,2 ##" NL
232 "+## -1,3 +1,3 ##" NL
233 " ## -1,5 -0,0 ##" NL
234 " @@ -1,5 -0,0 @@" NL
235 " Modified: prop_mod" NL
236 "## -10,4 +10,4 ##" NL
237 " context" NL
238 " context" NL
239 " context" NL
240 "-## -0,0 +1 ##" NL
241 "+## -1,2 +1,4 ##" NL
242 "" NL;
243
244 /* A unidiff containing paths with spaces. */
245 static const char *path_with_spaces_unidiff =
246 "diff --git a/path 1 b/path 1" NL
247 "new file mode 100644" NL
248 "diff --git a/path one 1 b/path one 1" NL
249 "new file mode 100644" NL
250 "diff --git a/dir/ b/path b/dir/ b/path" NL
251 "new file mode 100644" NL
252 "diff --git a/ b/path 1 b/ b/path 1" NL
253 "new file mode 100644" NL;
254
255 static const char *unidiff_lacking_trailing_eol =
256 "Index: A/C/gamma" NL
257 "===================================================================" NL
258 "--- A/C/gamma\t(revision 2)" NL
259 "+++ A/C/gamma\t(working copy)" NL
260 "@@ -1 +1,2 @@" NL
261 " This is the file 'gamma'." NL
262 "+some more bytes to 'gamma'"; /* Don't add NL after this line */
263
264 static const char *unidiff_with_mergeinfo =
265 "Index: A/C" NL
266 "===================================================================" NL
267 "--- A/C\t(revision 2)" NL
268 "+++ A/C\t(working copy)" NL
269 "Modified: svn:ignore" NL
270 "## -7,6 +7,7 ##" NL
271 " configure" NL
272 " libtool" NL
273 " .gdb_history" NL
274 "+.swig_checked" NL
275 " *.orig" NL
276 " *.rej" NL
277 " TAGS" NL
278 "Modified: svn:mergeinfo" NL
279 "## -0,1 +0,3 ##" NL
280 " Reverse-merged /subversion/branches/1.6.x-r935631:r952683-955333" NL
281 " /subversion/branches/nfc-nfd-aware-client:r870276,870376 をマージしました"NL
282 " Fusionné /subversion/branches/1.7.x-r1507044:r1507300-1511568" NL
283 " Merged /subversion/branches/1.8.x-openssl-dirs:r1535139" NL;
284 /* The above diff intentionally contains i18n versions of some lines. */
285
286 /* Create a PATCH_FILE containing the contents of DIFF. */
287 static svn_error_t *
create_patch_file(svn_patch_file_t ** patch_file,const char * diff,apr_pool_t * pool)288 create_patch_file(svn_patch_file_t **patch_file,
289 const char *diff, apr_pool_t *pool)
290 {
291 apr_size_t bytes;
292 apr_size_t len;
293 const char *path;
294 apr_file_t *apr_file;
295
296 /* Create a patch file. */
297 SVN_ERR(svn_io_open_unique_file3(&apr_file, &path, NULL,
298 svn_io_file_del_on_pool_cleanup,
299 pool, pool));
300
301 bytes = strlen(diff);
302 SVN_ERR(svn_io_file_write_full(apr_file, diff, bytes, &len, pool));
303 if (len != bytes)
304 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
305 "Cannot write to '%s'", path);
306 SVN_ERR(svn_io_file_close(apr_file, pool));
307 SVN_ERR(svn_diff_open_patch_file(patch_file, path, pool));
308
309 return SVN_NO_ERROR;
310 }
311
312 /* svn_stream_readline() with hunk reader semantics */
313 static svn_error_t *
stream_readline_diff(svn_stream_t * stream,svn_stringbuf_t ** buf,const char * eol,svn_boolean_t * eof,apr_pool_t * result_pool)314 stream_readline_diff(svn_stream_t *stream,
315 svn_stringbuf_t **buf,
316 const char *eol,
317 svn_boolean_t *eof,
318 apr_pool_t *result_pool)
319 {
320 SVN_ERR(svn_stream_readline(stream, buf, eol, eof, result_pool));
321
322 /* Hunks are only at EOF after they are completely read, even if
323 they don't have a final EOL in the text */
324 if (*eof && (*buf)->len)
325 *eof = FALSE;
326
327 return SVN_NO_ERROR;
328 }
329
330 /* Check that reading a line from HUNK equals what's inside EXPECTED.
331 * If ORIGINAL is TRUE, read the original hunk text; else, read the
332 * modified hunk text. */
333 static svn_error_t *
check_content(svn_diff_hunk_t * hunk,svn_boolean_t original,const char * expected,apr_pool_t * pool)334 check_content(svn_diff_hunk_t *hunk, svn_boolean_t original,
335 const char *expected, apr_pool_t *pool)
336 {
337 svn_stream_t *exp;
338 svn_stringbuf_t *exp_buf;
339 svn_stringbuf_t *hunk_buf;
340 svn_boolean_t exp_eof;
341 svn_boolean_t hunk_eof;
342
343 exp = svn_stream_from_string(svn_string_create(expected, pool),
344 pool);
345
346 while (TRUE)
347 {
348 SVN_ERR(stream_readline_diff(exp, &exp_buf, NL, &exp_eof, pool));
349 if (original)
350 SVN_ERR(svn_diff_hunk_readline_original_text(hunk, &hunk_buf, NULL,
351 &hunk_eof, pool, pool));
352 else
353 SVN_ERR(svn_diff_hunk_readline_modified_text(hunk, &hunk_buf, NULL,
354 &hunk_eof, pool, pool));
355
356 SVN_TEST_ASSERT(exp_eof == hunk_eof);
357 if (exp_eof)
358 break;
359 SVN_TEST_STRING_ASSERT(exp_buf->data, hunk_buf->data);
360 }
361
362 if (!hunk_eof)
363 SVN_TEST_ASSERT(hunk_buf->len == 0);
364
365 return SVN_NO_ERROR;
366 }
367
368 static svn_error_t *
test_parse_unidiff(apr_pool_t * pool)369 test_parse_unidiff(apr_pool_t *pool)
370 {
371 svn_patch_file_t *patch_file;
372 svn_boolean_t reverse;
373 svn_boolean_t ignore_whitespace;
374 int i;
375 apr_pool_t *iterpool;
376
377 reverse = FALSE;
378 ignore_whitespace = FALSE;
379 iterpool = svn_pool_create(pool);
380 for (i = 0; i < 2; i++)
381 {
382 svn_patch_t *patch;
383 svn_diff_hunk_t *hunk;
384
385 svn_pool_clear(iterpool);
386
387 SVN_ERR(create_patch_file(&patch_file, unidiff, pool));
388
389 /* We have two patches with one hunk each.
390 * Parse the first patch. */
391 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file, reverse,
392 ignore_whitespace, iterpool,
393 iterpool));
394 SVN_TEST_ASSERT(patch);
395 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/C/gamma");
396 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/C/gamma");
397 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
398
399 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
400 SVN_ERR(check_content(hunk, ! reverse,
401 "This is the file 'gamma'." NL,
402 pool));
403
404 SVN_ERR(check_content(hunk, reverse,
405 "This is the file 'gamma'." NL
406 "some more bytes to 'gamma'" NL,
407 pool));
408
409 /* Parse the second patch. */
410 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file, reverse,
411 ignore_whitespace, pool, pool));
412 SVN_TEST_ASSERT(patch);
413 if (reverse)
414 {
415 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/D/gamma.orig");
416 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/D/gamma");
417 }
418 else
419 {
420 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/D/gamma.orig");
421 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/D/gamma");
422 }
423 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
424
425 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
426 SVN_ERR(check_content(hunk, ! reverse,
427 "This is the file 'gamma'." NL
428 "some less bytes to 'gamma'" NL,
429 pool));
430
431 SVN_ERR(check_content(hunk, reverse,
432 "This is the file 'gamma'." NL,
433 pool));
434
435 reverse = !reverse;
436 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
437 }
438 svn_pool_destroy(iterpool);
439 return SVN_NO_ERROR;
440 }
441
442 static svn_error_t *
test_parse_git_diff(apr_pool_t * pool)443 test_parse_git_diff(apr_pool_t *pool)
444 {
445 /* ### Should we check for reversed diffs? */
446
447 svn_patch_file_t *patch_file;
448 svn_patch_t *patch;
449 svn_diff_hunk_t *hunk;
450
451 SVN_ERR(create_patch_file(&patch_file, git_unidiff, pool));
452
453 /* Parse a deleted empty file */
454 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
455 FALSE, /* reverse */
456 FALSE, /* ignore_whitespace */
457 pool, pool));
458 SVN_TEST_ASSERT(patch);
459 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/mu");
460 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/mu");
461 SVN_TEST_ASSERT(patch->operation == svn_diff_op_deleted);
462 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
463
464 /* Parse a modified file. */
465 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
466 FALSE, /* reverse */
467 FALSE, /* ignore_whitespace */
468 pool, pool));
469 SVN_TEST_ASSERT(patch);
470 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/C/gamma");
471 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/C/gamma");
472 SVN_TEST_ASSERT(patch->operation == svn_diff_op_modified);
473 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
474 SVN_TEST_ASSERT(patch->old_executable_bit == svn_tristate_false);
475 SVN_TEST_ASSERT(patch->new_executable_bit == svn_tristate_true);
476 SVN_TEST_ASSERT(patch->old_symlink_bit == svn_tristate_false);
477 SVN_TEST_ASSERT(patch->new_symlink_bit == svn_tristate_false);
478
479 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
480
481 SVN_ERR(check_content(hunk, TRUE,
482 "This is the file 'gamma'." NL,
483 pool));
484
485 SVN_ERR(check_content(hunk, FALSE,
486 "This is the file 'gamma'." NL
487 "some more bytes to 'gamma'" NL,
488 pool));
489
490 /* Parse a copied empty file */
491 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
492 FALSE, /* reverse */
493 FALSE, /* ignore_whitespace */
494 pool, pool));
495
496 SVN_TEST_ASSERT(patch);
497 SVN_TEST_STRING_ASSERT(patch->old_filename, "iota");
498 SVN_TEST_STRING_ASSERT(patch->new_filename, "iota.copied");
499 SVN_TEST_ASSERT(patch->operation == svn_diff_op_copied);
500 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
501
502 /* Parse an added empty file */
503 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
504 FALSE, /* reverse */
505 FALSE, /* ignore_whitespace */
506 pool, pool));
507
508 SVN_TEST_ASSERT(patch);
509 SVN_TEST_STRING_ASSERT(patch->old_filename, "new");
510 SVN_TEST_STRING_ASSERT(patch->new_filename, "new");
511 SVN_TEST_ASSERT(patch->operation == svn_diff_op_added);
512 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
513 SVN_TEST_ASSERT(patch->old_executable_bit == svn_tristate_unknown);
514 SVN_TEST_ASSERT(patch->new_executable_bit == svn_tristate_false);
515 SVN_TEST_ASSERT(patch->old_symlink_bit == svn_tristate_unknown);
516 SVN_TEST_ASSERT(patch->new_symlink_bit == svn_tristate_false);
517
518 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
519
520 return SVN_NO_ERROR;
521 }
522
523 static svn_error_t *
test_parse_git_tree_and_text_diff(apr_pool_t * pool)524 test_parse_git_tree_and_text_diff(apr_pool_t *pool)
525 {
526 /* ### Should we check for reversed diffs? */
527
528 svn_patch_file_t *patch_file;
529 svn_patch_t *patch;
530 svn_diff_hunk_t *hunk;
531
532 SVN_ERR(create_patch_file(&patch_file, git_tree_and_text_unidiff, pool));
533
534 /* Parse a copied file with text modifications. */
535 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
536 FALSE, /* reverse */
537 FALSE, /* ignore_whitespace */
538 pool, pool));
539 SVN_TEST_ASSERT(patch);
540 SVN_TEST_STRING_ASSERT(patch->old_filename, "iota");
541 SVN_TEST_STRING_ASSERT(patch->new_filename, "iota.copied");
542 SVN_TEST_ASSERT(patch->old_executable_bit == svn_tristate_false);
543 SVN_TEST_ASSERT(patch->new_executable_bit == svn_tristate_true);
544 SVN_TEST_ASSERT(patch->old_symlink_bit == svn_tristate_false);
545 SVN_TEST_ASSERT(patch->new_symlink_bit == svn_tristate_false);
546 SVN_TEST_ASSERT(patch->operation == svn_diff_op_copied);
547 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
548
549 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
550
551 SVN_ERR(check_content(hunk, TRUE,
552 "This is the file 'iota'." NL,
553 pool));
554
555 SVN_ERR(check_content(hunk, FALSE,
556 "This is the file 'iota'." NL
557 "some more bytes to 'iota'" NL,
558 pool));
559
560 /* Parse a moved file with text modifications. */
561 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
562 FALSE, /* reverse */
563 FALSE, /* ignore_whitespace */
564 pool, pool));
565 SVN_TEST_ASSERT(patch);
566 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/mu");
567 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/mu.moved");
568 SVN_TEST_ASSERT(patch->old_executable_bit == svn_tristate_false);
569 SVN_TEST_ASSERT(patch->new_executable_bit == svn_tristate_true);
570 SVN_TEST_ASSERT(patch->old_symlink_bit == svn_tristate_false);
571 SVN_TEST_ASSERT(patch->new_symlink_bit == svn_tristate_false);
572 SVN_TEST_ASSERT(patch->operation == svn_diff_op_moved);
573 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
574
575 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
576
577 SVN_ERR(check_content(hunk, TRUE,
578 "This is the file 'mu'." NL,
579 pool));
580
581 SVN_ERR(check_content(hunk, FALSE,
582 "This is the file 'mu'." NL
583 "some more bytes to 'mu'" NL,
584 pool));
585
586 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
587 FALSE, /* reverse */
588 FALSE, /* ignore_whitespace */
589 pool, pool));
590 SVN_TEST_ASSERT(patch);
591 SVN_TEST_STRING_ASSERT(patch->old_filename, "/dev/null");
592 SVN_TEST_STRING_ASSERT(patch->new_filename, "new");
593 SVN_TEST_ASSERT(patch->operation == svn_diff_op_added);
594 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
595
596 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
597
598 SVN_ERR(check_content(hunk, TRUE,
599 "",
600 pool));
601
602 SVN_ERR(check_content(hunk, FALSE,
603 "This is the file 'new'." NL,
604 pool));
605
606 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
607 FALSE, /* reverse */
608 FALSE, /* ignore_whitespace */
609 pool, pool));
610 SVN_TEST_ASSERT(patch);
611 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/B/lambda");
612 SVN_TEST_STRING_ASSERT(patch->new_filename, "/dev/null");
613 SVN_TEST_ASSERT(patch->operation == svn_diff_op_deleted);
614 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
615 SVN_TEST_ASSERT(patch->old_executable_bit == svn_tristate_true);
616 SVN_TEST_ASSERT(patch->new_executable_bit == svn_tristate_unknown);
617 SVN_TEST_ASSERT(patch->old_symlink_bit == svn_tristate_false);
618 SVN_TEST_ASSERT(patch->new_symlink_bit == svn_tristate_unknown);
619
620 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
621
622 SVN_ERR(check_content(hunk, TRUE,
623 "This is the file 'lambda'." NL,
624 pool));
625
626 SVN_ERR(check_content(hunk, FALSE,
627 "",
628 pool));
629
630 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
631 return SVN_NO_ERROR;
632 }
633
634 /* Tests to parse non-valid git diffs. */
635 static svn_error_t *
test_bad_git_diff_headers(apr_pool_t * pool)636 test_bad_git_diff_headers(apr_pool_t *pool)
637 {
638 svn_patch_file_t *patch_file;
639 svn_patch_t *patch;
640 svn_diff_hunk_t *hunk;
641
642 SVN_ERR(create_patch_file(&patch_file, bad_git_diff_header, pool));
643
644 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
645 FALSE, /* reverse */
646 FALSE, /* ignore_whitespace */
647 pool, pool));
648 SVN_TEST_ASSERT(patch);
649 SVN_TEST_STRING_ASSERT(patch->old_filename, "iota");
650 SVN_TEST_STRING_ASSERT(patch->new_filename, "iota.copied");
651 SVN_TEST_ASSERT(patch->operation == svn_diff_op_copied);
652 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
653
654 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
655
656 SVN_ERR(check_content(hunk, TRUE,
657 "This is the file 'iota'." NL,
658 pool));
659
660 SVN_ERR(check_content(hunk, FALSE,
661 "This is the file 'iota'." NL
662 "some more bytes to 'iota'" NL,
663 pool));
664
665 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
666 return SVN_NO_ERROR;
667 }
668
669 /* Tests to parse a diff with three property changes, one is added, one is
670 * modified and one is deleted. */
671 static svn_error_t *
test_parse_property_diff(apr_pool_t * pool)672 test_parse_property_diff(apr_pool_t *pool)
673 {
674 svn_patch_file_t *patch_file;
675 svn_patch_t *patch;
676 svn_prop_patch_t *prop_patch;
677 svn_diff_hunk_t *hunk;
678 apr_array_header_t *hunks;
679
680 SVN_ERR(create_patch_file(&patch_file, property_unidiff, pool));
681
682 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
683 FALSE, /* reverse */
684 FALSE, /* ignore_whitespace */
685 pool, pool));
686 SVN_TEST_ASSERT(patch);
687 SVN_TEST_STRING_ASSERT(patch->old_filename, "iota");
688 SVN_TEST_STRING_ASSERT(patch->new_filename, "iota");
689 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
690 SVN_TEST_ASSERT(apr_hash_count(patch->prop_patches) == 3);
691
692 /* Check the deleted property */
693 prop_patch = apr_hash_get(patch->prop_patches, "prop_del",
694 APR_HASH_KEY_STRING);
695
696 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_deleted);
697 hunks = prop_patch->hunks;
698
699 SVN_TEST_ASSERT(hunks->nelts == 1);
700 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
701
702 SVN_ERR(check_content(hunk, TRUE,
703 "value" NL,
704 pool));
705
706 SVN_ERR(check_content(hunk, FALSE,
707 "",
708 pool));
709
710 /* Check the added property */
711 prop_patch = apr_hash_get(patch->prop_patches, "prop_add",
712 APR_HASH_KEY_STRING);
713
714 SVN_TEST_STRING_ASSERT(prop_patch->name, "prop_add");
715 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_added);
716 hunks = prop_patch->hunks;
717
718 SVN_TEST_ASSERT(hunks->nelts == 1);
719 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
720
721 SVN_ERR(check_content(hunk, TRUE,
722 "",
723 pool));
724
725 SVN_ERR(check_content(hunk, FALSE,
726 "value" NL,
727 pool));
728
729 /* Check the modified property */
730 prop_patch = apr_hash_get(patch->prop_patches, "prop_mod",
731 APR_HASH_KEY_STRING);
732
733 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_modified);
734 hunks = prop_patch->hunks;
735
736 SVN_TEST_ASSERT(hunks->nelts == 2);
737 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
738
739 SVN_ERR(check_content(hunk, TRUE,
740 "value" NL
741 "context" NL
742 "context" NL
743 "context" NL,
744 pool));
745
746 SVN_ERR(check_content(hunk, FALSE,
747 "new value" NL
748 "context" NL
749 "context" NL
750 "context" NL,
751 pool));
752
753 hunk = APR_ARRAY_IDX(hunks, 1 , svn_diff_hunk_t *);
754
755 SVN_ERR(check_content(hunk, TRUE,
756 "context" NL
757 "context" NL
758 "context" NL
759 "value" NL,
760 pool));
761
762 SVN_ERR(check_content(hunk, FALSE,
763 "context" NL
764 "context" NL
765 "context" NL
766 "new value" NL,
767 pool));
768
769 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
770 return SVN_NO_ERROR;
771 }
772
773 static svn_error_t *
test_parse_property_and_text_diff(apr_pool_t * pool)774 test_parse_property_and_text_diff(apr_pool_t *pool)
775 {
776 svn_patch_file_t *patch_file;
777 svn_patch_t *patch;
778 svn_prop_patch_t *prop_patch;
779 svn_diff_hunk_t *hunk;
780 apr_array_header_t *hunks;
781
782 SVN_ERR(create_patch_file(&patch_file, property_and_text_unidiff, pool));
783
784 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
785 FALSE, /* reverse */
786 FALSE, /* ignore_whitespace */
787 pool, pool));
788 SVN_TEST_ASSERT(patch);
789 SVN_TEST_STRING_ASSERT(patch->old_filename, "iota");
790 SVN_TEST_STRING_ASSERT(patch->new_filename, "iota");
791 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
792 SVN_TEST_ASSERT(apr_hash_count(patch->prop_patches) == 1);
793
794 /* Check contents of text hunk */
795 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
796
797 SVN_ERR(check_content(hunk, TRUE,
798 "This is the file 'iota'." NL,
799 pool));
800
801 SVN_ERR(check_content(hunk, FALSE,
802 "This is the file 'iota'." NL
803 "some more bytes to 'iota'" NL,
804 pool));
805
806 /* Check the added property */
807 prop_patch = apr_hash_get(patch->prop_patches, "prop_add",
808 APR_HASH_KEY_STRING);
809 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_added);
810
811 hunks = prop_patch->hunks;
812 SVN_TEST_ASSERT(hunks->nelts == 1);
813 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
814
815 SVN_ERR(check_content(hunk, TRUE,
816 "",
817 pool));
818
819 SVN_ERR(check_content(hunk, FALSE,
820 "value" NL,
821 pool));
822
823 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
824 return SVN_NO_ERROR;
825 }
826
827 static svn_error_t *
test_parse_diff_symbols_in_prop_unidiff(apr_pool_t * pool)828 test_parse_diff_symbols_in_prop_unidiff(apr_pool_t *pool)
829 {
830 svn_patch_t *patch;
831 svn_patch_file_t *patch_file;
832 svn_prop_patch_t *prop_patch;
833 svn_diff_hunk_t *hunk;
834 apr_array_header_t *hunks;
835
836 SVN_ERR(create_patch_file(&patch_file, diff_symbols_in_prop_unidiff, pool));
837
838 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
839 FALSE, /* reverse */
840 FALSE, /* ignore_whitespace */
841 pool, pool));
842 SVN_TEST_ASSERT(patch);
843 SVN_TEST_STRING_ASSERT(patch->old_filename, "iota");
844 SVN_TEST_STRING_ASSERT(patch->new_filename, "iota");
845 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
846 SVN_TEST_ASSERT(apr_hash_count(patch->prop_patches) == 3);
847
848 /* Check the added property */
849 prop_patch = apr_hash_get(patch->prop_patches, "prop_add",
850 APR_HASH_KEY_STRING);
851 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_added);
852
853 hunks = prop_patch->hunks;
854 SVN_TEST_ASSERT(hunks->nelts == 1);
855 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
856
857 SVN_ERR(check_content(hunk, TRUE,
858 "",
859 pool));
860
861 SVN_ERR(check_content(hunk, FALSE,
862 "Added: bogus_prop" NL
863 "## -0,0 +20 ##" NL
864 "@@ -1,2 +0,0 @@" NL,
865 pool));
866
867 /* Check the deleted property */
868 prop_patch = apr_hash_get(patch->prop_patches, "prop_del",
869 APR_HASH_KEY_STRING);
870 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_deleted);
871
872 hunks = prop_patch->hunks;
873 SVN_TEST_ASSERT(hunks->nelts == 1);
874 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
875
876 SVN_ERR(check_content(hunk, TRUE,
877 "--- iota" NL
878 "+++ iota" NL,
879 pool));
880
881 SVN_ERR(check_content(hunk, FALSE,
882 "",
883 pool));
884
885 /* Check the modified property */
886 prop_patch = apr_hash_get(patch->prop_patches, "prop_mod",
887 APR_HASH_KEY_STRING);
888 SVN_TEST_ASSERT(prop_patch->operation == svn_diff_op_modified);
889 hunks = prop_patch->hunks;
890 SVN_TEST_ASSERT(hunks->nelts == 2);
891 hunk = APR_ARRAY_IDX(hunks, 0 , svn_diff_hunk_t *);
892
893 SVN_ERR(check_content(hunk, TRUE,
894 "## -1,2 +1,2 ##" NL
895 "## -1,5 -0,0 ##" NL
896 "@@ -1,5 -0,0 @@" NL
897 "Modified: prop_mod" NL,
898 pool));
899
900 SVN_ERR(check_content(hunk, FALSE,
901 "## -1,3 +1,3 ##" NL
902 "## -1,5 -0,0 ##" NL
903 "@@ -1,5 -0,0 @@" NL
904 "Modified: prop_mod" NL,
905 pool));
906
907 hunk = APR_ARRAY_IDX(hunks, 1 , svn_diff_hunk_t *);
908
909 SVN_ERR(check_content(hunk, TRUE,
910 "context" NL
911 "context" NL
912 "context" NL
913 "## -0,0 +1 ##" NL,
914 pool));
915
916 SVN_ERR(check_content(hunk, FALSE,
917 "context" NL
918 "context" NL
919 "context" NL
920 "## -1,2 +1,4 ##" NL,
921 pool));
922
923 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
924 return SVN_NO_ERROR;
925 }
926
927 static svn_error_t *
test_git_diffs_with_spaces_diff(apr_pool_t * pool)928 test_git_diffs_with_spaces_diff(apr_pool_t *pool)
929 {
930 svn_patch_file_t *patch_file;
931 svn_patch_t *patch;
932
933 SVN_ERR(create_patch_file(&patch_file, path_with_spaces_unidiff, pool));
934
935 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
936 FALSE, /* reverse */
937 FALSE, /* ignore_whitespace */
938 pool, pool));
939 SVN_TEST_ASSERT(patch);
940 SVN_TEST_STRING_ASSERT(patch->old_filename, "path 1");
941 SVN_TEST_STRING_ASSERT(patch->new_filename, "path 1");
942 SVN_TEST_ASSERT(patch->operation == svn_diff_op_added);
943 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
944
945 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
946 FALSE, /* reverse */
947 FALSE, /* ignore_whitespace */
948 pool, pool));
949 SVN_TEST_ASSERT(patch);
950 SVN_TEST_STRING_ASSERT(patch->old_filename, "path one 1");
951 SVN_TEST_STRING_ASSERT(patch->new_filename, "path one 1");
952 SVN_TEST_ASSERT(patch->operation == svn_diff_op_added);
953 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
954
955 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
956 FALSE, /* reverse */
957 FALSE, /* ignore_whitespace */
958 pool, pool));
959 SVN_TEST_ASSERT(patch);
960 SVN_TEST_STRING_ASSERT(patch->old_filename, "dir/ b/path");
961 SVN_TEST_STRING_ASSERT(patch->new_filename, "dir/ b/path");
962 SVN_TEST_ASSERT(patch->operation == svn_diff_op_added);
963 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
964
965 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
966 FALSE, /* reverse */
967 FALSE, /* ignore_whitespace */
968 pool, pool));
969 SVN_TEST_ASSERT(patch);
970 SVN_TEST_STRING_ASSERT(patch->old_filename, " b/path 1");
971 SVN_TEST_STRING_ASSERT(patch->new_filename, " b/path 1");
972 SVN_TEST_ASSERT(patch->operation == svn_diff_op_added);
973 SVN_TEST_ASSERT(patch->hunks->nelts == 0);
974
975 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
976 return SVN_NO_ERROR;
977 }
978
979 static svn_error_t *
test_parse_unidiff_lacking_trailing_eol(apr_pool_t * pool)980 test_parse_unidiff_lacking_trailing_eol(apr_pool_t *pool)
981 {
982 svn_patch_file_t *patch_file;
983 svn_boolean_t reverse;
984 svn_boolean_t ignore_whitespace;
985 int i;
986 apr_pool_t *iterpool;
987
988 reverse = FALSE;
989 ignore_whitespace = FALSE;
990 iterpool = svn_pool_create(pool);
991 for (i = 0; i < 2; i++)
992 {
993 svn_patch_t *patch;
994 svn_diff_hunk_t *hunk;
995
996 svn_pool_clear(iterpool);
997
998 SVN_ERR(create_patch_file(&patch_file, unidiff_lacking_trailing_eol,
999 pool));
1000
1001 /* We have one patch with one hunk. Parse it. */
1002 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file, reverse,
1003 ignore_whitespace, iterpool,
1004 iterpool));
1005 SVN_TEST_ASSERT(patch);
1006 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/C/gamma");
1007 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/C/gamma");
1008 SVN_TEST_ASSERT(patch->hunks->nelts == 1);
1009
1010 hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_diff_hunk_t *);
1011 SVN_ERR(check_content(hunk, ! reverse,
1012 "This is the file 'gamma'." NL,
1013 pool));
1014
1015 SVN_ERR(check_content(hunk, reverse,
1016 "This is the file 'gamma'." NL
1017 "some more bytes to 'gamma'",
1018 pool));
1019
1020 reverse = !reverse;
1021 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
1022 }
1023 svn_pool_destroy(iterpool);
1024 return SVN_NO_ERROR;
1025 }
1026
1027 static svn_error_t *
test_parse_unidiff_with_mergeinfo(apr_pool_t * pool)1028 test_parse_unidiff_with_mergeinfo(apr_pool_t *pool)
1029 {
1030 svn_patch_file_t *patch_file;
1031 svn_boolean_t reverse;
1032 svn_boolean_t ignore_whitespace;
1033 int i;
1034 apr_pool_t *iterpool;
1035
1036 reverse = FALSE;
1037 ignore_whitespace = FALSE;
1038 iterpool = svn_pool_create(pool);
1039 for (i = 0; i < 2; i++)
1040 {
1041 svn_patch_t *patch;
1042 svn_mergeinfo_t mergeinfo;
1043 svn_mergeinfo_t reverse_mergeinfo;
1044 svn_rangelist_t *rangelist;
1045 svn_merge_range_t *range;
1046
1047 svn_pool_clear(iterpool);
1048
1049 SVN_ERR(create_patch_file(&patch_file, unidiff_with_mergeinfo,
1050 pool));
1051
1052 SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file, reverse,
1053 ignore_whitespace, iterpool,
1054 iterpool));
1055 SVN_TEST_ASSERT(patch);
1056 SVN_TEST_STRING_ASSERT(patch->old_filename, "A/C");
1057 SVN_TEST_STRING_ASSERT(patch->new_filename, "A/C");
1058
1059 /* svn:ignore */
1060 SVN_TEST_ASSERT(apr_hash_count(patch->prop_patches) == 1);
1061
1062 SVN_TEST_ASSERT(patch->mergeinfo);
1063 SVN_TEST_ASSERT(patch->reverse_mergeinfo);
1064
1065 if (reverse)
1066 {
1067 mergeinfo = patch->reverse_mergeinfo;
1068 reverse_mergeinfo = patch->mergeinfo;
1069 }
1070 else
1071 {
1072 mergeinfo = patch->mergeinfo;
1073 reverse_mergeinfo = patch->reverse_mergeinfo;
1074 }
1075
1076 rangelist = svn_hash_gets(reverse_mergeinfo,
1077 "/subversion/branches/1.6.x-r935631");
1078 SVN_TEST_ASSERT(rangelist);
1079 SVN_TEST_ASSERT(rangelist->nelts == 1);
1080 range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1081 SVN_TEST_ASSERT(range->start == 952682);
1082 SVN_TEST_ASSERT(range->end == 955333);
1083
1084 rangelist = svn_hash_gets(mergeinfo,
1085 "/subversion/branches/nfc-nfd-aware-client");
1086 SVN_TEST_ASSERT(rangelist);
1087 SVN_TEST_ASSERT(rangelist->nelts == 2);
1088 range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1089 SVN_TEST_ASSERT(range->end == 870276);
1090 range = APR_ARRAY_IDX(rangelist, 1, svn_merge_range_t *);
1091 SVN_TEST_ASSERT(range->end == 870376);
1092
1093 rangelist = svn_hash_gets(mergeinfo,
1094 "/subversion/branches/1.8.x-openssl-dirs");
1095 SVN_TEST_ASSERT(rangelist);
1096 SVN_TEST_ASSERT(rangelist->nelts == 1);
1097 range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1098 SVN_TEST_ASSERT(range->end == 1535139);
1099
1100 reverse = !reverse;
1101 SVN_ERR(svn_diff_close_patch_file(patch_file, pool));
1102 }
1103 svn_pool_destroy(iterpool);
1104 return SVN_NO_ERROR;
1105 }
1106
1107 /* ========================================================================== */
1108
1109
1110 static int max_threads = 1;
1111
1112 static struct svn_test_descriptor_t test_funcs[] =
1113 {
1114 SVN_TEST_NULL,
1115 SVN_TEST_PASS2(test_parse_unidiff,
1116 "test unidiff parsing"),
1117 SVN_TEST_PASS2(test_parse_git_diff,
1118 "test git unidiff parsing"),
1119 SVN_TEST_PASS2(test_parse_git_tree_and_text_diff,
1120 "test git unidiff parsing of tree and text changes"),
1121 SVN_TEST_PASS2(test_bad_git_diff_headers,
1122 "test badly formatted git diff headers"),
1123 SVN_TEST_PASS2(test_parse_property_diff,
1124 "test property unidiff parsing"),
1125 SVN_TEST_PASS2(test_parse_property_and_text_diff,
1126 "test property and text unidiff parsing"),
1127 SVN_TEST_PASS2(test_parse_diff_symbols_in_prop_unidiff,
1128 "test property diffs with odd symbols"),
1129 SVN_TEST_PASS2(test_git_diffs_with_spaces_diff,
1130 "test git diffs with spaces in paths"),
1131 SVN_TEST_PASS2(test_parse_unidiff_lacking_trailing_eol,
1132 "test parsing unidiffs lacking trailing eol"),
1133 SVN_TEST_PASS2(test_parse_unidiff_with_mergeinfo,
1134 "test parsing unidiffs with mergeinfo"),
1135 SVN_TEST_NULL
1136 };
1137
1138 SVN_TEST_MAIN
1139