1 /*
2  * mergeinfo-test.c -- test the mergeinfo functions
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 #include <stdio.h>
25 #include <string.h>
26 #include <apr_hash.h>
27 #include <apr_tables.h>
28 
29 #define SVN_DEPRECATED
30 
31 #include "svn_hash.h"
32 #include "svn_pools.h"
33 #include "svn_types.h"
34 #include "svn_mergeinfo.h"
35 #include "private/svn_mergeinfo_private.h"
36 #include "private/svn_sorts_private.h"
37 #include "private/svn_error_private.h"
38 #include "../svn_test.h"
39 
40 /* A quick way to create error messages.  */
41 static svn_error_t *
fail(apr_pool_t * pool,const char * fmt,...)42 fail(apr_pool_t *pool, const char *fmt, ...)
43 {
44   va_list ap;
45   char *msg;
46 
47   va_start(ap, fmt);
48   msg = apr_pvsprintf(pool, fmt, ap);
49   va_end(ap);
50 
51   return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg);
52 }
53 
54 #define MAX_NBR_RANGES 5
55 
56 /* Verify that INPUT is parsed properly, and returns an error if
57    parsing fails, or incorret parsing is detected.  Assumes that INPUT
58    contains only one path -> ranges mapping, and that EXPECTED_RANGES points
59    to the first range in an array whose size is greater than or equal to
60    the number of ranges in INPUTS path -> ranges mapping but less than
61    MAX_NBR_RANGES.  If fewer than MAX_NBR_RANGES ranges are present, then the
62    trailing expected_ranges should be have their end revision set to 0. */
63 static svn_error_t *
verify_mergeinfo_parse(const char * input,const char * expected_path,const svn_merge_range_t * expected_ranges,apr_pool_t * pool)64 verify_mergeinfo_parse(const char *input,
65                        const char *expected_path,
66                        const svn_merge_range_t *expected_ranges,
67                        apr_pool_t *pool)
68 {
69   svn_error_t *err;
70   apr_hash_t *path_to_merge_ranges;
71   apr_hash_index_t *hi;
72 
73   /* Test valid input. */
74   err = svn_mergeinfo_parse(&path_to_merge_ranges, input, pool);
75   if (err || apr_hash_count(path_to_merge_ranges) != 1)
76     return svn_error_createf(SVN_ERR_TEST_FAILED, err,
77                              "svn_mergeinfo_parse (%s) failed unexpectedly",
78                              input);
79   for (hi = apr_hash_first(pool, path_to_merge_ranges); hi;
80        hi = apr_hash_next(hi))
81     {
82       const void *path;
83       void *val;
84       svn_rangelist_t *ranges;
85       svn_merge_range_t *range;
86       int j;
87 
88       apr_hash_this(hi, &path, NULL, &val);
89       ranges = val;
90       if (strcmp((const char *) path, expected_path) != 0)
91         return fail(pool, "svn_mergeinfo_parse (%s) failed to parse the "
92                     "correct path (%s)", input, expected_path);
93 
94       /* Test each parsed range. */
95       for (j = 0; j < ranges->nelts; j++)
96         {
97           range = APR_ARRAY_IDX(ranges, j, svn_merge_range_t *);
98           if (range->start != expected_ranges[j].start
99               || range->end != expected_ranges[j].end
100               || range->inheritable != expected_ranges[j].inheritable)
101             return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
102                                      "svn_mergeinfo_parse (%s) failed to "
103                                      "parse the correct range",
104                                      input);
105         }
106 
107       /* Were we expecting any more ranges? */
108       if (j < MAX_NBR_RANGES - 1
109           && expected_ranges[j].end != 0)
110         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
111                                  "svn_mergeinfo_parse (%s) failed to "
112                                  "produce the expected number of ranges",
113                                   input);
114     }
115   return SVN_NO_ERROR;
116 }
117 
118 
119 #define NBR_MERGEINFO_VALS 25
120 
121 /* Valid mergeinfo values. */
122 static const char * const mergeinfo_vals[NBR_MERGEINFO_VALS] =
123   {
124     "/trunk:1",
125     "/trunk/foo:1-6",
126     "/trunk: 5,7-9,10,11,13,14",
127     "/trunk: 3-10,11*,13,14",
128     "/branch: 1,2-18*,33*",
129     /* Path names containing ':'s */
130     "patch-common::netasq-bpf.c:25381",
131     "patch-common_netasq-bpf.c::25381",
132     ":patch:common:netasq:bpf.c:25381",
133     /* Unordered rangelists */
134     "/trunk:3-6,15,18,9,22",
135     "/trunk:5,3",
136     "/trunk:3-6*,15*,18*,9,22*",
137     "/trunk:5,3*",
138     "/trunk:100,3-7,50,99,1-2",
139     /* Overlapping rangelists */
140     "/gunther_branch:5-10,7-12",
141     "/gunther_branch:5-10*,7-12*",
142     "/branches/branch1:43832-45742,49990-53669,43832-49987",
143     /* Unordered and overlapping rangelists */
144     "/gunther_branch:7-12,1,5-10",
145     "/gunther_branch:7-12*,1,5-10*",
146     /* Adjacent rangelists of differing inheritability. */
147     "/b5:5-53,1-4,54-90*",
148     "/c0:1-77,12-44",
149     /* Non-canonical paths. */
150     "/A/:7-8",
151     "/A///:7-8",
152     "/A/.:7-8",
153     "/A/./B:7-8",
154     ":7-8",
155   };
156 /* Paths corresponding to mergeinfo_vals. */
157 static const char * const mergeinfo_paths[NBR_MERGEINFO_VALS] =
158   {
159     "/trunk",
160     "/trunk/foo",
161     "/trunk",
162     "/trunk",
163     "/branch",
164 
165     /* svn_mergeinfo_parse converts relative merge soure paths to absolute. */
166     "/patch-common::netasq-bpf.c",
167     "/patch-common_netasq-bpf.c:",
168     "/:patch:common:netasq:bpf.c",
169 
170     "/trunk",
171     "/trunk",
172     "/trunk",
173     "/trunk",
174     "/trunk",
175     "/gunther_branch",
176     "/gunther_branch",
177     "/branches/branch1",
178     "/gunther_branch",
179     "/gunther_branch",
180     "/b5",
181     "/c0",
182 
183     /* non-canonical paths converted to canonical */
184     "/A",
185     "/A",
186     "/A",
187     "/A/B",
188     "/",
189   };
190 /* First ranges from the paths identified by mergeinfo_paths. */
191 static svn_merge_range_t mergeinfo_ranges[NBR_MERGEINFO_VALS][MAX_NBR_RANGES] =
192   {
193     { {0, 1,  TRUE} },
194     { {0, 6,  TRUE} },
195     { {4, 5,  TRUE}, { 6, 11, TRUE }, {12, 14, TRUE } },
196     { {2, 10, TRUE}, {10, 11, FALSE}, {12, 14, TRUE } },
197     { {0, 1,  TRUE}, { 1, 18, FALSE}, {32, 33, FALSE} },
198     { {25380, 25381, TRUE } },
199     { {25380, 25381, TRUE } },
200     { {25380, 25381, TRUE } },
201     { {2, 6, TRUE}, {8, 9, TRUE}, {14, 15, TRUE}, {17, 18, TRUE},
202       {21, 22, TRUE} },
203     { {2, 3, TRUE}, {4, 5, TRUE} },
204     { {2, 6, FALSE}, {8, 9, TRUE}, {14, 15, FALSE}, {17, 18, FALSE},
205       {21, 22, FALSE} },
206     { {2, 3, FALSE}, {4, 5, TRUE} },
207     { {0, 7, TRUE}, {49, 50, TRUE}, {98, 100, TRUE} },
208     { {4, 12, TRUE} },
209     { {4, 12, FALSE} },
210     { {43831, 49987, TRUE}, {49989, 53669, TRUE} },
211     { {0, 1, TRUE}, {4, 12, TRUE} },
212     { {0, 1, TRUE}, {4, 12, FALSE} },
213     { {0, 53, TRUE}, {53, 90, FALSE} },
214     { {0, 77, TRUE} },
215     { {6, 8, TRUE} },
216     { {6, 8, TRUE} },
217     { {6, 8, TRUE} },
218     { {6, 8, TRUE} },
219     { {6, 8, TRUE} },
220   };
221 
222 static svn_error_t *
test_parse_single_line_mergeinfo(apr_pool_t * pool)223 test_parse_single_line_mergeinfo(apr_pool_t *pool)
224 {
225   int i;
226 
227   for (i = 0; i < NBR_MERGEINFO_VALS; i++)
228     SVN_ERR(verify_mergeinfo_parse(mergeinfo_vals[i], mergeinfo_paths[i],
229                                    mergeinfo_ranges[i], pool));
230 
231   return SVN_NO_ERROR;
232 }
233 
234 static const char *single_mergeinfo = "/trunk: 5,7-9,10,11,13,14";
235 
236 static svn_error_t *
test_mergeinfo_dup(apr_pool_t * pool)237 test_mergeinfo_dup(apr_pool_t *pool)
238 {
239   apr_hash_t *orig_mergeinfo, *copied_mergeinfo;
240   apr_pool_t *subpool;
241   svn_rangelist_t *rangelist;
242 
243   /* Assure that copies which should be empty turn out that way. */
244   subpool = svn_pool_create(pool);
245   orig_mergeinfo = apr_hash_make(subpool);
246   copied_mergeinfo = svn_mergeinfo_dup(orig_mergeinfo, subpool);
247   if (apr_hash_count(copied_mergeinfo) != 0)
248     return fail(pool, "Copied mergeinfo should be empty");
249 
250   /* Create some mergeinfo, copy it using another pool, then destroy
251      the pool with which the original mergeinfo was created. */
252   SVN_ERR(svn_mergeinfo_parse(&orig_mergeinfo, single_mergeinfo, subpool));
253   copied_mergeinfo = svn_mergeinfo_dup(orig_mergeinfo, pool);
254   svn_pool_destroy(subpool);
255   if (apr_hash_count(copied_mergeinfo) != 1)
256     return fail(pool, "Copied mergeinfo should contain one merge source");
257   rangelist = apr_hash_get(copied_mergeinfo, "/trunk", APR_HASH_KEY_STRING);
258   if (! rangelist)
259     return fail(pool, "Expected copied mergeinfo; got nothing");
260   if (rangelist->nelts != 3)
261     return fail(pool, "Copied mergeinfo should contain 3 revision ranges, "
262                 "rather than the %d it contains", rangelist->nelts);
263 
264   return SVN_NO_ERROR;
265 }
266 
267 static svn_error_t *
test_parse_combine_rangeinfo(apr_pool_t * pool)268 test_parse_combine_rangeinfo(apr_pool_t *pool)
269 {
270   apr_array_header_t *result;
271   svn_merge_range_t *resultrange;
272   apr_hash_t *info1;
273 
274   SVN_ERR(svn_mergeinfo_parse(&info1, single_mergeinfo, pool));
275 
276   if (apr_hash_count(info1) != 1)
277     return fail(pool, "Wrong number of paths in parsed mergeinfo");
278 
279   result = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
280   if (!result)
281     return fail(pool, "Missing path in parsed mergeinfo");
282 
283   /* /trunk should have three ranges, 5-5, 7-11, 13-14 */
284   if (result->nelts != 3)
285     return fail(pool, "Parsing failed to combine ranges");
286 
287   resultrange = APR_ARRAY_IDX(result, 0, svn_merge_range_t *);
288 
289   if (resultrange->start != 4 || resultrange->end != 5)
290     return fail(pool, "Range combining produced wrong result");
291 
292   resultrange = APR_ARRAY_IDX(result, 1, svn_merge_range_t *);
293 
294   if (resultrange->start != 6 || resultrange->end != 11)
295     return fail(pool, "Range combining produced wrong result");
296 
297   resultrange = APR_ARRAY_IDX(result, 2, svn_merge_range_t *);
298 
299   if (resultrange->start != 12 || resultrange->end != 14)
300     return fail(pool, "Range combining produced wrong result");
301 
302   return SVN_NO_ERROR;
303 }
304 
305 
306 #define NBR_BROKEN_MERGEINFO_VALS 26
307 /* Invalid mergeinfo values. */
308 static const char * const broken_mergeinfo_vals[NBR_BROKEN_MERGEINFO_VALS] =
309   {
310     /* Invalid grammar  */
311     "/missing-revs",
312     "/trunk: 5,7-9,10,11,13,14,",
313     "/trunk 5,7-9,10,11,13,14",
314     "/trunk:5 7--9 10 11 13 14",
315     /* Overlapping revs differing inheritability */
316     "/trunk:5-9*,9",
317     "/trunk:5,5-9*",
318     "/trunk:5-9,9*",
319     "/trunk:5*,5-9",
320     "/trunk:4,4*",
321     "/trunk:4*,4",
322     "/trunk:3-7*,4-23",
323     "/trunk:3-7,4-23*",
324     /* Reversed revision ranges */
325     "/trunk:22-20",
326     "/trunk:22-20*",
327     "/trunk:3,7-12,22-20,25",
328     "/trunk:3,7,22-20*,25-30",
329     /* Range with same start and end revision */
330     "/trunk:22-22",
331     "/trunk:22-22*",
332     "/trunk:3,7-12,20-20,25",
333     "/trunk:3,7,20-20*,25-30",
334     /* path mapped to range with no revisions */
335     "/trunk:",
336     "/trunk:2-9\n/branch:",
337     "::",
338     /* Invalid revisions */
339     "trunk:a-3",
340     "branch:3-four",
341     "trunk:yadayadayada"
342   };
343 
344 static svn_error_t *
test_parse_broken_mergeinfo(apr_pool_t * pool)345 test_parse_broken_mergeinfo(apr_pool_t *pool)
346 {
347   int i;
348   svn_error_t *err;
349   apr_hash_t *info1;
350 
351   /* Trigger some error(s) with mal-formed input. */
352   for (i = 0; i < NBR_BROKEN_MERGEINFO_VALS; i++)
353     {
354       err = svn_mergeinfo_parse(&info1, broken_mergeinfo_vals[i], pool);
355       if (err == SVN_NO_ERROR)
356         {
357           return fail(pool, "svn_mergeinfo_parse (%s) failed to detect an error",
358                       broken_mergeinfo_vals[i]);
359         }
360       else if (err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR)
361         {
362           svn_error_clear(err);
363           return fail(pool, "svn_mergeinfo_parse (%s) returned some error other"
364                       " than SVN_ERR_MERGEINFO_PARSE_ERROR",
365                       broken_mergeinfo_vals[i]);
366         }
367       else
368         {
369           svn_error_clear(err);
370         }
371     }
372 
373   return SVN_NO_ERROR;
374 }
375 
376 
377 static const char *mergeinfo1 = "/trunk: 3,5,7-9,10,11,13,14\n/fred:8-10";
378 
379 #define NBR_RANGELIST_DELTAS 4
380 
381 
382 /* Convert a single svn_merge_range_t * back into an svn_stringbuf_t *.  */
383 static char *
range_to_string(svn_merge_range_t * range,apr_pool_t * pool)384 range_to_string(svn_merge_range_t *range,
385                 apr_pool_t *pool)
386 {
387   if (range->start == range->end - 1)
388     return apr_psprintf(pool, "%ld%s", range->end,
389                         range->inheritable
390                         ? "" : SVN_MERGEINFO_NONINHERITABLE_STR);
391   else
392     return apr_psprintf(pool, "%ld-%ld%s", range->start + 1,
393                         range->end, range->inheritable
394                         ? "" : SVN_MERGEINFO_NONINHERITABLE_STR);
395 }
396 
397 
398 /* Verify that ACTUAL_RANGELIST matches EXPECTED_RANGES (an array of
399    NBR_EXPECTED length).  Return an error based careful examination if
400    they do not match.  FUNC_VERIFIED is the name of the API being
401    verified (e.g. "svn_rangelist_intersect"), while TYPE is a word
402    describing what the ranges being examined represent. */
403 static svn_error_t *
verify_ranges_match(const svn_rangelist_t * actual_rangelist,svn_merge_range_t * expected_ranges,int nbr_expected,const char * func_verified,const char * type,apr_pool_t * pool)404 verify_ranges_match(const svn_rangelist_t *actual_rangelist,
405                     svn_merge_range_t *expected_ranges, int nbr_expected,
406                     const char *func_verified, const char *type,
407                     apr_pool_t *pool)
408 {
409   int i;
410 
411   if (actual_rangelist->nelts != nbr_expected)
412     return fail(pool, "%s should report %d range %ss, but found %d",
413                 func_verified, nbr_expected, type, actual_rangelist->nelts);
414 
415   for (i = 0; i < actual_rangelist->nelts; i++)
416     {
417       svn_merge_range_t *range = APR_ARRAY_IDX(actual_rangelist, i,
418                                                svn_merge_range_t *);
419       if (range->start != expected_ranges[i].start
420           || range->end != expected_ranges[i].end
421           || range->inheritable != expected_ranges[i].inheritable)
422         return fail(pool, "%s should report range %s, but found %s",
423                     func_verified,
424                     range_to_string(&expected_ranges[i], pool),
425                     range_to_string(range, pool));
426     }
427   return SVN_NO_ERROR;
428 }
429 
430 /* Verify that DELTAS matches EXPECTED_DELTAS (both expected to
431    contain only a rangelist for "/trunk").  Return an error based
432    careful examination if they do not match.  FUNC_VERIFIED is the
433    name of the API being verified (e.g. "svn_mergeinfo_diff"), while
434    TYPE is a word describing what the deltas being examined
435    represent. */
436 static svn_error_t *
verify_mergeinfo_deltas(apr_hash_t * deltas,svn_merge_range_t * expected_deltas,const char * func_verified,const char * type,apr_pool_t * pool)437 verify_mergeinfo_deltas(apr_hash_t *deltas, svn_merge_range_t *expected_deltas,
438                         const char *func_verified, const char *type,
439                         apr_pool_t *pool)
440 {
441   svn_rangelist_t *rangelist;
442 
443   if (apr_hash_count(deltas) != 1)
444     /* Deltas on "/trunk" expected. */
445     return fail(pool, "%s should report 1 path %s, but found %d",
446                 func_verified, type, apr_hash_count(deltas));
447 
448   rangelist = apr_hash_get(deltas, "/trunk", APR_HASH_KEY_STRING);
449   if (rangelist == NULL)
450     return fail(pool, "%s failed to produce a rangelist for /trunk",
451                 func_verified);
452 
453   return verify_ranges_match(rangelist, expected_deltas, NBR_RANGELIST_DELTAS,
454                              func_verified, type, pool);
455 }
456 
457 static svn_error_t *
test_diff_mergeinfo(apr_pool_t * pool)458 test_diff_mergeinfo(apr_pool_t *pool)
459 {
460   apr_hash_t *deleted, *added, *from, *to;
461   svn_merge_range_t expected_rangelist_deletions[NBR_RANGELIST_DELTAS] =
462     { {6, 7, TRUE}, {8, 9, TRUE}, {10, 11, TRUE}, {32, 34, TRUE} };
463   svn_merge_range_t expected_rangelist_additions[NBR_RANGELIST_DELTAS] =
464     { {1, 2, TRUE}, {4, 6, TRUE}, {12, 16, TRUE}, {29, 30, TRUE} };
465 
466   SVN_ERR(svn_mergeinfo_parse(&from, "/trunk: 1,3-4,7,9,11-12,31-34", pool));
467   SVN_ERR(svn_mergeinfo_parse(&to, "/trunk: 1-6,12-16,30-32", pool));
468   /* On /trunk: deleted (7, 9, 11, 33-34) and added (2, 5-6, 13-16, 30) */
469   SVN_ERR(svn_mergeinfo_diff(&deleted, &added, from, to,
470                              FALSE, pool));
471 
472   /* Verify calculation of range list deltas. */
473   SVN_ERR(verify_mergeinfo_deltas(deleted, expected_rangelist_deletions,
474                                   "svn_mergeinfo_diff", "deletion", pool));
475   SVN_ERR(verify_mergeinfo_deltas(added, expected_rangelist_additions,
476                                   "svn_mergeinfo_diff", "addition", pool));
477 
478   return SVN_NO_ERROR;
479 }
480 
481 static svn_error_t *
test_rangelist_reverse(apr_pool_t * pool)482 test_rangelist_reverse(apr_pool_t *pool)
483 {
484   svn_rangelist_t *rangelist;
485   svn_merge_range_t expected_rangelist[3] =
486     { {10, 9, TRUE}, {7, 4, TRUE}, {3, 2, TRUE} };
487 
488   SVN_ERR(svn_rangelist__parse(&rangelist, "3,5-7,10", pool));
489 
490   SVN_ERR(svn_rangelist_reverse(rangelist, pool));
491 
492   return verify_ranges_match(rangelist, expected_rangelist, 3,
493                              "svn_rangelist_reverse", "reversal", pool);
494 }
495 
496 static svn_error_t *
test_rangelist_intersect(apr_pool_t * pool)497 test_rangelist_intersect(apr_pool_t *pool)
498 {
499   svn_rangelist_t *rangelist1, *rangelist2, *intersection;
500 
501   /* Expected intersection when considering inheritance. */
502   svn_merge_range_t intersection_consider_inheritance[] =
503     { {0, 1, TRUE}, {11, 12, TRUE}, {30, 32, FALSE}, {39, 42, TRUE} };
504 
505   /* Expected intersection when ignoring inheritance. */
506   svn_merge_range_t intersection_ignore_inheritance[] =
507     { {0, 1, TRUE}, {2, 4, TRUE}, {11, 12, TRUE}, {30, 32, FALSE},
508       {39, 42, TRUE} };
509 
510   SVN_ERR(svn_rangelist__parse(&rangelist1, "1-6,12-16,30-32*,40-42", pool));
511   SVN_ERR(svn_rangelist__parse(&rangelist2, "1,3-4*,7,9,11-12,31-34*,38-44",
512                                pool));
513 
514   /* Check the intersection while considering inheritance twice, reversing
515      the order of the rangelist arguments on the second call to
516      svn_rangelist_intersection.  The order *should* have no effect on
517      the result -- see http://svn.haxx.se/dev/archive-2010-03/0351.shtml.
518 
519      '3-4*' has different inheritance than '1-6', so no intersection is
520      expected.  '30-32*' and '31-34*' have the same inheritance, so intersect
521      at '31-32*'.  Per the svn_rangelist_intersect API, since both ranges
522      are non-inheritable, so is the result. */
523   SVN_ERR(svn_rangelist_intersect(&intersection, rangelist1, rangelist2,
524                                   TRUE, pool));
525 
526   SVN_ERR(verify_ranges_match(intersection,
527                               intersection_consider_inheritance,
528                               4, "svn_rangelist_intersect", "intersect",
529                               pool));
530 
531   SVN_ERR(svn_rangelist_intersect(&intersection, rangelist2, rangelist1,
532                                   TRUE, pool));
533 
534   SVN_ERR(verify_ranges_match(intersection,
535                               intersection_consider_inheritance,
536                               4, "svn_rangelist_intersect", "intersect",
537                               pool));
538 
539   /* Check the intersection while ignoring inheritance.  The one difference
540      from when we consider inheritance is that '3-4*' and '1-6' now intersect,
541      since we don't care about inheritability, just the start and end ranges.
542      Per the svn_rangelist_intersect API, since only one range is
543      non-inheritable the result is inheritable. */
544   SVN_ERR(svn_rangelist_intersect(&intersection, rangelist1, rangelist2,
545                                   FALSE, pool));
546 
547   SVN_ERR(verify_ranges_match(intersection,
548                               intersection_ignore_inheritance,
549                               5, "svn_rangelist_intersect", "intersect",
550                               pool));
551 
552   SVN_ERR(svn_rangelist_intersect(&intersection, rangelist2, rangelist1,
553                                   FALSE, pool));
554 
555   SVN_ERR(verify_ranges_match(intersection,
556                               intersection_ignore_inheritance,
557                               5, "svn_rangelist_intersect", "intersect",
558                               pool));
559   return SVN_NO_ERROR;
560 }
561 
562 static svn_error_t *
test_mergeinfo_intersect(apr_pool_t * pool)563 test_mergeinfo_intersect(apr_pool_t *pool)
564 {
565   svn_merge_range_t expected_intersection[3] =
566     { {0, 1, TRUE}, {2, 4, TRUE}, {11, 12, TRUE} };
567   svn_rangelist_t *rangelist;
568   apr_hash_t *intersection;
569   apr_hash_t *info1, *info2;
570 
571   SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 1-6,12-16\n/foo: 31", pool));
572   SVN_ERR(svn_mergeinfo_parse(&info2, "/trunk: 1,3-4,7,9,11-12", pool));
573 
574   SVN_ERR(svn_mergeinfo_intersect(&intersection, info1, info2, pool));
575   if (apr_hash_count(intersection) != 1)
576     return fail(pool, "Unexpected number of rangelists in mergeinfo "
577                 "intersection: Expected %d, found %d", 1,
578                 apr_hash_count(intersection));
579 
580   rangelist = apr_hash_get(intersection, "/trunk", APR_HASH_KEY_STRING);
581   return verify_ranges_match(rangelist, expected_intersection, 3,
582                              "svn_rangelist_intersect", "intersect", pool);
583 }
584 
585 static svn_error_t *
test_merge_mergeinfo(apr_pool_t * pool)586 test_merge_mergeinfo(apr_pool_t *pool)
587 {
588   int i;
589 
590   /* Structures and constants for test_merge_mergeinfo() */
591   /* Number of svn_mergeinfo_merge test sets */
592   #define NBR_MERGEINFO_MERGES 12
593 
594   /* Maximum number of expected paths in the results
595      of the svn_mergeinfo_merge tests */
596   #define MAX_NBR_MERGEINFO_PATHS 4
597 
598   /* Maximum number of expected ranges in the results
599      of the svn_mergeinfo_merge tests */
600   #define MAX_NBR_MERGEINFO_RANGES 10
601 
602   /* Struct to store a path and it's expected ranges,
603      i.e. the expected result of an svn_mergeinfo_merge
604      test. */
605   struct mergeinfo_merge_path_range
606     {
607       const char *path;
608       int expected_n;
609       svn_merge_range_t expected_rngs[MAX_NBR_MERGEINFO_RANGES];
610     };
611 
612   /* Struct for svn_mergeinfo_merge test data.
613      If MERGEINFO1 and MERGEINFO2 are parsed to a hash with
614      svn_mergeinfo_parse() and then merged with svn_mergeinfo_merge(),
615      the resulting hash should have EXPECTED_PATHS number of paths
616      mapped to rangelists and each mapping is described by PATH_RNGS
617      where PATH_RNGS->PATH is not NULL. */
618   struct mergeinfo_merge_test_data
619     {
620       const char *mergeinfo1;
621       const char *mergeinfo2;
622       int expected_paths;
623       struct mergeinfo_merge_path_range path_rngs[MAX_NBR_MERGEINFO_PATHS];
624     };
625 
626   static struct mergeinfo_merge_test_data mergeinfo[NBR_MERGEINFO_MERGES] =
627     {
628       /* One path, intersecting inheritable ranges */
629       { "/trunk: 5-10",
630         "/trunk: 6", 1,
631         { {"/trunk", 1, { {4, 10, TRUE} } } } },
632 
633       /* One path, intersecting non-inheritable ranges */
634       { "/trunk: 5-10*",
635         "/trunk: 6*", 1,
636         { {"/trunk", 1, { {4, 10, FALSE} } } } },
637 
638       /* One path, intersecting ranges with different inheritability */
639       { "/trunk: 5-10",
640         "/trunk: 6*", 1,
641         { {"/trunk", 1, { {4, 10, TRUE} } } } },
642 
643       /* One path, intersecting ranges with different inheritability */
644       { "/trunk: 5-10*",
645         "/trunk: 6", 1,
646         { {"/trunk", 3, { {4, 5, FALSE}, {5, 6, TRUE}, {6, 10, FALSE} } } } },
647 
648       /* Adjacent ranges all inheritable ranges */
649       { "/trunk: 1,3,5-11,13",
650         "/trunk: 2,4,12,14-22", 1,
651         { {"/trunk", 1, { {0, 22, TRUE} } } } },
652 
653       /* Adjacent ranges all non-inheritable ranges */
654       { "/trunk: 1*,3*,5-11*,13*",
655         "/trunk: 2*,4*,12*,14-22*", 1,
656         { {"/trunk", 1, { {0, 22, FALSE} } } } },
657 
658       /* Adjacent ranges differing inheritability */
659       { "/trunk: 1*,3*,5-11*,13*",
660         "/trunk: 2,4,12,14-22", 1,
661         { {"/trunk", 8, { { 0,  1, FALSE}, { 1,  2, TRUE},
662                           { 2,  3, FALSE}, { 3,  4, TRUE},
663                           { 4, 11, FALSE}, {11, 12, TRUE},
664                           {12, 13, FALSE}, {13, 22, TRUE} } } } },
665 
666       /* Adjacent ranges differing inheritability */
667       { "/trunk: 1,3,5-11,13",
668         "/trunk: 2*,4*,12*,14-22*", 1,
669         { {"/trunk", 8, { { 0,  1, TRUE}, { 1,  2, FALSE},
670                           { 2,  3, TRUE}, { 3,  4, FALSE},
671                           { 4, 11, TRUE}, {11, 12, FALSE},
672                           {12, 13, TRUE}, {13, 22, FALSE} } } } },
673 
674       /* Two paths all inheritable ranges */
675       { "/trunk::1: 3,5,7-9,10,11,13,14\n/fred:8-10",
676         "/trunk::1: 1-4,6\n/fred:9-12", 2,
677         { {"/trunk::1", 2, { {0, 11, TRUE}, {12, 14, TRUE} } },
678           {"/fred",     1, { {7, 12, TRUE} } } } },
679 
680       /* Two paths all non-inheritable ranges */
681       { "/trunk: 3*,5*,7-9*,10*,11*,13*,14*\n/fred:8-10*",
682         "/trunk: 1-4*,6*\n/fred:9-12*", 2,
683         { {"/trunk", 2, { {0, 11, FALSE}, {12, 14, FALSE} } },
684           {"/fred",  1, { {7, 12, FALSE} } } } },
685 
686       /* Two paths mixed inheritability */
687       { "/trunk: 3,5*,7-9,10,11*,13,14\n/fred:8-10",
688         "/trunk: 1-4,6\n/fred:9-12*", 2,
689         { {"/trunk", 5, { { 0,  4, TRUE }, { 4,  5, FALSE}, {5, 10, TRUE},
690                           {10, 11, FALSE}, {12, 14, TRUE } } },
691           {"/fred",  2, { { 7, 10, TRUE }, {10, 12, FALSE} } } } },
692 
693       /* A slew of different paths but no ranges to be merged */
694       { "/trunk: 3,5-9*\n/betty: 2-4",
695         "/fred: 1-18\n/:barney: 1,3-43", 4,
696         { {"/trunk",   2, { {2,  3, TRUE}, {4,  9, FALSE} } },
697           {"/betty",   1, { {1,  4, TRUE} } },
698           {"/:barney", 2, { {0,  1, TRUE}, {2, 43, TRUE} } },
699           {"/fred",    1, { {0, 18, TRUE} } } } }
700     };
701 
702   for (i = 0; i < NBR_MERGEINFO_MERGES; i++)
703     {
704       int j;
705       svn_string_t *info2_starting, *info2_ending;
706       apr_hash_t *info1, *info2;
707 
708       SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo[i].mergeinfo1, pool));
709       SVN_ERR(svn_mergeinfo_parse(&info2, mergeinfo[i].mergeinfo2, pool));
710 
711       /* Make a copy of info2.  We will merge it into info1, but info2
712          should remain unchanged.  Store the mergeinfo as a svn_string_t
713          rather than making a copy and using svn_mergeinfo_diff().  Since
714          that API uses some of the underlying code as svn_mergeinfo_merge
715          we might mask potential errors. */
716       SVN_ERR(svn_mergeinfo_to_string(&info2_starting, info2, pool));
717 
718       SVN_ERR(svn_mergeinfo_merge(info1, info2, pool));
719       if (mergeinfo[i].expected_paths != (int)apr_hash_count(info1))
720         return fail(pool, "Wrong number of paths in merged mergeinfo");
721 
722       /* Check that info2 remained unchanged. */
723       SVN_ERR(svn_mergeinfo_to_string(&info2_ending, info2, pool));
724 
725       if (strcmp(info2_ending->data, info2_starting->data))
726         return fail(pool,
727                     apr_psprintf(pool,
728                                  "svn_mergeinfo_merge case %i "
729                                  "modified its CHANGES arg from "
730                                  "%s to %s", i, info2_starting->data,
731                                  info2_ending->data));
732 
733       for (j = 0; j < mergeinfo[i].expected_paths; j++)
734         {
735           svn_rangelist_t *rangelist =
736             apr_hash_get(info1, mergeinfo[i].path_rngs[j].path,
737                          APR_HASH_KEY_STRING);
738           if (!rangelist)
739             return fail(pool, "Missing path '%s' in merged mergeinfo",
740                         mergeinfo[i].path_rngs[j].path);
741           SVN_ERR(verify_ranges_match(
742                     rangelist,
743                     mergeinfo[i].path_rngs[j].expected_rngs,
744                     mergeinfo[i].path_rngs[j].expected_n,
745                     apr_psprintf(pool, "svn_rangelist_merge case %i:%i", i, j),
746                     "merge", pool));
747         }
748     }
749 
750   return SVN_NO_ERROR;
751 }
752 
753 static svn_error_t *
test_remove_rangelist(apr_pool_t * pool)754 test_remove_rangelist(apr_pool_t *pool)
755 {
756   int i, j;
757   svn_error_t *err, *child_err;
758   svn_rangelist_t *output, *eraser, *whiteboard;
759 
760   /* Struct for svn_rangelist_remove test data.
761      Parse WHITEBOARD and ERASER to hashes and then get the rangelist for
762      path 'A' from both.
763 
764      Remove ERASER's rangelist from WHITEBOARD's twice, once while
765      considering inheritance and once while not.  In the first case the
766      resulting rangelist should have EXPECTED_RANGES_CONSIDER_INHERITANCE
767      number of ranges and these ranges should match the ranges in
768      EXPECTED_REMOVED_CONSIDER_INHERITANCE.  In the second case there
769      should be EXPECTED_RANGES_IGNORE_INHERITANCE number of ranges and
770      these should match EXPECTED_REMOVED_IGNORE_INHERITANCE */
771   struct rangelist_remove_test_data
772   {
773     const char *whiteboard;
774     const char *eraser;
775     int expected_ranges_consider_inheritance;
776     svn_merge_range_t expected_removed_consider_inheritance[10];
777     int expected_ranges_ignore_inheritance;
778     svn_merge_range_t expected_removed_ignore_inheritance[10];
779   };
780 
781   #define SIZE_OF_RANGE_REMOVE_TEST_ARRAY 15
782 
783   /* The actual test data */
784   struct rangelist_remove_test_data test_data[SIZE_OF_RANGE_REMOVE_TEST_ARRAY] =
785     {
786       /* Eraser is a proper subset of whiteboard */
787       {"1-44",  "5",  2, { {0,  4, TRUE }, {5, 44, TRUE }},
788                       2, { {0,  4, TRUE }, {5, 44, TRUE }}},
789       {"1-44*", "5",  1, { {0, 44, FALSE} },
790                       2, { {0,  4, FALSE}, {5, 44, FALSE}}},
791       {"1-44",  "5*", 1, { {0, 44, TRUE } },
792                       2, { {0,  4, TRUE }, {5, 44, TRUE }}},
793       {"1-44*", "5*", 2, { {0,  4, FALSE}, {5, 44, FALSE}},
794                       2, { {0,  4, FALSE}, {5, 44, FALSE}}},
795       /* Non-intersecting ranges...nothing is removed */
796       {"2-9,14-19",   "12",  2, { {1, 9, TRUE }, {13, 19, TRUE }},
797                              2, { {1, 9, TRUE }, {13, 19, TRUE }}},
798       {"2-9*,14-19*", "12",  2, { {1, 9, FALSE}, {13, 19, FALSE}},
799                              2, { {1, 9, FALSE}, {13, 19, FALSE}}},
800       {"2-9,14-19",   "12*", 2, { {1, 9, TRUE }, {13, 19, TRUE }},
801                              2, { {1, 9, TRUE }, {13, 19, TRUE }}},
802       {"2-9*,14-19*", "12*", 2, { {1, 9, FALSE}, {13, 19, FALSE}},
803                              2, { {1, 9, FALSE}, {13, 19, FALSE}}},
804       /* Eraser overlaps whiteboard */
805       {"1,9-17",  "12-20",  2, { {0,  1, TRUE }, {8, 11, TRUE }},
806                             2, { {0,  1, TRUE }, {8, 11, TRUE }}},
807       {"1,9-17*", "12-20",  2, { {0,  1, TRUE }, {8, 17, FALSE}},
808                             2, { {0,  1, TRUE }, {8, 11, FALSE}}},
809       {"1,9-17",  "12-20*", 2, { {0,  1, TRUE }, {8, 17, TRUE }},
810                             2, { {0,  1, TRUE }, {8, 11, TRUE }}},
811       {"1,9-17*", "12-20*", 2, { {0,  1, TRUE }, {8, 11, FALSE}},
812                             2, { {0,  1, TRUE }, {8, 11, FALSE}}},
813       /* Empty rangelist */
814       {"",  "",           0, { {0, 0, FALSE}},
815                           0, { {0, 0, FALSE}}},
816       {"",  "5-8,10-100", 0, { {0, 0, FALSE}},
817                           0, { {0, 0, FALSE}}},
818       {"5-8,10-100",  "", 2, { {4, 8, TRUE }, {9, 100, TRUE }},
819                           2, { {4, 8, TRUE }, {9, 100, TRUE }}}
820     };
821 
822   err = child_err = SVN_NO_ERROR;
823   for (j = 0; j < 2; j++)
824     {
825       for (i = 0; i < SIZE_OF_RANGE_REMOVE_TEST_ARRAY; i++)
826         {
827           int expected_nbr_ranges;
828           svn_merge_range_t *expected_ranges;
829           svn_string_t *eraser_starting;
830           svn_string_t *eraser_ending;
831           svn_string_t *whiteboard_starting;
832           svn_string_t *whiteboard_ending;
833 
834           SVN_ERR(svn_rangelist__parse(&eraser, test_data[i].eraser, pool));
835           SVN_ERR(svn_rangelist__parse(&whiteboard, test_data[i].whiteboard, pool));
836 
837           /* Represent empty mergeinfo with an empty rangelist. */
838           if (eraser == NULL)
839             eraser = apr_array_make(pool, 0, sizeof(*eraser));
840           if (whiteboard == NULL)
841             whiteboard = apr_array_make(pool, 0, sizeof(*whiteboard));
842 
843           /* First pass try removal considering inheritance, on the
844              second pass ignore it. */
845           if (j == 0)
846             {
847               expected_nbr_ranges = (test_data[i]).expected_ranges_consider_inheritance;
848               expected_ranges = (test_data[i]).expected_removed_consider_inheritance;
849 
850             }
851           else
852             {
853               expected_nbr_ranges = (test_data[i]).expected_ranges_ignore_inheritance;
854               expected_ranges = (test_data[i]).expected_removed_ignore_inheritance;
855 
856             }
857 
858          /* Make a copies of whiteboard and eraser.  They should not be
859             modified by svn_rangelist_remove(). */
860          SVN_ERR(svn_rangelist_to_string(&eraser_starting, eraser, pool));
861          SVN_ERR(svn_rangelist_to_string(&whiteboard_starting, whiteboard,
862                                          pool));
863 
864           SVN_ERR(svn_rangelist_remove(&output, eraser, whiteboard,
865                                        j == 0,
866                                        pool));
867           child_err = verify_ranges_match(output, expected_ranges,
868                                           expected_nbr_ranges,
869                                           apr_psprintf(pool,
870                                                        "svn_rangelist_remove "
871                                                        "case %i", i),
872                                           "remove", pool);
873 
874           /* Collect all the errors rather than returning on the first. */
875           if (child_err)
876             {
877               if (err)
878                 svn_error_compose(err, child_err);
879               else
880                 err = child_err;
881             }
882 
883           /* Check that eraser and whiteboard were not modified. */
884           SVN_ERR(svn_rangelist_to_string(&eraser_ending, eraser, pool));
885           SVN_ERR(svn_rangelist_to_string(&whiteboard_ending, whiteboard,
886                                           pool));
887           if (strcmp(eraser_starting->data, eraser_ending->data))
888             {
889               child_err = fail(pool,
890                                apr_psprintf(pool,
891                                             "svn_rangelist_remove case %i "
892                                             "modified its ERASER arg from "
893                                             "%s to %s when %sconsidering "
894                                             "inheritance", i,
895                                             eraser_starting->data,
896                                             eraser_ending->data,
897                                             j ? "" : "not "));
898               if (err)
899                 svn_error_compose(err, child_err);
900               else
901                 err = child_err;
902             }
903           if (strcmp(whiteboard_starting->data, whiteboard_ending->data))
904             {
905               child_err = fail(pool,
906                                apr_psprintf(pool,
907                                             "svn_rangelist_remove case %i "
908                                             "modified its WHITEBOARD arg "
909                                             "from %s to %s when "
910                                             "%sconsidering inheritance", i,
911                                             whiteboard_starting->data,
912                                             whiteboard_ending->data,
913                                             j ? "" : "not "));
914               if (err)
915                 svn_error_compose(err, child_err);
916               else
917                 err = child_err;
918             }
919         }
920     }
921   return err;
922 }
923 
924 #define RANDOM_REV_ARRAY_LENGTH 100
925 
926 /* Random number seed. */
927 static apr_uint32_t random_rev_array_seed;
928 
929 /* Set a random 3/4-ish of the elements of array REVS[RANDOM_REV_ARRAY_LENGTH]
930  * to TRUE and the rest to FALSE. */
931 static void
randomly_fill_rev_array(svn_boolean_t * revs)932 randomly_fill_rev_array(svn_boolean_t *revs)
933 {
934   int i;
935   for (i = 0; i < RANDOM_REV_ARRAY_LENGTH; i++)
936     {
937       apr_uint32_t next = svn_test_rand(&random_rev_array_seed);
938       revs[i] = (next < 0x40000000) ? 0 : 1;
939     }
940 }
941 
942 /* Set *RANGELIST to a rangelist representing the revisions that are marked
943  * with TRUE in the array REVS[RANDOM_REV_ARRAY_LENGTH]. */
944 static svn_error_t *
rev_array_to_rangelist(svn_rangelist_t ** rangelist,svn_boolean_t * revs,apr_pool_t * pool)945 rev_array_to_rangelist(svn_rangelist_t **rangelist,
946                        svn_boolean_t *revs,
947                        apr_pool_t *pool)
948 {
949   svn_stringbuf_t *buf = svn_stringbuf_create("/trunk: ", pool);
950   svn_boolean_t first = TRUE;
951   apr_hash_t *mergeinfo;
952   int i;
953 
954   for (i = 0; i < RANDOM_REV_ARRAY_LENGTH; i++)
955     {
956       if (revs[i])
957         {
958           if (first)
959             first = FALSE;
960           else
961             svn_stringbuf_appendcstr(buf, ",");
962           svn_stringbuf_appendcstr(buf, apr_psprintf(pool, "%d", i));
963         }
964     }
965 
966   SVN_ERR(svn_mergeinfo_parse(&mergeinfo, buf->data, pool));
967   *rangelist = apr_hash_get(mergeinfo, "/trunk", APR_HASH_KEY_STRING);
968 
969   return SVN_NO_ERROR;
970 }
971 
972 static svn_error_t *
test_rangelist_remove_randomly(apr_pool_t * pool)973 test_rangelist_remove_randomly(apr_pool_t *pool)
974 {
975   int i;
976   apr_pool_t *iterpool;
977 
978   random_rev_array_seed = (apr_uint32_t) apr_time_now();
979 
980   iterpool = svn_pool_create(pool);
981 
982   for (i = 0; i < 20; i++)
983     {
984       svn_boolean_t first_revs[RANDOM_REV_ARRAY_LENGTH],
985         second_revs[RANDOM_REV_ARRAY_LENGTH],
986         expected_revs[RANDOM_REV_ARRAY_LENGTH];
987       svn_rangelist_t *first_rangelist, *second_rangelist,
988         *expected_rangelist, *actual_rangelist;
989       /* There will be at most RANDOM_REV_ARRAY_LENGTH ranges in
990          expected_rangelist. */
991       svn_merge_range_t expected_range_array[RANDOM_REV_ARRAY_LENGTH];
992       int j;
993 
994       svn_pool_clear(iterpool);
995 
996       randomly_fill_rev_array(first_revs);
997       randomly_fill_rev_array(second_revs);
998       /* There is no change numbered "r0" */
999       first_revs[0] = FALSE;
1000       second_revs[0] = FALSE;
1001       for (j = 0; j < RANDOM_REV_ARRAY_LENGTH; j++)
1002         expected_revs[j] = second_revs[j] && !first_revs[j];
1003 
1004       SVN_ERR(rev_array_to_rangelist(&first_rangelist, first_revs, iterpool));
1005       SVN_ERR(rev_array_to_rangelist(&second_rangelist, second_revs, iterpool));
1006       SVN_ERR(rev_array_to_rangelist(&expected_rangelist, expected_revs,
1007                                      iterpool));
1008 
1009       for (j = 0; j < expected_rangelist->nelts; j++)
1010         {
1011           expected_range_array[j] = *(APR_ARRAY_IDX(expected_rangelist, j,
1012                                                     svn_merge_range_t *));
1013         }
1014 
1015       SVN_ERR(svn_rangelist_remove(&actual_rangelist, first_rangelist,
1016                                    second_rangelist, TRUE, iterpool));
1017 
1018       SVN_ERR(verify_ranges_match(actual_rangelist,
1019                                   expected_range_array,
1020                                   expected_rangelist->nelts,
1021                                   "svn_rangelist_remove random call",
1022                                   "remove", iterpool));
1023     }
1024 
1025   svn_pool_destroy(iterpool);
1026 
1027   return SVN_NO_ERROR;
1028 }
1029 
1030 static svn_error_t *
test_rangelist_intersect_randomly(apr_pool_t * pool)1031 test_rangelist_intersect_randomly(apr_pool_t *pool)
1032 {
1033   int i;
1034   apr_pool_t *iterpool;
1035 
1036   random_rev_array_seed = (apr_uint32_t) apr_time_now();
1037 
1038   iterpool = svn_pool_create(pool);
1039 
1040   for (i = 0; i < 20; i++)
1041     {
1042       svn_boolean_t first_revs[RANDOM_REV_ARRAY_LENGTH],
1043         second_revs[RANDOM_REV_ARRAY_LENGTH],
1044         expected_revs[RANDOM_REV_ARRAY_LENGTH];
1045       svn_rangelist_t *first_rangelist, *second_rangelist,
1046         *expected_rangelist, *actual_rangelist;
1047       /* There will be at most RANDOM_REV_ARRAY_LENGTH ranges in
1048          expected_rangelist. */
1049       svn_merge_range_t expected_range_array[RANDOM_REV_ARRAY_LENGTH];
1050       int j;
1051 
1052       svn_pool_clear(iterpool);
1053 
1054       randomly_fill_rev_array(first_revs);
1055       randomly_fill_rev_array(second_revs);
1056       /* There is no change numbered "r0" */
1057       first_revs[0] = FALSE;
1058       second_revs[0] = FALSE;
1059       for (j = 0; j < RANDOM_REV_ARRAY_LENGTH; j++)
1060         expected_revs[j] = second_revs[j] && first_revs[j];
1061 
1062       SVN_ERR(rev_array_to_rangelist(&first_rangelist, first_revs, iterpool));
1063       SVN_ERR(rev_array_to_rangelist(&second_rangelist, second_revs, iterpool));
1064       SVN_ERR(rev_array_to_rangelist(&expected_rangelist, expected_revs,
1065                                      iterpool));
1066 
1067       for (j = 0; j < expected_rangelist->nelts; j++)
1068         {
1069           expected_range_array[j] = *(APR_ARRAY_IDX(expected_rangelist, j,
1070                                                     svn_merge_range_t *));
1071         }
1072 
1073       SVN_ERR(svn_rangelist_intersect(&actual_rangelist, first_rangelist,
1074                                       second_rangelist, TRUE, iterpool));
1075 
1076       SVN_ERR(verify_ranges_match(actual_rangelist,
1077                                   expected_range_array,
1078                                   expected_rangelist->nelts,
1079                                   "svn_rangelist_intersect random call",
1080                                   "intersect", iterpool));
1081     }
1082 
1083   svn_pool_destroy(iterpool);
1084 
1085   return SVN_NO_ERROR;
1086 }
1087 
1088 /* ### Share code with test_diff_mergeinfo() and test_remove_rangelist(). */
1089 static svn_error_t *
test_remove_mergeinfo(apr_pool_t * pool)1090 test_remove_mergeinfo(apr_pool_t *pool)
1091 {
1092   apr_hash_t *output, *whiteboard, *eraser;
1093   svn_merge_range_t expected_rangelist_remainder[NBR_RANGELIST_DELTAS] =
1094     { {6, 7, TRUE}, {8, 9, TRUE}, {10, 11, TRUE}, {32, 34, TRUE} };
1095 
1096   SVN_ERR(svn_mergeinfo_parse(&whiteboard,
1097                               "/trunk: 1,3-4,7,9,11-12,31-34", pool));
1098   SVN_ERR(svn_mergeinfo_parse(&eraser, "/trunk: 1-6,12-16,30-32", pool));
1099 
1100   /* Leftover on /trunk should be the set (7, 9, 11, 33-34) */
1101   SVN_ERR(svn_mergeinfo_remove(&output, eraser, whiteboard, pool));
1102 
1103   /* Verify calculation of range list remainder. */
1104   return verify_mergeinfo_deltas(output, expected_rangelist_remainder,
1105                                  "svn_mergeinfo_remove", "leftover", pool);
1106 }
1107 #undef NBR_RANGELIST_DELTAS
1108 
1109 static svn_error_t *
test_rangelist_to_string(apr_pool_t * pool)1110 test_rangelist_to_string(apr_pool_t *pool)
1111 {
1112   svn_rangelist_t *result;
1113   svn_string_t *output;
1114   svn_string_t *expected = svn_string_create("3,5,7-11,13-14", pool);
1115   apr_hash_t *info1;
1116 
1117   SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo1, pool));
1118 
1119   result = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
1120   if (!result)
1121     return fail(pool, "Missing path in parsed mergeinfo");
1122 
1123   SVN_ERR(svn_rangelist_to_string(&output, result, pool));
1124 
1125   if (!svn_string_compare(expected, output))
1126     return fail(pool, "Rangelist string not what we expected");
1127 
1128   return SVN_NO_ERROR;
1129 }
1130 
1131 static svn_error_t *
test_mergeinfo_to_string(apr_pool_t * pool)1132 test_mergeinfo_to_string(apr_pool_t *pool)
1133 {
1134   svn_string_t *output;
1135   svn_string_t *expected;
1136   apr_hash_t *info1, *info2;
1137   expected = svn_string_create("/fred:8-10\n/trunk:3,5,7-11,13-14", pool);
1138 
1139   SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo1, pool));
1140 
1141   SVN_ERR(svn_mergeinfo_to_string(&output, info1, pool));
1142 
1143   if (!svn_string_compare(expected, output))
1144     return fail(pool, "Mergeinfo string not what we expected");
1145 
1146   /* Manually construct some mergeinfo with relative path
1147      merge source keys.  These should be tolerated as input
1148      to svn_mergeinfo_to_string(), but the resulting svn_string_t
1149      should have absolute keys. */
1150   info2 = apr_hash_make(pool);
1151   apr_hash_set(info2, "fred",
1152                APR_HASH_KEY_STRING,
1153                apr_hash_get(info1, "/fred", APR_HASH_KEY_STRING));
1154   apr_hash_set(info2, "trunk",
1155                APR_HASH_KEY_STRING,
1156                apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING));
1157   SVN_ERR(svn_mergeinfo_to_string(&output, info2, pool));
1158 
1159   if (!svn_string_compare(expected, output))
1160     return fail(pool, "Mergeinfo string not what we expected");
1161 
1162   return SVN_NO_ERROR;
1163 }
1164 
1165 
1166 static svn_error_t *
test_rangelist_merge(apr_pool_t * pool)1167 test_rangelist_merge(apr_pool_t *pool)
1168 {
1169   int i;
1170   svn_error_t *err, *child_err;
1171   svn_rangelist_t *rangelist1, *rangelist2;
1172 
1173   /* Struct for svn_rangelist_merge test data.  Similar to
1174      mergeinfo_merge_test_data struct in svn_mergeinfo_merge() test. */
1175   struct rangelist_merge_test_data
1176   {
1177     const char *mergeinfo1;
1178     const char *mergeinfo2;
1179     int expected_ranges;
1180     svn_merge_range_t expected_merge[6];
1181   };
1182 
1183   #define SIZE_OF_RANGE_MERGE_TEST_ARRAY 68
1184   /* The actual test data. */
1185   struct rangelist_merge_test_data test_data[SIZE_OF_RANGE_MERGE_TEST_ARRAY] =
1186     {
1187       /* Non-intersecting ranges */
1188       {"1-44",    "70-101",  2, {{ 0, 44, TRUE }, {69, 101, TRUE }}},
1189       {"1-44*",   "70-101",  2, {{ 0, 44, FALSE}, {69, 101, TRUE }}},
1190       {"1-44",    "70-101*", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}},
1191       {"1-44*",   "70-101*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}},
1192       {"70-101",  "1-44",    2, {{ 0, 44, TRUE }, {69, 101, TRUE }}},
1193       {"70-101*", "1-44",    2, {{ 0, 44, TRUE }, {69, 101, FALSE}}},
1194       {"70-101",  "1-44*",   2, {{ 0, 44, FALSE}, {69, 101, TRUE }}},
1195       {"70-101*", "1-44*",   2, {{ 0, 44, FALSE}, {69, 101, FALSE}}},
1196 
1197       /* Intersecting ranges with same starting and ending revisions */
1198       {"4-20",  "4-20",  1, {{3, 20, TRUE }}},
1199       {"4-20*", "4-20",  1, {{3, 20, TRUE }}},
1200       {"4-20",  "4-20*", 1, {{3, 20, TRUE }}},
1201       {"4-20*", "4-20*", 1, {{3, 20, FALSE}}},
1202 
1203       /* Intersecting ranges with same starting revision */
1204       {"6-17",  "6-12",  1, {{5, 17, TRUE}}},
1205       {"6-17*", "6-12",  2, {{5, 12, TRUE }, {12, 17, FALSE}}},
1206       {"6-17",  "6-12*", 1, {{5, 17, TRUE }}},
1207       {"6-17*", "6-12*", 1, {{5, 17, FALSE}}},
1208       {"6-12",  "6-17",  1, {{5, 17, TRUE }}},
1209       {"6-12*", "6-17",  1, {{5, 17, TRUE }}},
1210       {"6-12",  "6-17*", 2, {{5, 12, TRUE }, {12, 17, FALSE}}},
1211       {"6-12*", "6-17*", 1, {{5, 17, FALSE}}},
1212 
1213       /* Intersecting ranges with same ending revision */
1214       {"5-77",   "44-77",  1, {{4, 77, TRUE }}},
1215       {"5-77*",  "44-77",  2, {{4, 43, FALSE}, {43, 77, TRUE}}},
1216       {"5-77",   "44-77*", 1, {{4, 77, TRUE }}},
1217       {"5-77*",  "44-77*", 1, {{4, 77, FALSE}}},
1218       {"44-77",  "5-77",   1, {{4, 77, TRUE }}},
1219       {"44-77*", "5-77",   1, {{4, 77, TRUE }}},
1220       {"44-77",  "5-77*",  2, {{4, 43, FALSE}, {43, 77, TRUE}}},
1221       {"44-77*", "5-77*",  1, {{4, 77, FALSE}}},
1222 
1223       /* Intersecting ranges with different starting and ending revision
1224          where one range is a proper subset of the other. */
1225       {"12-24",  "20-23",  1, {{11, 24, TRUE }}},
1226       {"12-24*", "20-23",  3, {{11, 19, FALSE}, {19, 23, TRUE },
1227                                {23, 24, FALSE}}},
1228       {"12-24",  "20-23*", 1, {{11, 24, TRUE }}},
1229       {"12-24*", "20-23*", 1, {{11, 24, FALSE}}},
1230       {"20-23",  "12-24",  1, {{11, 24, TRUE }}},
1231       {"20-23*", "12-24",  1, {{11, 24, TRUE }}},
1232       {"20-23",  "12-24*", 3, {{11, 19, FALSE}, {19, 23, TRUE },
1233                                {23, 24, FALSE}}},
1234       {"20-23*", "12-24*", 1, {{11, 24, FALSE}}},
1235 
1236       /* Intersecting ranges with different starting and ending revision
1237          where neither range is a proper subset of the other. */
1238       {"50-73",  "60-99",  1, {{49, 99, TRUE }}},
1239       {"50-73*", "60-99",  2, {{49, 59, FALSE}, {59, 99, TRUE }}},
1240       {"50-73",  "60-99*", 2, {{49, 73, TRUE }, {73, 99, FALSE}}},
1241       {"50-73*", "60-99*", 1, {{49, 99, FALSE}}},
1242       {"60-99",  "50-73",  1, {{49, 99, TRUE }}},
1243       {"60-99*", "50-73",  2, {{49, 73, TRUE }, {73, 99, FALSE}}},
1244       {"60-99",  "50-73*", 2, {{49, 59, FALSE}, {59, 99, TRUE }}},
1245       {"60-99*", "50-73*", 1, {{49, 99, FALSE}}},
1246 
1247       /* Multiple ranges. */
1248       {"1-5,7,12-13",    "2-17",  1, {{0,  17, TRUE }}},
1249       {"1-5*,7*,12-13*", "2-17*", 1, {{0,  17, FALSE}}},
1250 
1251       {"1-5,7,12-13",    "2-17*", 6,
1252        {{0,  5, TRUE }, { 5,  6, FALSE}, { 6,  7, TRUE },
1253         {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}},
1254 
1255       {"1-5*,7*,12-13*", "2-17", 2,
1256        {{0, 1, FALSE}, {1, 17, TRUE }}},
1257 
1258       {"2-17",  "1-5,7,12-13",    1, {{0,  17, TRUE }}},
1259       {"2-17*", "1-5*,7*,12-13*", 1, {{0,  17, FALSE}}},
1260 
1261       {"2-17*", "1-5,7,12-13", 6,
1262        {{0,  5, TRUE }, { 5,  6, FALSE}, { 6,  7, TRUE },
1263         {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}},
1264 
1265       {"2-17", "1-5*,7*,12-13*", 2,
1266        {{0, 1, FALSE}, {1, 17, TRUE}}},
1267 
1268       {"3-4*,10-15,20", "5-60*", 5,
1269        {{2, 9, FALSE}, {9, 15, TRUE}, {15, 19, FALSE},{19, 20, TRUE},
1270         {20, 60, FALSE}}},
1271 
1272       {"5-60*", "3-4*,10-15,20", 5,
1273        {{2, 9, FALSE}, {9, 15, TRUE}, {15, 19, FALSE},{19, 20, TRUE},
1274         {20, 60, FALSE}}},
1275 
1276       {"3-4*,50-100*", "5-60*", 1, {{2, 100, FALSE}}},
1277 
1278       {"5-60*", "3-4*,50-100*", 1, {{2, 100, FALSE}}},
1279 
1280       {"3-4*,50-100", "5-60*", 2, {{2, 49, FALSE}, {49, 100, TRUE}}},
1281 
1282       {"5-60*", "3-4*,50-100", 2, {{2, 49, FALSE}, {49, 100, TRUE}}},
1283 
1284       {"3-4,50-100*", "5-60", 2, {{2, 60, TRUE}, {60, 100, FALSE}}},
1285 
1286       {"5-60", "3-4,50-100*", 2, {{2, 60, TRUE}, {60, 100, FALSE}}},
1287 
1288       {"5,9,11-15,17,200-300,999", "7-50", 4,
1289        {{4, 5, TRUE}, {6, 50, TRUE}, {199, 300, TRUE}, {998, 999, TRUE}}},
1290 
1291       /* A rangelist merged with an empty rangelist should equal the
1292          non-empty rangelist but in compacted form. */
1293       {"1-44,45,46,47-50",       "",  1, {{ 0, 50, TRUE }}},
1294       {"1,2,3,4,5,6,7,8",        "",  1, {{ 0, 8,  TRUE }}},
1295       {"6-10,12-13,14,15,16-22", "",  2,
1296        {{ 5, 10, TRUE }, { 11, 22, TRUE }}},
1297       {"", "1-44,45,46,47-50",        1, {{ 0, 50, TRUE }}},
1298       {"", "1,2,3,4,5,6,7,8",         1, {{ 0, 8,  TRUE }}},
1299       {"", "6-10,12-13,14,15,16-22",  2,
1300        {{ 5, 10, TRUE }, { 11, 22, TRUE }}},
1301 
1302       /* An empty rangelist merged with an empty rangelist is, drum-roll
1303          please, an empty rangelist. */
1304       {"", "", 0, {{0, 0, FALSE}}}
1305     };
1306 
1307   err = child_err = SVN_NO_ERROR;
1308   for (i = 0; i < SIZE_OF_RANGE_MERGE_TEST_ARRAY; i++)
1309     {
1310       svn_string_t *rangelist2_starting, *rangelist2_ending;
1311 
1312       SVN_ERR(svn_rangelist__parse(&rangelist1, test_data[i].mergeinfo1, pool));
1313       SVN_ERR(svn_rangelist__parse(&rangelist2, test_data[i].mergeinfo2, pool));
1314 
1315       /* Create empty rangelists if necessary. */
1316       if (rangelist1 == NULL)
1317         rangelist1 = apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
1318       if (rangelist2 == NULL)
1319         rangelist2 = apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
1320 
1321       /* Make a copy of rangelist2.  We will merge it into rangelist1, but
1322          rangelist2 should remain unchanged. */
1323       SVN_ERR(svn_rangelist_to_string(&rangelist2_starting, rangelist2,
1324                                       pool));
1325       SVN_ERR(svn_rangelist_merge(&rangelist1, rangelist2, pool));
1326       child_err = verify_ranges_match(rangelist1,
1327                                      (test_data[i]).expected_merge,
1328                                      (test_data[i]).expected_ranges,
1329                                      apr_psprintf(pool,
1330                                                   "svn_rangelist_merge "
1331                                                   "case %i", i),
1332                                      "merge", pool);
1333 
1334       /* Collect all the errors rather than returning on the first. */
1335       if (child_err)
1336         {
1337           if (err)
1338             svn_error_compose(err, child_err);
1339           else
1340             err = child_err;
1341         }
1342 
1343       /* Check that rangelist2 remains unchanged. */
1344       SVN_ERR(svn_rangelist_to_string(&rangelist2_ending, rangelist2, pool));
1345       if (strcmp(rangelist2_ending->data, rangelist2_starting->data))
1346         {
1347           child_err = fail(pool,
1348                            apr_psprintf(pool,
1349                                         "svn_rangelist_merge case %i "
1350                                         "modified its CHANGES arg from "
1351                                         "%s to %s", i,
1352                                         rangelist2_starting->data,
1353                                         rangelist2_ending->data));
1354           if (err)
1355             svn_error_compose(err, child_err);
1356           else
1357             err = child_err;
1358         }
1359     }
1360   return err;
1361 }
1362 
1363 static svn_error_t *
test_rangelist_diff(apr_pool_t * pool)1364 test_rangelist_diff(apr_pool_t *pool)
1365 {
1366   int i;
1367   svn_error_t *err, *child_err;
1368   svn_rangelist_t *from, *to, *added, *deleted;
1369 
1370   /* Structure containing two ranges to diff and the expected output of the
1371      diff both when considering and ignoring range inheritance. */
1372   struct rangelist_diff_test_data
1373   {
1374     /* svn:mergeinfo string representations */
1375     const char *from;
1376     const char *to;
1377 
1378     /* Expected results for performing svn_rangelist_diff
1379        while considering differences in inheritability to be real
1380        differences. */
1381     int expected_add_ranges;
1382     svn_merge_range_t expected_adds[10];
1383     int expected_del_ranges;
1384     svn_merge_range_t expected_dels[10];
1385 
1386     /* Expected results for performing svn_rangelist_diff
1387        while ignoring differences in inheritability. */
1388     int expected_add_ranges_ignore_inheritance;
1389     svn_merge_range_t expected_adds_ignore_inheritance[10];
1390     int expected_del_ranges_ignore_inheritance;
1391     svn_merge_range_t expected_dels_ignore_inheritance[10];
1392   };
1393 
1394   #define SIZE_OF_RANGE_DIFF_TEST_ARRAY 16
1395   /* The actual test data array.
1396 
1397                     'from' --> {"1,5-8",  "1,6,10-12", <-- 'to'
1398       Number of adds when  -->  1, { { 9, 12, TRUE } },
1399       considering inheritance
1400 
1401       Number of dels when  -->  2, { { 4,  5, TRUE }, { 6, 8, TRUE } },
1402       considering inheritance
1403 
1404       Number of adds when  -->  1, { { 9, 12, TRUE } },
1405       ignoring inheritance
1406 
1407       Number of dels when  -->  2, { { 4,  5, TRUE }, { 6, 8, TRUE } } },
1408       ignoring inheritance
1409                                             ^               ^
1410                                     The expected svn_merge_range_t's
1411   */
1412   struct rangelist_diff_test_data test_data[SIZE_OF_RANGE_DIFF_TEST_ARRAY] =
1413     {
1414       /* Add and Delete */
1415       {"1",  "3",
1416        1, { { 2, 3, TRUE } },
1417        1, { { 0, 1, TRUE } },
1418        1, { { 2, 3, TRUE } },
1419        1, { { 0, 1, TRUE } } },
1420 
1421       /* Add only */
1422       {"1",  "1,3",
1423        1, { { 2, 3, TRUE } },
1424        0, { { 0, 0, FALSE } },
1425        1, { { 2, 3, TRUE } },
1426        0, { { 0, 0, FALSE } } },
1427 
1428       /* Delete only */
1429       {"1,3",  "1",
1430        0, { { 0, 0, FALSE } },
1431        1, { { 2, 3, TRUE  } },
1432        0, { { 0, 0, FALSE } },
1433        1, { { 2, 3, TRUE  } } },
1434 
1435       /* No diff */
1436       {"1,3",  "1,3",
1437        0, { { 0, 0, FALSE } },
1438        0, { { 0, 0, FALSE } },
1439        0, { { 0, 0, FALSE } },
1440        0, { { 0, 0, FALSE } } },
1441 
1442       {"1,3*",  "1,3*",
1443        0, { { 0, 0, FALSE } },
1444        0, { { 0, 0, FALSE } },
1445        0, { { 0, 0, FALSE } },
1446        0, { { 0, 0, FALSE } } },
1447 
1448       /* Adds and Deletes */
1449       {"1,5-8",  "1,6,10-12",
1450        1, { { 9, 12, TRUE } },
1451        2, { { 4, 5, TRUE }, { 6, 8, TRUE } },
1452        1, { { 9, 12, TRUE } },
1453        2, { { 4, 5, TRUE }, { 6, 8, TRUE } } },
1454 
1455       {"6*",  "6",
1456        1, { { 5, 6, TRUE  } },
1457        1, { { 5, 6, FALSE } },
1458        0, { { 0, 0, FALSE } },
1459        0, { { 0, 0, FALSE } } },
1460 
1461       /* Intersecting range with different inheritability */
1462       {"6",  "6*",
1463        1, { { 5, 6, FALSE } },
1464        1, { { 5, 6, TRUE  } },
1465        0, { { 0, 0, FALSE } },
1466        0, { { 0, 0, FALSE } } },
1467 
1468       {"6*",  "6",
1469        1, { { 5, 6, TRUE  } },
1470        1, { { 5, 6, FALSE } },
1471        0, { { 0, 0, FALSE } },
1472        0, { { 0, 0, FALSE } } },
1473 
1474       {"1,5-8",  "1,6*,10-12",
1475        2, { { 5,  6, FALSE }, { 9, 12, TRUE } },
1476        1, { { 4,  8, TRUE  } },
1477        1, { { 9, 12, TRUE  } },
1478        2, { { 4,  5, TRUE  }, { 6,  8, TRUE } } },
1479 
1480      {"1,5-8*",  "1,6,10-12",
1481        2, { { 5,  6, TRUE  }, { 9, 12, TRUE  } },
1482        1, { { 4,  8, FALSE } },
1483        1, { { 9, 12, TRUE  } },
1484        2, { { 4,  5, FALSE }, { 6,  8, FALSE } } },
1485 
1486       /* Empty range diffs */
1487       {"3-9",  "",
1488        0, { { 0, 0, FALSE } },
1489        1, { { 2, 9, TRUE  } },
1490        0, { { 0, 0, FALSE } },
1491        1, { { 2, 9, TRUE  } } },
1492 
1493       {"3-9*",  "",
1494        0, { { 0, 0, FALSE } },
1495        1, { { 2, 9, FALSE } },
1496        0, { { 0, 0, FALSE } },
1497        1, { { 2, 9, FALSE } } },
1498 
1499       {"",  "3-9",
1500        1, { { 2, 9, TRUE  } },
1501        0, { { 0, 0, FALSE } },
1502        1, { { 2, 9, TRUE  } },
1503        0, { { 0, 0, FALSE } } },
1504 
1505       {"",  "3-9*",
1506        1, { { 2, 9, FALSE } },
1507        0, { { 0, 0, FALSE } },
1508        1, { { 2, 9, FALSE } },
1509        0, { { 0, 0, FALSE } } },
1510 
1511        /* Empty range no diff */
1512       {"",  "",
1513        0, { { 0, 0, FALSE } },
1514        0, { { 0, 0, FALSE } },
1515        0, { { 0, 0, FALSE } },
1516        0, { { 0, 0, FALSE } } },
1517     };
1518 
1519   err = child_err = SVN_NO_ERROR;
1520   for (i = 0; i < SIZE_OF_RANGE_DIFF_TEST_ARRAY; i++)
1521     {
1522       SVN_ERR(svn_rangelist__parse(&to, test_data[i].to, pool));
1523       SVN_ERR(svn_rangelist__parse(&from, test_data[i].from, pool));
1524 
1525       /* Represent empty mergeinfo with an empty rangelist. */
1526       if (to == NULL)
1527         to = apr_array_make(pool, 0, sizeof(*to));
1528       if (from == NULL)
1529         from = apr_array_make(pool, 0, sizeof(*from));
1530 
1531       /* First diff the ranges while considering
1532          differences in inheritance. */
1533       SVN_ERR(svn_rangelist_diff(&deleted, &added, from, to, TRUE, pool));
1534 
1535       child_err = verify_ranges_match(added,
1536                                      (test_data[i]).expected_adds,
1537                                      (test_data[i]).expected_add_ranges,
1538                                      apr_psprintf(pool,
1539                                                   "svn_rangelist_diff"
1540                                                   "case %i", i),
1541                                      "diff", pool);
1542       if (!child_err)
1543         child_err = verify_ranges_match(deleted,
1544                                         (test_data[i]).expected_dels,
1545                                         (test_data[i]).expected_del_ranges,
1546                                         apr_psprintf(pool,
1547                                                      "svn_rangelist_diff"
1548                                                      "case %i", i),
1549                                                      "diff", pool);
1550       if (!child_err)
1551         {
1552           /* Now do the diff while ignoring differences in inheritance. */
1553           SVN_ERR(svn_rangelist_diff(&deleted, &added, from, to, FALSE,
1554                                      pool));
1555           child_err = verify_ranges_match(
1556             added,
1557             (test_data[i]).expected_adds_ignore_inheritance,
1558             (test_data[i]).expected_add_ranges_ignore_inheritance,
1559             apr_psprintf(pool, "svn_rangelist_diff case %i", i),
1560             "diff", pool);
1561 
1562           if (!child_err)
1563             child_err = verify_ranges_match(
1564               deleted,
1565               (test_data[i]).expected_dels_ignore_inheritance,
1566               (test_data[i]).expected_del_ranges_ignore_inheritance,
1567               apr_psprintf(pool, "svn_rangelist_diff case %i", i),
1568               "diff", pool);
1569         }
1570 
1571       /* Collect all the errors rather than returning on the first. */
1572       if (child_err)
1573         {
1574           if (err)
1575             svn_error_compose(err, child_err);
1576           else
1577             err = child_err;
1578         }
1579     }
1580   return err;
1581 }
1582 
1583 
1584 /* Test data structure for test_remove_prefix_from_catalog(). */
1585 struct catalog_bits
1586 {
1587   const char *orig_path;
1588   const char *new_path;
1589   const char *mergeinfo;
1590 };
1591 
1592 
1593 /* Helper for test_remove_prefix_from_catalog(). */
1594 static svn_error_t *
remove_prefix_helper(struct catalog_bits * test_data,const char * prefix_path,apr_pool_t * pool)1595 remove_prefix_helper(struct catalog_bits *test_data,
1596                      const char *prefix_path,
1597                      apr_pool_t *pool)
1598 {
1599   svn_mergeinfo_catalog_t in_catalog, out_catalog, exp_out_catalog;
1600   apr_hash_index_t *hi;
1601   int i = 0;
1602 
1603   in_catalog = apr_hash_make(pool);
1604   exp_out_catalog = apr_hash_make(pool);
1605   while (test_data[i].orig_path)
1606     {
1607       struct catalog_bits data = test_data[i];
1608       const char *orig_path = apr_pstrdup(pool, data.orig_path);
1609       const char *new_path = apr_pstrdup(pool, data.new_path);
1610       svn_mergeinfo_t mergeinfo;
1611       SVN_ERR(svn_mergeinfo_parse(&mergeinfo, data.mergeinfo, pool));
1612       apr_hash_set(in_catalog, orig_path, APR_HASH_KEY_STRING, mergeinfo);
1613       apr_hash_set(exp_out_catalog, new_path, APR_HASH_KEY_STRING, mergeinfo);
1614       i++;
1615     }
1616   SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&out_catalog, in_catalog,
1617                                                     prefix_path, pool));
1618   if (apr_hash_count(exp_out_catalog) != apr_hash_count(out_catalog))
1619     return svn_error_create(SVN_ERR_TEST_FAILED, 0,
1620                             "Got unexpected number of catalog entries");
1621   for (hi = apr_hash_first(pool, out_catalog); hi; hi = apr_hash_next(hi))
1622     {
1623       const void *path;
1624       apr_ssize_t path_len;
1625       void *out_mergeinfo, *exp_out_mergeinfo;
1626       apr_hash_this(hi, &path, &path_len, &out_mergeinfo);
1627       exp_out_mergeinfo = apr_hash_get(exp_out_catalog, path, path_len);
1628       if (! exp_out_mergeinfo)
1629         return svn_error_createf(SVN_ERR_TEST_FAILED, 0,
1630                                  "Found unexpected key '%s' in catalog",
1631                                  (const char *)path);
1632       if (exp_out_mergeinfo != out_mergeinfo)
1633         return svn_error_create(SVN_ERR_TEST_FAILED, 0,
1634                                 "Detected value tampering in catalog");
1635     }
1636   return SVN_NO_ERROR;
1637 }
1638 
1639 static svn_error_t *
test_remove_prefix_from_catalog(apr_pool_t * pool)1640 test_remove_prefix_from_catalog(apr_pool_t *pool)
1641 {
1642   apr_pool_t *subpool = svn_pool_create(pool);
1643 
1644   /* For testing the remove of the prefix "/trunk"  */
1645   struct catalog_bits test_data_1[] =
1646     {
1647       { "/trunk",           "",          "/A:1" },
1648       { "/trunk/foo",       "foo",       "/A/foo:1,3*" },
1649       { "/trunk/foo/bar",   "foo/bar",   "/A/foo:1-4" },
1650       { "/trunk/baz",       "baz",       "/A/baz:2" },
1651       { NULL, NULL, NULL }
1652     };
1653 
1654   /* For testing the remove of the prefix "/"  */
1655   struct catalog_bits test_data_2[] =
1656     {
1657       { "/",                "",                "/:2" },
1658       { "/trunk",           "trunk",           "/A:1" },
1659       { "/trunk/foo",       "trunk/foo",       "/A/foo:1,3*" },
1660       { "/trunk/foo/bar",   "trunk/foo/bar",   "/A/foo:1-4" },
1661       { "/trunk/baz",       "trunk/baz",       "/A/baz:2" },
1662       { NULL, NULL, NULL }
1663     };
1664 
1665   svn_pool_clear(subpool);
1666   SVN_ERR(remove_prefix_helper(test_data_1, "/trunk", subpool));
1667 
1668   svn_pool_clear(subpool);
1669   SVN_ERR(remove_prefix_helper(test_data_2, "/", subpool));
1670 
1671   svn_pool_destroy(subpool);
1672   return SVN_NO_ERROR;
1673 }
1674 
1675 static svn_error_t *
test_rangelist_merge_overlap(apr_pool_t * pool)1676 test_rangelist_merge_overlap(apr_pool_t *pool)
1677 {
1678   const char *rangelist_str = "19473-19612*,19615-19630*,19631-19634";
1679   const char *changes_str = "15014-20515*";
1680   const char *expected_str = "15014-19630*,19631-19634,19635-20515*";
1681   /* wrong result: "15014-19630*,19634-19631*,19631-19634,19635-20515*" */
1682   svn_rangelist_t *rangelist, *changes;
1683   svn_string_t *result_string;
1684 
1685   /* prepare the inputs */
1686   SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool));
1687   SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool));
1688   SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
1689   SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes));
1690 
1691   /* perform the merge */
1692   SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool));
1693 
1694   /* check the output */
1695   SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
1696   SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool));
1697   SVN_TEST_STRING_ASSERT(result_string->data, expected_str);
1698 
1699   return SVN_NO_ERROR;
1700 }
1701 
1702 static svn_error_t *
test_rangelist_loop(apr_pool_t * pool)1703 test_rangelist_loop(apr_pool_t *pool)
1704 {
1705   apr_pool_t *iterpool = svn_pool_create(pool);
1706   int x, y;
1707 
1708   for (x = 0; x < 62; x++)
1709     for (y = x + 1; y < 63; y++)
1710       {
1711         svn_rangelist_t *base_list;
1712         svn_rangelist_t *change_list;
1713         svn_merge_range_t *mrange;
1714         svn_pool_clear(iterpool);
1715 
1716         SVN_ERR(svn_rangelist__parse(&base_list,
1717                                      "2,4,7-9,12-15,18-20,"
1718                                      "22*,25*,28-30*,33-35*,"
1719                                      "38-40,43-45*,48-50,52-54,56-59*",
1720                                      iterpool));
1721 
1722         change_list = apr_array_make(iterpool, 1, sizeof(mrange));
1723 
1724         mrange = apr_pcalloc(pool, sizeof(*mrange));
1725         mrange->start = x;
1726         mrange->end = y;
1727         APR_ARRAY_PUSH(change_list, svn_merge_range_t *) = mrange;
1728 
1729         {
1730           svn_rangelist_t *bl = svn_rangelist_dup(base_list, iterpool);
1731           svn_rangelist_t *cl = svn_rangelist_dup(change_list, iterpool);
1732 
1733           SVN_TEST_ASSERT(svn_rangelist__is_canonical(bl));
1734           SVN_TEST_ASSERT(svn_rangelist__is_canonical(cl));
1735 
1736           SVN_ERR(svn_rangelist_merge2(bl, cl, iterpool, iterpool));
1737 
1738           SVN_TEST_ASSERT(svn_rangelist__is_canonical(bl));
1739           SVN_TEST_ASSERT(svn_rangelist__is_canonical(cl));
1740 
1741           /* TODO: Verify result */
1742         }
1743 
1744         {
1745           svn_rangelist_t *bl = svn_rangelist_dup(base_list, iterpool);
1746           svn_rangelist_t *cl = svn_rangelist_dup(change_list, iterpool);
1747 
1748           SVN_ERR(svn_rangelist_merge2(cl, bl, iterpool, iterpool));
1749 
1750           SVN_TEST_ASSERT(svn_rangelist__is_canonical(bl));
1751           SVN_TEST_ASSERT(svn_rangelist__is_canonical(cl));
1752 
1753           /* TODO: Verify result */
1754         }
1755 
1756         mrange->inheritable = TRUE;
1757 
1758         {
1759           svn_rangelist_t *bl = svn_rangelist_dup(base_list, iterpool);
1760           svn_rangelist_t *cl = svn_rangelist_dup(change_list, iterpool);
1761 
1762           SVN_TEST_ASSERT(svn_rangelist__is_canonical(bl));
1763           SVN_TEST_ASSERT(svn_rangelist__is_canonical(cl));
1764 
1765           SVN_ERR(svn_rangelist_merge2(bl, cl, iterpool, iterpool));
1766 
1767           SVN_TEST_ASSERT(svn_rangelist__is_canonical(bl));
1768           SVN_TEST_ASSERT(svn_rangelist__is_canonical(cl));
1769 
1770           /* TODO: Verify result */
1771         }
1772 
1773         {
1774           svn_rangelist_t *bl = svn_rangelist_dup(base_list, iterpool);
1775           svn_rangelist_t *cl = svn_rangelist_dup(change_list, iterpool);
1776 
1777           SVN_ERR(svn_rangelist_merge2(cl, bl, iterpool, iterpool));
1778 
1779           SVN_TEST_ASSERT(svn_rangelist__is_canonical(bl));
1780           SVN_TEST_ASSERT(svn_rangelist__is_canonical(cl));
1781 
1782           /* TODO: Verify result */
1783         }
1784       }
1785 
1786   return SVN_NO_ERROR;
1787 }
1788 
1789 /* A specific case where result was non-canonical, around svn 1.10 ~ 1.13. */
1790 static svn_error_t *
test_rangelist_merge_canonical_result(apr_pool_t * pool)1791 test_rangelist_merge_canonical_result(apr_pool_t *pool)
1792 {
1793   const char *rangelist_str = "8-10";
1794   const char *changes_str = "5-10*,11-24";
1795   const char *expected_str = "5-7*,8-24";
1796   /* wrong result: "5-7*,8-10,11-24" */
1797   svn_rangelist_t *rangelist, *changes;
1798   svn_string_t *result_string;
1799 
1800   /* prepare the inputs */
1801   SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool));
1802   SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool));
1803   SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
1804   SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes));
1805 
1806   /* perform the merge */
1807   SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool));
1808 
1809   /* check the output */
1810   SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
1811   SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool));
1812   SVN_TEST_STRING_ASSERT(result_string->data, expected_str);
1813 
1814   return SVN_NO_ERROR;
1815 }
1816 
1817 /* Test svn_rangelist_merge2() with specific inputs derived from an
1818  * occurrence of issue #4840 "in the wild", that triggered a hard
1819  * assertion failure (abort) in a 1.10.6 release-mode build.
1820  */
1821 static svn_error_t *
test_rangelist_merge_array_insert_failure(apr_pool_t * pool)1822 test_rangelist_merge_array_insert_failure(apr_pool_t *pool)
1823 {
1824   svn_rangelist_t *rx, *ry;
1825   svn_string_t *rxs;
1826 
1827   /* Simplified case with same failure mode as reported case: error
1828    * "E200004: svn_sort__array_insert2:
1829    *  Attempted insert at index 4 in array length 3" */
1830   SVN_ERR(svn_rangelist__parse(&rx, "2*,4*,6*,8", pool));
1831   SVN_ERR(svn_rangelist__parse(&ry, "1-9*,11", pool));
1832   SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool));
1833   SVN_ERR(svn_rangelist_to_string(&rxs, rx, pool));
1834   SVN_TEST_STRING_ASSERT(rxs->data, "1-7*,8,9*,11");
1835 
1836   /* Actual reported case: in v1.10.6, aborted; after r1872118, error
1837    * "E200004: svn_sort__array_insert2:
1838    *  Attempted insert at index 57 in array length 55".  The actual "index"
1839    *  and "array length" numbers vary with changes such as r1823728. */
1840   SVN_ERR(svn_rangelist__parse(&rx, "997347-997597*,997884-1000223*,1000542-1000551*,1001389-1001516,1002139-1002268*,1002896-1003064*,1003320-1003468,1005939-1006089*,1006443-1006630*,1006631-1006857,1007028-1007116*,1009467-1009629,1009630-1010007*,1010774-1010860,1011036-1011502,1011672-1014004*,1014023-1014197,1014484-1014542*,1015077-1015568,1016219-1016365,1016698-1016845,1017331-1018616,1027032-1027180,1027855-1028051,1028261-1028395,1028553-1028663,1028674-1028708,1028773-1028891*,1029223-1030557,1032239-1032284*,1032801-1032959,1032960-1033074*,1033745-1033810,1034990-1035104,1035435-1036108*,1036109-1036395,1036396-1036865*,1036866-1036951,1036952-1037647*,1037648-1037750,1037751-1038548*,1038549-1038700,1038701-1042103*,1042104-1042305,1042306-1046626*,1046627-1046910,1046911-1047676*,1047677-1047818,1047819-1047914*,1047915-1048025,1048026-1048616*,1048617-1048993,1048994-1050066*,1054605-1054739,1054854-1055021", pool));
1841   SVN_ERR(svn_rangelist__parse(&ry, "1035435-1050066*,1052459-1054617", pool));
1842   SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool));
1843   /* Here we don't care to check the result; just that it returns "success". */
1844   return SVN_NO_ERROR;
1845 }
1846 
1847 
1848 /* Random testing parameters and coverage
1849  *
1850  * The parameters for testing random inputs, in conjunction with the
1851  * specific test case generation code, were adjusted so as to observe the
1852  * tests generating each of the known failure modes.  The aim is also to
1853  * have sufficient coverage of inputs to discover other failure modes in
1854  * future if the code is changed.
1855  *
1856  * There are neither theoretic nor empirical guarantees on the coverage.
1857  */
1858 
1859 /* Randomize revision numbers over this small range.
1860  * (With a larger range, we would find edge cases more rarely.)
1861  * See comment "Random testing parameters and coverage" */
1862 #define RANGELIST_TESTS_MAX_REV 15
1863 
1864 /* A representation of svn_rangelist_t in which
1865  *   root[R]    := (revision R is in the rangelist)
1866  *   inherit[R] := (revision R is in the rangelist and inheritable)
1867  *
1868  * Assuming all forward ranges.
1869  */
1870 typedef struct rl_array_t {
1871     svn_boolean_t root[RANGELIST_TESTS_MAX_REV + 1];
1872     svn_boolean_t inherit[RANGELIST_TESTS_MAX_REV + 1];
1873 } rl_array_t;
1874 
1875 static void
rangelist_to_array(rl_array_t * a,const svn_rangelist_t * rl)1876 rangelist_to_array(rl_array_t *a,
1877                    const svn_rangelist_t *rl)
1878 {
1879   int i;
1880 
1881   memset(a, 0, sizeof(*a));
1882   for (i = 0; i < rl->nelts; i++)
1883     {
1884       svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *);
1885       svn_revnum_t r;
1886 
1887       for (r = range->start + 1; r <= range->end; r++)
1888         {
1889           a->root[r] = TRUE;
1890           a->inherit[r] = range->inheritable;
1891         }
1892     }
1893 }
1894 
1895 /* Compute the union of two rangelists arrays.
1896  * Let MA := union(BA, CA)
1897  */
1898 static void
rangelist_array_union(rl_array_t * ma,const rl_array_t * ba,const rl_array_t * ca)1899 rangelist_array_union(rl_array_t *ma,
1900                       const rl_array_t *ba,
1901                       const rl_array_t *ca)
1902 {
1903   svn_revnum_t r;
1904 
1905   for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
1906     {
1907       ma->root[r]    = ba->root[r]    || ca->root[r];
1908       ma->inherit[r] = ba->inherit[r] || ca->inherit[r];
1909     }
1910 }
1911 
1912 /* Return TRUE iff two rangelist arrays are equal.
1913  */
1914 static svn_boolean_t
rangelist_array_equal(const rl_array_t * ba,const rl_array_t * ca)1915 rangelist_array_equal(const rl_array_t *ba,
1916                       const rl_array_t *ca)
1917 {
1918   svn_revnum_t r;
1919 
1920   for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
1921     {
1922       if (ba->root[r]    != ca->root[r]
1923        || ba->inherit[r] != ca->inherit[r])
1924         {
1925           return FALSE;
1926         }
1927     }
1928   return TRUE;
1929 }
1930 
1931 #define IS_VALID_FORWARD_RANGE(range) \
1932   (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
1933 
1934 /* Check rangelist is sorted and contains forward ranges. */
1935 static svn_boolean_t
rangelist_is_sorted(const svn_rangelist_t * rangelist)1936 rangelist_is_sorted(const svn_rangelist_t *rangelist)
1937 {
1938   int i;
1939 
1940   for (i = 0; i < rangelist->nelts; i++)
1941     {
1942       const svn_merge_range_t *thisrange
1943         = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1944 
1945       if (!IS_VALID_FORWARD_RANGE(thisrange))
1946         return FALSE;
1947     }
1948   for (i = 1; i < rangelist->nelts; i++)
1949     {
1950       const svn_merge_range_t *lastrange
1951         = APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *);
1952       const svn_merge_range_t *thisrange
1953         = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1954 
1955       if (svn_sort_compare_ranges(&lastrange, &thisrange) > 0)
1956         return FALSE;
1957     }
1958   return TRUE;
1959 }
1960 
1961 /* Return a random number R, where 0 <= R < N.
1962  */
rand_less_than(int n,apr_uint32_t * seed)1963 static int rand_less_than(int n, apr_uint32_t *seed)
1964 {
1965   apr_uint32_t next = svn_test_rand(seed);
1966   return ((apr_uint64_t)next * n) >> 32;
1967 }
1968 
1969 /* Return a random integer in a triangular (centre-weighted) distribution in
1970  * the inclusive interval [MIN, MAX]. */
1971 static int
rand_interval_triangular(int min,int max,apr_uint32_t * seed)1972 rand_interval_triangular(int min, int max, apr_uint32_t *seed)
1973 {
1974   int span = max - min + 1;
1975 
1976   return min + rand_less_than(span/2 + 1, seed)
1977              + rand_less_than((span+1)/2, seed);
1978 }
1979 
1980 /* Generate a rangelist with a random number of random ranges.
1981  * Choose from 0 to NON_V_MAX_RANGES ranges, biased towards the middle.
1982  */
1983 #define NON_V_MAX_RANGES 4  /* See "Random testing parameters and coverage" */
1984 static void
rangelist_random_non_validated(svn_rangelist_t ** rl,apr_uint32_t * seed,apr_pool_t * pool)1985 rangelist_random_non_validated(svn_rangelist_t **rl,
1986                                apr_uint32_t *seed,
1987                                apr_pool_t *pool)
1988 {
1989   svn_rangelist_t *r = apr_array_make(pool, NON_V_MAX_RANGES,
1990                                       sizeof(svn_merge_range_t *));
1991   int n_ranges = rand_interval_triangular(0, NON_V_MAX_RANGES, seed);
1992   int i;
1993 
1994   for (i = 0; i < n_ranges; i++)
1995     {
1996       svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
1997 
1998       mrange->start = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed);
1999       mrange->end = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed);
2000       mrange->inheritable = rand_less_than(2, seed);
2001       APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange;
2002     }
2003   *rl = r;
2004 }
2005 
2006 /* Compare two integers pointed to by A_P and B_P, for use with qsort(). */
2007 static int
int_compare(const void * a_p,const void * b_p)2008 int_compare(const void *a_p, const void *b_p)
2009 {
2010   const int *a = a_p, *b = b_p;
2011 
2012   return (*a < *b) ? -1 : (*a > *b) ? 1 : 0;
2013 }
2014 
2015 /* Fill an ARRAY[ARRAY_LENGTH] with values each in the inclusive range
2016  * [0, MAX].  The values are in ascending order, possibly with the same
2017  * value repeated any number of times.
2018  */
2019 static void
ascending_values(int * array,int array_length,int max,apr_uint32_t * seed)2020 ascending_values(int *array,
2021                  int array_length,
2022                  int max,
2023                  apr_uint32_t *seed)
2024 {
2025   int i;
2026 
2027   for (i = 0; i < array_length; i++)
2028     array[i] = rand_less_than(max + 1, seed);
2029   /* Sort them. (Some values will be repeated.) */
2030   qsort(array, array_length, sizeof(*array), int_compare);
2031 }
2032 
2033 /* Generate a random rangelist that is not necessarily canonical
2034  * but is at least sorted according to svn_sort_compare_ranges()
2035  * and on which svn_rangelist__canonicalize() would succeed.
2036  * Choose from 0 to SEMI_C_MAX_RANGES ranges, biased towards the middle.
2037  */
2038 #define SEMI_C_MAX_RANGES 8
2039 static void
rangelist_random_semi_canonical(svn_rangelist_t ** rl,apr_uint32_t * seed,apr_pool_t * pool)2040 rangelist_random_semi_canonical(svn_rangelist_t **rl,
2041                                 apr_uint32_t *seed,
2042                                 apr_pool_t *pool)
2043 {
2044   svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *));
2045   int n_ranges = rand_interval_triangular(0, SEMI_C_MAX_RANGES, seed);
2046   int start_and_end_revs[SEMI_C_MAX_RANGES * 2];
2047   int i;
2048 
2049   /* Choose start and end revs of the ranges. To end up with ranges evenly
2050    * distributed up to RANGELIST_TESTS_MAX_REV, we start with them evenly
2051    * distributed up to RANGELIST_TESTS_MAX_REV - N_RANGES, before spreading. */
2052   ascending_values(start_and_end_revs, n_ranges * 2,
2053                    RANGELIST_TESTS_MAX_REV - n_ranges,
2054                    seed);
2055   /* Some values will be repeated. Within one range, that is not allowed,
2056    * so add 1 to the length of each range, spreading the ranges out so they
2057    * still don't overlap:
2058    * [(6,6), (6,8)] becomes [(6,7), (7, 10)] */
2059   for (i = 0; i < n_ranges; i++)
2060     {
2061       start_and_end_revs[i*2] += i;
2062       start_and_end_revs[i*2 + 1] += i+1;
2063     }
2064 
2065   for (i = 0; i < n_ranges; i++)
2066     {
2067       svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
2068 
2069       mrange->start = start_and_end_revs[i * 2];
2070       mrange->end = start_and_end_revs[i * 2 + 1];
2071       mrange->inheritable = rand_less_than(2, seed);
2072       APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange;
2073     }
2074   *rl = r;
2075 
2076   /* check postconditions */
2077   {
2078     svn_rangelist_t *dup;
2079     svn_error_t *err;
2080 
2081     SVN_ERR_ASSERT_NO_RETURN(rangelist_is_sorted(*rl));
2082     dup = svn_rangelist_dup(*rl, pool);
2083     err = svn_rangelist__canonicalize(dup, pool);
2084     SVN_ERR_ASSERT_NO_RETURN(!err);
2085   }
2086 }
2087 
2088 /* Generate a random rangelist that satisfies svn_rangelist__is_canonical().
2089  */
2090 static void
rangelist_random_canonical(svn_rangelist_t ** rl,apr_uint32_t * seed,apr_pool_t * pool)2091 rangelist_random_canonical(svn_rangelist_t **rl,
2092                            apr_uint32_t *seed,
2093                            apr_pool_t *pool)
2094 {
2095   svn_rangelist_t *r;
2096   int i;
2097 
2098   rangelist_random_semi_canonical(&r, seed, pool);
2099   for (i = 1; i < r->nelts; i++)
2100     {
2101       svn_merge_range_t *prev_mrange = APR_ARRAY_IDX(r, i-1, svn_merge_range_t *);
2102       svn_merge_range_t *mrange = APR_ARRAY_IDX(r, i, svn_merge_range_t *);
2103 
2104       /* to be canonical: adjacent ranges need differing inheritability */
2105       if (mrange->start == prev_mrange->end)
2106         {
2107           mrange->inheritable = !prev_mrange->inheritable;
2108         }
2109     }
2110   *rl = r;
2111 
2112   /* check postconditions */
2113   SVN_ERR_ASSERT_NO_RETURN(svn_rangelist__is_canonical(*rl));
2114 }
2115 
2116 static const char *
rangelist_to_string(const svn_rangelist_t * rl,apr_pool_t * pool)2117 rangelist_to_string(const svn_rangelist_t *rl,
2118                     apr_pool_t *pool)
2119 {
2120   svn_error_t *err;
2121   svn_string_t *ss;
2122 
2123   err = svn_rangelist_to_string(&ss, rl, pool);
2124   if (err)
2125     {
2126       const char *s
2127         = apr_psprintf(pool, "<rangelist[%d ranges]: %s>",
2128                        rl->nelts, svn_error_purge_tracing(err)->message);
2129       svn_error_clear(err);
2130       return s;
2131     }
2132   return ss->data;
2133 }
2134 
2135 /* Try svn_rangelist_merge2(rlx, rly) and check errors and result */
2136 static svn_error_t *
rangelist_merge_random_inputs(svn_rangelist_t * rlx,svn_rangelist_t * rly,apr_pool_t * pool)2137 rangelist_merge_random_inputs(svn_rangelist_t *rlx,
2138                               svn_rangelist_t *rly,
2139                               apr_pool_t *pool)
2140 {
2141   rl_array_t ax, ay, a_expected, a_actual;
2142   svn_rangelist_t *rlm;
2143 
2144   rangelist_to_array(&ax, rlx);
2145   rangelist_to_array(&ay, rly);
2146 
2147   rlm = svn_rangelist_dup(rlx, pool);
2148   /*printf("testcase: %s / %s\n", rangelist_to_string(rlx, pool), rangelist_to_string(rly, pool));*/
2149   SVN_ERR(svn_rangelist_merge2(rlm, rly, pool, pool));
2150 
2151   if (!svn_rangelist__is_canonical(rlm))
2152     {
2153       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
2154                                "non-canonical result %s",
2155                                rangelist_to_string(rlm, pool));
2156     }
2157 
2158   /*SVN_TEST_ASSERT(rangelist_equal(rlm, ...));*/
2159   rangelist_array_union(&a_expected, &ax, &ay);
2160   rangelist_to_array(&a_actual, rlm);
2161   if (!rangelist_array_equal(&a_actual, &a_expected))
2162     {
2163       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
2164                                "wrong result: (c? %d / %d) -> %s",
2165                                svn_rangelist__is_canonical(rlx),
2166                                svn_rangelist__is_canonical(rly),
2167                                rangelist_to_string(rlm, pool));
2168     }
2169 
2170   return SVN_NO_ERROR;
2171 }
2172 
2173 /* Insert a failure mode (ERR_CHAIN) into RESPONSES, keyed by a message
2174  * representing its failure mode.  The failure mode message is the lowest
2175  * level error message in ERR_CHAIN, with some case-specific details
2176  * removed to aid de-duplication.  If it is new failure mode (not already in
2177  * RESPONSES), store the error and return the message (hash key), else
2178  * clear the error and return NULL.
2179  */
2180 static const char *
add_failure_mode(svn_error_t * err_chain,apr_hash_t * failure_modes)2181 add_failure_mode(svn_error_t *err_chain,
2182                  apr_hash_t *failure_modes)
2183 {
2184   svn_error_t *err = err_chain;
2185   char buf[100];
2186   const char *message;
2187 
2188   if (!err_chain)
2189     return NULL;
2190 
2191   while (err->child)
2192     err = err->child;
2193   message = svn_err_best_message(err, buf, sizeof(buf));
2194 
2195   /* For deduplication, ignore case-specific data in certain messages. */
2196   if (strstr(message, "Unable to parse overlapping revision ranges '"))
2197             message = "Unable to parse overlapping revision ranges '...";
2198   if (strstr(message, "wrong result: (c?"))
2199             message = "wrong result: (c?...";
2200   if (strstr(message, "svn_sort__array_insert2: Attempted insert at index "))
2201             message = "svn_sort__array_insert2: Attempted insert at index ...";
2202 
2203   if (!svn_hash_gets(failure_modes, message))
2204     {
2205       svn_hash_sets(failure_modes, message, err);
2206       return message;
2207     }
2208   else
2209     {
2210       svn_error_clear(err_chain);
2211       return NULL;
2212     }
2213 }
2214 
2215 static void
clear_failure_mode_errors(apr_hash_t * failure_modes,apr_pool_t * scratch_pool)2216 clear_failure_mode_errors(apr_hash_t *failure_modes, apr_pool_t *scratch_pool)
2217 {
2218   apr_hash_index_t *hi;
2219 
2220   for (hi = apr_hash_first(scratch_pool, failure_modes);
2221        hi;
2222        hi = apr_hash_next(hi))
2223     {
2224       svn_error_t *err = apr_hash_this_val(hi);
2225       svn_error_clear(err);
2226     }
2227 }
2228 
2229 static svn_error_t *
test_rangelist_merge_random_canonical_inputs(apr_pool_t * pool)2230 test_rangelist_merge_random_canonical_inputs(apr_pool_t *pool)
2231 {
2232   static apr_uint32_t seed = 0;
2233   apr_pool_t *iterpool = svn_pool_create(pool);
2234   apr_hash_t *failure_modes = apr_hash_make(pool);
2235   svn_boolean_t pass = TRUE;
2236   int ix, iy;
2237 
2238   /* "300": See comment "Random testing parameters and coverage" */
2239   for (ix = 0; ix < 300; ix++)
2240    {
2241     svn_rangelist_t *rlx;
2242 
2243     rangelist_random_canonical(&rlx, &seed, pool);
2244 
2245     for (iy = 0; iy < 300; iy++)
2246       {
2247         svn_rangelist_t *rly;
2248         svn_error_t *err;
2249         const char *failure_mode;
2250 
2251         svn_pool_clear(iterpool);
2252 
2253         rangelist_random_canonical(&rly, &seed, iterpool);
2254 
2255         err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool));
2256         failure_mode = add_failure_mode(err, failure_modes);
2257         if (failure_mode)
2258           {
2259             printf("first example of a failure mode: %s / %s\n"
2260                    "  %s\n",
2261                    rangelist_to_string(rlx, iterpool),
2262                    rangelist_to_string(rly, iterpool),
2263                    failure_mode);
2264             /*svn_handle_error(err, stdout, FALSE);*/
2265             pass = FALSE;
2266           }
2267       }
2268    }
2269 
2270   clear_failure_mode_errors(failure_modes, pool);
2271 
2272   if (!pass)
2273     return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
2274                              "Test failed: %d failure modes",
2275                              apr_hash_count(failure_modes));
2276   return SVN_NO_ERROR;
2277 }
2278 
2279 /* Test svn_rangelist_merge2() with inputs that confirm to its doc-string.
2280  * Fail if any errors are produced.
2281  */
2282 static svn_error_t *
test_rangelist_merge_random_semi_c_inputs(apr_pool_t * pool)2283 test_rangelist_merge_random_semi_c_inputs(apr_pool_t *pool)
2284 {
2285   static apr_uint32_t seed = 0;
2286   apr_pool_t *iterpool = svn_pool_create(pool);
2287   apr_hash_t *failure_modes = apr_hash_make(pool);
2288   svn_boolean_t pass = TRUE;
2289   int ix, iy;
2290 
2291   /* "300": See comment "Random testing parameters and coverage" */
2292   for (ix = 0; ix < 300; ix++)
2293    {
2294     svn_rangelist_t *rlx;
2295 
2296     rangelist_random_semi_canonical(&rlx, &seed, pool);
2297 
2298     for (iy = 0; iy < 300; iy++)
2299       {
2300         svn_rangelist_t *rly;
2301         svn_error_t *err;
2302         const char *failure_mode;
2303 
2304         svn_pool_clear(iterpool);
2305 
2306         rangelist_random_semi_canonical(&rly, &seed, iterpool);
2307 
2308         err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool));
2309         failure_mode = add_failure_mode(err, failure_modes);
2310         if (failure_mode)
2311           {
2312             printf("first example of a failure mode: %s / %s\n"
2313                    "  %s\n",
2314                    rangelist_to_string(rlx, iterpool),
2315                    rangelist_to_string(rly, iterpool),
2316                    failure_mode);
2317             /*svn_handle_error(err, stdout, FALSE);*/
2318             pass = FALSE;
2319           }
2320       }
2321    }
2322 
2323   clear_failure_mode_errors(failure_modes, pool);
2324 
2325   if (!pass)
2326     return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
2327                              "Test failed: %d failure modes",
2328                              apr_hash_count(failure_modes));
2329   return SVN_NO_ERROR;
2330 }
2331 
2332 /* Test svn_rangelist_merge2() with random non-validated inputs.
2333  *
2334  * Unlike the tests with valid inputs, this test expects many assertion
2335  * failures.  We don't care about those.  All we care about is that it does
2336  * not crash. */
2337 static svn_error_t *
test_rangelist_merge_random_non_validated_inputs(apr_pool_t * pool)2338 test_rangelist_merge_random_non_validated_inputs(apr_pool_t *pool)
2339 {
2340   static apr_uint32_t seed = 0;
2341   apr_pool_t *iterpool = svn_pool_create(pool);
2342   apr_hash_t *failure_modes = apr_hash_make(pool);
2343   int ix, iy;
2344 
2345   /* "300": See comment "Random testing parameters and coverage" */
2346   for (ix = 0; ix < 300; ix++)
2347    {
2348     svn_rangelist_t *rlx;
2349 
2350     rangelist_random_non_validated(&rlx, &seed, pool);
2351 
2352     for (iy = 0; iy < 300; iy++)
2353       {
2354         svn_rangelist_t *rly;
2355         svn_error_t *err;
2356 
2357         svn_pool_clear(iterpool);
2358 
2359         rangelist_random_non_validated(&rly, &seed, iterpool);
2360 
2361         err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool));
2362         add_failure_mode(err, failure_modes);
2363       }
2364    }
2365 
2366   clear_failure_mode_errors(failure_modes, pool);
2367 
2368   return SVN_NO_ERROR;
2369 }
2370 
2371 /* Generate random mergeinfo, in which the paths and rangelists are not
2372  * necessarily valid. */
2373 static svn_error_t *
mergeinfo_random_non_validated(svn_mergeinfo_t * mp,apr_uint32_t * seed,apr_pool_t * pool)2374 mergeinfo_random_non_validated(svn_mergeinfo_t *mp,
2375                                apr_uint32_t *seed,
2376                                apr_pool_t *pool)
2377 {
2378   svn_mergeinfo_t m = apr_hash_make(pool);
2379   int n_paths = 3;  /* See comment "Random testing parameters and coverage" */
2380   int i;
2381 
2382   for (i = 0; i < n_paths; i++)
2383     {
2384       const char *path;
2385       svn_rangelist_t *rl;
2386 
2387       /* A manually chosen distribution of valid and invalid paths:
2388          See comment "Random testing parameters and coverage" */
2389       switch (rand_less_than(8, seed))
2390         {
2391         case 0: case 1: case 2: case 3:
2392           path = apr_psprintf(pool, "/path%d", i); break;
2393         case 4:
2394           path = apr_psprintf(pool, "path%d", i); break;
2395         case 5:
2396           path = apr_psprintf(pool, "//path%d", i); break;
2397         case 6:
2398           path = "/"; break;
2399         case 7:
2400           path = ""; break;
2401         }
2402       rangelist_random_non_validated(&rl, seed, pool);
2403       svn_hash_sets(m, path, rl);
2404     }
2405   *mp = m;
2406   return SVN_NO_ERROR;
2407 }
2408 
2409 #if 0
2410 static const char *
2411 mergeinfo_to_string_debug(svn_mergeinfo_t m,
2412                           apr_pool_t *pool)
2413 {
2414   svn_string_t *s;
2415   svn_error_t *err;
2416 
2417   err = svn_mergeinfo_to_string(&s, m, pool);
2418   if (err)
2419     {
2420       const char *s2 = err->message;
2421       svn_error_clear(err);
2422       return s2;
2423     }
2424   return s->data;
2425 }
2426 #endif
2427 
2428 /* Try a mergeinfo merge.  This does not check the result. */
2429 static svn_error_t *
mergeinfo_merge_random_inputs(const svn_mergeinfo_t mx,const svn_mergeinfo_t my,apr_pool_t * pool)2430 mergeinfo_merge_random_inputs(const svn_mergeinfo_t mx,
2431                               const svn_mergeinfo_t my,
2432                               apr_pool_t *pool)
2433 {
2434   svn_mergeinfo_t mm = svn_mergeinfo_dup(mx, pool);
2435 
2436   SVN_ERR(svn_mergeinfo_merge2(mm, my, pool, pool));
2437   return SVN_NO_ERROR;
2438 }
2439 
2440 /* Test svn_mergeinfo_merge2() with random non-validated inputs.
2441  *
2442  * Unlike the tests with valid inputs, this test expects many assertion
2443  * failures.  We don't care about those.  All we care about is that it does
2444  * not crash. */
2445 static svn_error_t *
test_mergeinfo_merge_random_non_validated_inputs(apr_pool_t * pool)2446 test_mergeinfo_merge_random_non_validated_inputs(apr_pool_t *pool)
2447 {
2448   static apr_uint32_t seed = 0;
2449   apr_pool_t *iterpool = svn_pool_create(pool);
2450   int ix, iy;
2451 
2452   for (ix = 0; ix < 300; ix++)
2453    {
2454     svn_mergeinfo_t mx;
2455 
2456     SVN_ERR(mergeinfo_random_non_validated(&mx, &seed, pool));
2457 
2458     for (iy = 0; iy < 300; iy++)
2459       {
2460         svn_mergeinfo_t my;
2461         svn_error_t *err;
2462 
2463         svn_pool_clear(iterpool);
2464 
2465         SVN_ERR(mergeinfo_random_non_validated(&my, &seed, iterpool));
2466 
2467         err = mergeinfo_merge_random_inputs(mx, my, iterpool);
2468         if (err)
2469           {
2470             /*
2471             printf("testcase FAIL: %s / %s\n",
2472                    mergeinfo_to_string_debug(mx, iterpool),
2473                    mergeinfo_to_string_debug(my, iterpool));
2474             svn_handle_error(err, stdout, FALSE);
2475             */
2476             svn_error_clear(err);
2477           }
2478       }
2479    }
2480 
2481   return SVN_NO_ERROR;
2482 }
2483 
2484 /* The test table.  */
2485 
2486 static int max_threads = 4;
2487 
2488 static struct svn_test_descriptor_t test_funcs[] =
2489   {
2490     SVN_TEST_NULL,
2491     SVN_TEST_PASS2(test_parse_single_line_mergeinfo,
2492                    "parse single line mergeinfo"),
2493     SVN_TEST_PASS2(test_mergeinfo_dup,
2494                    "copy a mergeinfo data structure"),
2495     SVN_TEST_PASS2(test_parse_combine_rangeinfo,
2496                    "parse single line mergeinfo and combine ranges"),
2497     SVN_TEST_PASS2(test_parse_broken_mergeinfo,
2498                    "parse broken single line mergeinfo"),
2499     SVN_TEST_PASS2(test_remove_rangelist,
2500                    "remove rangelists"),
2501     SVN_TEST_PASS2(test_rangelist_remove_randomly,
2502                    "test rangelist remove with random data"),
2503     SVN_TEST_PASS2(test_remove_mergeinfo,
2504                    "remove of mergeinfo"),
2505     SVN_TEST_PASS2(test_rangelist_reverse,
2506                    "reversal of rangelist"),
2507     SVN_TEST_PASS2(test_rangelist_intersect,
2508                    "intersection of rangelists"),
2509     SVN_TEST_PASS2(test_rangelist_intersect_randomly,
2510                    "test rangelist intersect with random data"),
2511     SVN_TEST_PASS2(test_diff_mergeinfo,
2512                    "diff of mergeinfo"),
2513     SVN_TEST_PASS2(test_merge_mergeinfo,
2514                    "merging of mergeinfo hashes"),
2515     SVN_TEST_PASS2(test_mergeinfo_intersect,
2516                    "intersection of mergeinfo"),
2517     SVN_TEST_PASS2(test_rangelist_to_string,
2518                    "turning rangelist back into a string"),
2519     SVN_TEST_PASS2(test_mergeinfo_to_string,
2520                    "turning mergeinfo back into a string"),
2521     SVN_TEST_PASS2(test_rangelist_merge,
2522                    "merge of rangelists"),
2523     SVN_TEST_PASS2(test_rangelist_diff,
2524                    "diff of rangelists"),
2525     SVN_TEST_PASS2(test_remove_prefix_from_catalog,
2526                    "removal of prefix paths from catalog keys"),
2527     SVN_TEST_PASS2(test_rangelist_merge_overlap,
2528                    "merge of rangelists with overlaps (issue 4686)"),
2529     SVN_TEST_PASS2(test_rangelist_loop,
2530                     "test rangelist edgecases via loop"),
2531     SVN_TEST_PASS2(test_rangelist_merge_canonical_result,
2532                    "test rangelist merge canonical result (#4840)"),
2533     SVN_TEST_PASS2(test_rangelist_merge_array_insert_failure,
2534                    "test rangelist merge array insert failure (#4840)"),
2535     SVN_TEST_PASS2(test_rangelist_merge_random_canonical_inputs,
2536                    "test rangelist merge random canonical inputs"),
2537     SVN_TEST_PASS2(test_rangelist_merge_random_semi_c_inputs,
2538                    "test rangelist merge random semi-c inputs"),
2539     SVN_TEST_PASS2(test_rangelist_merge_random_non_validated_inputs,
2540                    "test rangelist merge random non-validated inputs"),
2541     SVN_TEST_PASS2(test_mergeinfo_merge_random_non_validated_inputs,
2542                    "test mergeinfo merge random non-validated inputs"),
2543     SVN_TEST_NULL
2544   };
2545 
2546 SVN_TEST_MAIN
2547