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