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