In the command-line client, forbid
[svn.git] / subversion / tests / libsvn_subr / mergeinfo-test.c
bloba51d0a3ff008bec5175cde65e1885cd45765e155
1 /*
2 * mergeinfo-test.c -- test the mergeinfo functions
4 * ====================================================================
5 * Copyright (c) 2006-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 #include <stdio.h>
20 #include <string.h>
21 #include <apr_hash.h>
22 #include <apr_tables.h>
24 #include "svn_pools.h"
25 #include "svn_types.h"
26 #include "svn_mergeinfo.h"
27 #include "private/svn_mergeinfo_private.h"
28 #include "../svn_test.h"
30 /* A quick way to create error messages. */
31 static svn_error_t *
32 fail(apr_pool_t *pool, const char *fmt, ...)
34 va_list ap;
35 char *msg;
37 va_start(ap, fmt);
38 msg = apr_pvsprintf(pool, fmt, ap);
39 va_end(ap);
41 return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg);
44 #define MAX_NBR_RANGES 3
46 /* Verify that INPUT is parsed properly, and returns an error if
47 parsing fails, or incorret parsing is detected. Assumes that INPUT
48 contains only one path -> ranges mapping, and that EXPECTED_RANGES points
49 to the first range in an array whose size is greater than or equal to
50 the number of ranges in INPUTS path -> ranges mapping but less than
51 MAX_NBR_RANGES. If fewer than MAX_NBR_RANGES ranges are present, then the
52 trailing expected_ranges should be have their end revision set to 0. */
53 static svn_error_t *
54 verify_mergeinfo_parse(const char *input,
55 const char *expected_path,
56 const svn_merge_range_t *expected_ranges,
57 apr_pool_t *pool)
59 svn_error_t *err;
60 apr_hash_t *path_to_merge_ranges;
61 apr_hash_index_t *hi;
63 /* Test valid input. */
64 err = svn_mergeinfo_parse(&path_to_merge_ranges, input, pool);
65 if (err || apr_hash_count(path_to_merge_ranges) != 1)
66 return svn_error_createf(SVN_ERR_TEST_FAILED, err,
67 "svn_mergeinfo_parse (%s) failed unexpectedly",
68 input);
69 for (hi = apr_hash_first(pool, path_to_merge_ranges); hi;
70 hi = apr_hash_next(hi))
72 const void *path;
73 void *val;
74 apr_array_header_t *ranges;
75 svn_merge_range_t *range;
76 int j;
78 apr_hash_this(hi, &path, NULL, &val);
79 ranges = val;
80 if (strcmp((const char *) path, expected_path) != 0)
81 return fail(pool, "svn_mergeinfo_parse (%s) failed to parse the "
82 "correct path (%s)", input, expected_path);
84 /* Test each parsed range. */
85 for (j = 0; j < ranges->nelts; j++)
87 range = APR_ARRAY_IDX(ranges, j, svn_merge_range_t *);
88 if (range->start != expected_ranges[j].start
89 || range->end != expected_ranges[j].end
90 || range->inheritable != expected_ranges[j].inheritable)
91 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
92 "svn_mergeinfo_parse (%s) failed to "
93 "parse the correct range",
94 input);
97 /* Were we expecting any more ranges? */
98 if (j < MAX_NBR_RANGES - 1
99 && !expected_ranges[j].end == 0)
100 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
101 "svn_mergeinfo_parse (%s) failed to "
102 "produce the expected number of ranges",
103 input);
105 return SVN_NO_ERROR;
109 /* Some of our own global variables (for simplicity), which map paths
110 -> merge ranges. */
111 static apr_hash_t *info1, *info2;
113 #define NBR_MERGEINFO_VALS 5
114 /* Valid mergeinfo values. */
115 static const char * const mergeinfo_vals[NBR_MERGEINFO_VALS] =
117 "/trunk:1",
118 "/trunk/foo:1-6",
119 "/trunk: 5,7-9,10,11,13,14",
120 "/trunk: 3-10,11*,13,14",
121 "/branch: 1,2-18*,33*"
123 /* Paths corresponding to mergeinfo_vals. */
124 static const char * const mergeinfo_paths[NBR_MERGEINFO_VALS] =
126 "/trunk",
127 "/trunk/foo",
128 "/trunk",
129 "/trunk",
130 "/branch"
132 /* First ranges from the paths identified by mergeinfo_paths. */
133 static svn_merge_range_t mergeinfo_ranges[NBR_MERGEINFO_VALS][MAX_NBR_RANGES] =
135 { {0, 1, TRUE} },
136 { {0, 6, TRUE} },
137 { {4, 5, TRUE}, { 6, 11, TRUE }, {12, 14, TRUE } },
138 { {2, 10, TRUE}, {10, 11, FALSE}, {12, 14, TRUE } },
139 { {0, 1, TRUE}, { 1, 18, FALSE}, {32, 33, FALSE} }
142 static svn_error_t *
143 test_parse_single_line_mergeinfo(const char **msg,
144 svn_boolean_t msg_only,
145 svn_test_opts_t *opts,
146 apr_pool_t *pool)
148 int i;
150 *msg = "parse single line mergeinfo";
152 if (msg_only)
153 return SVN_NO_ERROR;
155 for (i = 0; i < NBR_MERGEINFO_VALS; i++)
156 SVN_ERR(verify_mergeinfo_parse(mergeinfo_vals[i], mergeinfo_paths[i],
157 mergeinfo_ranges[i], pool));
159 return SVN_NO_ERROR;
162 static const char *single_mergeinfo = "/trunk: 5,7-9,10,11,13,14";
164 static svn_error_t *
165 test_mergeinfo_dup(const char **msg,
166 svn_boolean_t msg_only,
167 svn_test_opts_t *opts,
168 apr_pool_t *pool)
170 apr_hash_t *orig_mergeinfo, *copied_mergeinfo;
171 apr_pool_t *subpool;
172 apr_array_header_t *rangelist;
174 *msg = "copy a mergeinfo data structure";
176 if (msg_only)
177 return SVN_NO_ERROR;
179 /* Assure that copies which should be empty turn out that way. */
180 subpool = svn_pool_create(pool);
181 orig_mergeinfo = apr_hash_make(subpool);
182 copied_mergeinfo = svn_mergeinfo_dup(orig_mergeinfo, subpool);
183 if (apr_hash_count(copied_mergeinfo) != 0)
184 return fail(pool, "Copied mergeinfo should be empty");
186 /* Create some mergeinfo, copy it using another pool, then destroy
187 the pool with which the original mergeinfo was created. */
188 SVN_ERR(svn_mergeinfo_parse(&orig_mergeinfo, single_mergeinfo, subpool));
189 copied_mergeinfo = svn_mergeinfo_dup(orig_mergeinfo, pool);
190 svn_pool_destroy(subpool);
191 if (apr_hash_count(copied_mergeinfo) != 1)
192 return fail(pool, "Copied mergeinfo should contain one merge source");
193 rangelist = apr_hash_get(copied_mergeinfo, "/trunk", APR_HASH_KEY_STRING);
194 if (! rangelist)
195 return fail(pool, "Expected copied mergeinfo; got nothing");
196 if (rangelist->nelts != 3)
197 return fail(pool, "Copied mergeinfo should contain 3 revision ranges, "
198 "rather than the %d it contains", rangelist->nelts);
200 return SVN_NO_ERROR;
203 static svn_error_t *
204 test_parse_combine_rangeinfo(const char **msg,
205 svn_boolean_t msg_only,
206 svn_test_opts_t *opts,
207 apr_pool_t *pool)
209 apr_array_header_t *result;
210 svn_merge_range_t *resultrange;
212 *msg = "parse single line mergeinfo and combine ranges";
214 if (msg_only)
215 return SVN_NO_ERROR;
217 SVN_ERR(svn_mergeinfo_parse(&info1, single_mergeinfo, pool));
219 if (apr_hash_count(info1) != 1)
220 return fail(pool, "Wrong number of paths in parsed mergeinfo");
222 result = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
223 if (!result)
224 return fail(pool, "Missing path in parsed mergeinfo");
226 /* /trunk should have three ranges, 5-5, 7-11, 13-14 */
227 if (result->nelts != 3)
228 return fail(pool, "Parsing failed to combine ranges");
230 resultrange = APR_ARRAY_IDX(result, 0, svn_merge_range_t *);
232 if (resultrange->start != 4 || resultrange->end != 5)
233 return fail(pool, "Range combining produced wrong result");
235 resultrange = APR_ARRAY_IDX(result, 1, svn_merge_range_t *);
237 if (resultrange->start != 6 || resultrange->end != 11)
238 return fail(pool, "Range combining produced wrong result");
240 resultrange = APR_ARRAY_IDX(result, 2, svn_merge_range_t *);
242 if (resultrange->start != 12 || resultrange->end != 14)
243 return fail(pool, "Range combining produced wrong result");
245 return SVN_NO_ERROR;
249 #define NBR_BROKEN_MERGEINFO_VALS 34
250 /* Invalid mergeinfo values. */
251 static const char * const broken_mergeinfo_vals[NBR_BROKEN_MERGEINFO_VALS] =
253 /* Invalid grammar */
254 "/missing-revs",
255 "/trunk: 5,7-9,10,11,13,14,",
256 "/trunk 5,7-9,10,11,13,14",
257 "/trunk:5 7--9 10 11 13 14",
258 /* Unordered revs */
259 "/trunk:3-6,15,18,9,22",
260 "/trunk:5,3",
261 "/trunk:3-6*,15*,18*,9,22*",
262 "/trunk:5,3*",
263 /* Overlapping revs differing inheritability */
264 "/trunk:5-9*,9",
265 "/trunk:5,5-9*",
266 "/trunk:5-9,9*",
267 "/trunk:5*,5-9",
268 "/trunk:4,4*",
269 "/trunk:4*,4",
270 "/trunk:3-7*,4-23",
271 "/trunk:3-7,4-23*",
272 /* Overlapping revs same inheritability */
273 "/trunk:5-9*,9*",
274 "/trunk:5*,5-9*",
275 "/trunk:5-9,9",
276 "/trunk:5,5-9",
277 "/trunk:4,4",
278 "/trunk:4*,4*",
279 "/trunk:3-7,4-23",
280 "/trunk:3-7*,4-23*",
281 /* Reversed revision ranges */
282 "/trunk:22-20",
283 "/trunk:22-20*",
284 "/trunk:3,7-12,22-20,25",
285 "/trunk:3,7,22-20*,25-30",
286 /* Range with same start and end revision */
287 "/trunk:22-22",
288 "/trunk:22-22*",
289 "/trunk:3,7-12,20-20,25",
290 "/trunk:3,7,20-20*,25-30",
291 /* path mapped to range with no revisions */
292 "/trunk:",
293 "/trunk:2-9\n/branch:"
296 static svn_error_t *
297 test_parse_broken_mergeinfo(const char **msg,
298 svn_boolean_t msg_only,
299 svn_test_opts_t *opts,
300 apr_pool_t *pool)
302 int i;
303 svn_error_t *err;
304 *msg = "parse broken single line mergeinfo";
306 if (msg_only)
307 return SVN_NO_ERROR;
309 /* Trigger some error(s) with mal-formed input. */
310 for (i = 0; i < NBR_BROKEN_MERGEINFO_VALS; i++)
312 err = svn_mergeinfo_parse(&info1, broken_mergeinfo_vals[i], pool);
313 if (err == SVN_NO_ERROR)
314 return fail(pool, "svn_mergeinfo_parse (%s) failed to detect an error",
315 broken_mergeinfo_vals[i]);
316 else
317 svn_error_clear(err);
320 return SVN_NO_ERROR;
324 static const char *mergeinfo1 = "/trunk: 3,5,7-9,10,11,13,14\n/fred:8-10";
326 #define NBR_RANGELIST_DELTAS 4
328 /* Verify that ACTUAL_RANGELIST matches EXPECTED_RANGES (an array of
329 NBR_EXPECTED length). Return an error based careful examination if
330 they do not match. FUNC_VERIFIED is the name of the API being
331 verified (e.g. "svn_rangelist_intersect"), while TYPE is a word
332 describing what the ranges being examined represent. */
333 static svn_error_t *
334 verify_ranges_match(apr_array_header_t *actual_rangelist,
335 svn_merge_range_t *expected_ranges, int nbr_expected,
336 const char *func_verified, const char *type,
337 apr_pool_t *pool)
339 int i;
341 if (actual_rangelist->nelts != nbr_expected)
342 return fail(pool, "%s should report %d range %ss, but found %d",
343 func_verified, nbr_expected, type, actual_rangelist->nelts);
345 for (i = 0; i < actual_rangelist->nelts; i++)
347 svn_merge_range_t *range = APR_ARRAY_IDX(actual_rangelist, i,
348 svn_merge_range_t *);
349 if (range->start != expected_ranges[i].start
350 || range->end != expected_ranges[i].end
351 || range->inheritable != expected_ranges[i].inheritable)
352 return fail(pool, "%s should report range %ld-%ld%s, "
353 "but found %ld-%ld%s",
354 func_verified, expected_ranges[i].start,
355 expected_ranges[i].end,
356 expected_ranges[i].inheritable ? "*" : "",
357 range->start, range->end,
358 range->inheritable ? "*" : "");
360 return SVN_NO_ERROR;
363 /* Verify that DELTAS matches EXPECTED_DELTAS (both expected to
364 contain only a rangelist for "/trunk"). Return an error based
365 careful examination if they do not match. FUNC_VERIFIED is the
366 name of the API being verified (e.g. "svn_mergeinfo_diff"), while
367 TYPE is a word describing what the deltas being examined
368 represent. */
369 static svn_error_t *
370 verify_mergeinfo_deltas(apr_hash_t *deltas, svn_merge_range_t *expected_deltas,
371 const char *func_verified, const char *type,
372 apr_pool_t *pool)
374 apr_array_header_t *rangelist;
376 if (apr_hash_count(deltas) != 1)
377 /* Deltas on "/trunk" expected. */
378 return fail(pool, "%s should report 1 path %s, but found %d",
379 func_verified, type, apr_hash_count(deltas));
381 rangelist = apr_hash_get(deltas, "/trunk", APR_HASH_KEY_STRING);
382 if (rangelist == NULL)
383 return fail(pool, "%s failed to produce a rangelist for /trunk",
384 func_verified);
386 return verify_ranges_match(rangelist, expected_deltas, NBR_RANGELIST_DELTAS,
387 func_verified, type, pool);
390 static svn_error_t *
391 test_diff_mergeinfo(const char **msg,
392 svn_boolean_t msg_only,
393 svn_test_opts_t *opts,
394 apr_pool_t *pool)
396 apr_hash_t *deleted, *added, *from, *to;
397 svn_merge_range_t expected_rangelist_deletions[NBR_RANGELIST_DELTAS] =
398 { {6, 7, TRUE}, {8, 9, TRUE}, {10, 11, TRUE}, {32, 34, TRUE} };
399 svn_merge_range_t expected_rangelist_additions[NBR_RANGELIST_DELTAS] =
400 { {1, 2, TRUE}, {4, 6, TRUE}, {12, 16, TRUE}, {29, 30, TRUE} };
402 *msg = "diff of mergeinfo";
403 if (msg_only)
404 return SVN_NO_ERROR;
406 SVN_ERR(svn_mergeinfo_parse(&from, "/trunk: 1,3-4,7,9,11-12,31-34", pool));
407 SVN_ERR(svn_mergeinfo_parse(&to, "/trunk: 1-6,12-16,30-32", pool));
408 /* On /trunk: deleted (7, 9, 11, 33-34) and added (2, 5-6, 13-16, 30) */
409 SVN_ERR(svn_mergeinfo_diff(&deleted, &added, from, to,
410 FALSE, pool));
412 /* Verify calculation of range list deltas. */
413 SVN_ERR(verify_mergeinfo_deltas(deleted, expected_rangelist_deletions,
414 "svn_mergeinfo_diff", "deletion", pool));
415 SVN_ERR(verify_mergeinfo_deltas(added, expected_rangelist_additions,
416 "svn_mergeinfo_diff", "addition", pool));
418 return SVN_NO_ERROR;
421 static svn_error_t *
422 test_rangelist_reverse(const char **msg,
423 svn_boolean_t msg_only,
424 svn_test_opts_t *opts,
425 apr_pool_t *pool)
427 apr_array_header_t *rangelist;
428 svn_merge_range_t expected_rangelist[3] =
429 { {10, 9, TRUE}, {7, 4, TRUE}, {3, 2, TRUE} };
431 *msg = "reversal of rangelist";
432 if (msg_only)
433 return SVN_NO_ERROR;
435 SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 3,5-7,10", pool));
436 rangelist = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
438 SVN_ERR(svn_rangelist_reverse(rangelist, pool));
440 return verify_ranges_match(rangelist, expected_rangelist, 3,
441 "svn_rangelist_reverse", "reversal", pool);
444 static svn_error_t *
445 test_rangelist_count_revs(const char **msg,
446 svn_boolean_t msg_only,
447 svn_test_opts_t *opts,
448 apr_pool_t *pool)
450 apr_array_header_t *rangelist;
451 apr_uint64_t nbr_revs;
453 *msg = "counting revs in rangelist";
454 if (msg_only)
455 return SVN_NO_ERROR;
457 SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 3,5-7,10", pool));
458 rangelist = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
460 nbr_revs = svn_rangelist_count_revs(rangelist);
462 if (nbr_revs != 5)
463 return fail(pool, "expecting 5 revs in count, found %d", nbr_revs);
465 return SVN_NO_ERROR;
468 static svn_error_t *
469 test_rangelist_to_revs(const char **msg,
470 svn_boolean_t msg_only,
471 svn_test_opts_t *opts,
472 apr_pool_t *pool)
474 apr_array_header_t *revs, *rangelist;
475 svn_revnum_t expected_revs[] = {3, 5, 6, 7, 10};
476 int i;
478 *msg = "returning revs in rangelist";
479 if (msg_only)
480 return SVN_NO_ERROR;
482 SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 3,5-7,10", pool));
483 rangelist = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
485 SVN_ERR(svn_rangelist_to_revs(&revs, rangelist, pool));
487 for (i = 0; i < revs->nelts; i++)
489 svn_revnum_t rev = APR_ARRAY_IDX(revs, i, svn_revnum_t);
491 if (rev != expected_revs[i])
492 return fail(pool, "rev mis-match at position %d: expecting %d, "
493 "found %d", i, expected_revs[i], rev);
496 return SVN_NO_ERROR;
499 static svn_error_t *
500 test_rangelist_intersect(const char **msg,
501 svn_boolean_t msg_only,
502 svn_test_opts_t *opts,
503 apr_pool_t *pool)
505 apr_array_header_t *rangelist1, *rangelist2, *intersection;
506 svn_merge_range_t expected_intersection[4] =
507 { {0, 1, TRUE}, {2, 4, TRUE}, {11, 12, TRUE}, {30, 32, TRUE} };
509 *msg = "intersection of rangelists";
510 if (msg_only)
511 return SVN_NO_ERROR;
513 SVN_ERR(svn_mergeinfo_parse(&info1, "/trunk: 1-6,12-16,30-32", pool));
514 SVN_ERR(svn_mergeinfo_parse(&info2, "/trunk: 1,3-4,7,9,11-12,31-34", pool));
515 rangelist1 = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
516 rangelist2 = apr_hash_get(info2, "/trunk", APR_HASH_KEY_STRING);
518 SVN_ERR(svn_rangelist_intersect(&intersection, rangelist1, rangelist2,
519 pool));
521 return verify_ranges_match(intersection, expected_intersection, 4,
522 "svn_rangelist_intersect", "intersect", pool);
525 static svn_error_t *
526 test_merge_mergeinfo(const char **msg,
527 svn_boolean_t msg_only,
528 svn_test_opts_t *opts,
529 apr_pool_t *pool)
531 int i;
532 svn_stringbuf_t *output;
534 /* Structures and constants for test_merge_mergeinfo() */
535 /* Number of svn_mergeinfo_merge test sets */
536 #define NBR_MERGEINFO_MERGES 12
538 /* Maximum number of expected paths in the results
539 of the svn_mergeinfo_merge tests */
540 #define MAX_NBR_MERGEINFO_PATHS 4
542 /* Maximum number of expected ranges in the results
543 of the svn_mergeinfo_merge tests */
544 #define MAX_NBR_MERGEINFO_RANGES 10
546 /* Struct to store a path and it's expected ranges,
547 i.e. the expected result of an svn_mergeinfo_merge
548 test. */
549 struct mergeinfo_merge_path_range
551 const char *path;
552 svn_merge_range_t expected_rngs[MAX_NBR_MERGEINFO_RANGES];
555 /* Struct for svn_mergeinfo_merge test data.
556 If MERGEINFO1 and MERGEINFO2 are parsed to a hash with
557 svn_mergeinfo_parse() and then merged with svn_mergeinfo_merge(),
558 the resulting hash should have EXPECTED_PATHS number of paths
559 mapped to rangelists and each mapping is described by PATH_RNGS
560 where PATH_RNGS->PATH is not NULL. */
561 struct mergeinfo_merge_test_data
563 const char *mergeinfo1;
564 const char *mergeinfo2;
565 int expected_paths;
566 struct mergeinfo_merge_path_range path_rngs[MAX_NBR_MERGEINFO_PATHS];
569 static struct mergeinfo_merge_test_data mergeinfo[NBR_MERGEINFO_MERGES] =
571 /* One path, intersecting inheritable ranges */
572 { "/trunk: 5-10",
573 "/trunk: 6", 1,
574 { {"/trunk", { {4, 10, TRUE} } } } },
576 /* One path, intersecting non-inheritable ranges */
577 { "/trunk: 5-10*",
578 "/trunk: 6*", 1,
579 { {"/trunk", { {4, 10, FALSE} } } } },
581 /* One path, intersecting ranges with different inheritability */
582 { "/trunk: 5-10",
583 "/trunk: 6*", 1,
584 { {"/trunk", { {4, 10, TRUE} } } } },
586 /* One path, intersecting ranges with different inheritability */
587 { "/trunk: 5-10*",
588 "/trunk: 6", 1,
589 { {"/trunk", { {4, 5, FALSE}, {5, 6, TRUE}, {6, 10, FALSE} } } } },
591 /* Adjacent ranges all inheritable ranges */
592 { "/trunk: 1,3,5-11,13",
593 "/trunk: 2,4,12,14-22", 1,
594 { {"/trunk", { {0, 22, TRUE} } } } },
596 /* Adjacent ranges all non-inheritable ranges */
597 { "/trunk: 1*,3*,5-11*,13*",
598 "/trunk: 2*,4*,12*,14-22*", 1,
599 { {"/trunk", { {0, 22, FALSE} } } } },
601 /* Adjacent ranges differing inheritability */
602 { "/trunk: 1*,3*,5-11*,13*",
603 "/trunk: 2,4,12,14-22", 1,
604 { {"/trunk", { { 0, 1, FALSE}, { 1, 2, TRUE},
605 { 2, 3, FALSE}, { 3, 4, TRUE},
606 { 4, 11, FALSE}, {11, 12, TRUE},
607 {12, 13, FALSE}, {13, 22, TRUE} } } } },
609 /* Adjacent ranges differing inheritability */
610 { "/trunk: 1,3,5-11,13",
611 "/trunk: 2*,4*,12*,14-22*", 1,
612 { {"/trunk", { { 0, 1, TRUE}, { 1, 2, FALSE},
613 { 2, 3, TRUE}, { 3, 4, FALSE},
614 { 4, 11, TRUE}, {11, 12, FALSE},
615 {12, 13, TRUE}, {13, 22, FALSE} } } } },
617 /* Two paths all inheritable ranges */
618 { "/trunk: 3,5,7-9,10,11,13,14\n/fred:8-10",
619 "/trunk: 1-4,6\n/fred:9-12", 2,
620 { {"/trunk", { {0, 11, TRUE}, {12, 14, TRUE} } },
621 {"/fred", { {7, 12, TRUE} } } } },
623 /* Two paths all non-inheritable ranges */
624 { "/trunk: 3*,5*,7-9*,10*,11*,13*,14*\n/fred:8-10*",
625 "/trunk: 1-4*,6*\n/fred:9-12*", 2,
626 { {"/trunk", { {0, 11, FALSE}, {12, 14, FALSE} } },
627 {"/fred", { {7, 12, FALSE} } } } },
629 /* Two paths mixed inheritability */
630 { "/trunk: 3,5*,7-9,10,11*,13,14\n/fred:8-10",
631 "/trunk: 1-4,6\n/fred:9-12*", 2,
632 { {"/trunk", { { 0, 4, TRUE }, { 4, 5, FALSE}, {5, 10, TRUE},
633 {10, 11, FALSE}, {12, 14, TRUE } } },
634 {"/fred", { { 7, 10, TRUE }, {10, 12, FALSE} } } } },
636 /* A slew of different paths but no ranges to be merged */
637 { "/trunk: 3,5-9*\n/betty: 2-4",
638 "/fred: 1-18\n/barney: 1,3-43", 4,
639 { {"/trunk", { {2, 3, TRUE}, {4, 9, FALSE} } },
640 {"/betty", { {1, 4, TRUE} } },
641 {"/barney", { {0, 1, TRUE}, {2, 43, TRUE} } },
642 {"/fred", { {0, 18, TRUE} } } } }
645 *msg = "merging of mergeinfo hashs";
646 if (msg_only)
647 return SVN_NO_ERROR;
649 for (i = 0; i < NBR_MERGEINFO_MERGES; i++)
651 int j;
652 SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo[i].mergeinfo1, pool));
653 SVN_ERR(svn_mergeinfo_parse(&info2, mergeinfo[i].mergeinfo2, pool));
654 SVN_ERR(svn_mergeinfo_merge(info1, info2, pool));
655 SVN_ERR(svn_mergeinfo_to_stringbuf(&output, info1, pool));
656 if (mergeinfo[i].expected_paths != apr_hash_count(info1))
657 return fail(pool, "Wrong number of paths in merged mergeinfo");
658 for (j = 0; j < mergeinfo[i].expected_paths; j++)
660 int k;
661 apr_array_header_t *rangelist =
662 apr_hash_get(info1, mergeinfo[i].path_rngs[j].path,
663 APR_HASH_KEY_STRING);
664 if (!rangelist)
665 return fail(pool, "Missing path '%s' in merged mergeinfo",
666 mergeinfo[i].path_rngs->path);
667 for (k = 0; k < rangelist->nelts; k++)
669 svn_merge_range_t *ranges =
670 APR_ARRAY_IDX(rangelist, k, svn_merge_range_t *);
671 if (ranges->start
672 != mergeinfo[i].path_rngs[j].expected_rngs[k].start
673 || ranges->end
674 != mergeinfo[i].path_rngs[j].expected_rngs[k].end
675 || ranges->inheritable
676 != mergeinfo[i].path_rngs[j].expected_rngs[k].inheritable)
677 return fail(
678 pool,
679 "Range'%i-%i%s' not found in merged mergeinfo",
680 mergeinfo[i].path_rngs->expected_rngs[k].start,
681 mergeinfo[i].path_rngs->expected_rngs[k].end,
682 mergeinfo[i].path_rngs->expected_rngs[k].inheritable
683 ? "" : "*");
685 /* Were more ranges expected? */
686 if (k < MAX_NBR_MERGEINFO_RANGES
687 && mergeinfo[i].path_rngs[j].expected_rngs[k].start != 0)
688 return fail(pool,
689 "Not all expected ranges found in merged mergeinfo");
693 return SVN_NO_ERROR;
696 static svn_error_t *
697 test_remove_rangelist(const char **msg,
698 svn_boolean_t msg_only,
699 svn_test_opts_t *opts,
700 apr_pool_t *pool)
702 int i, j;
703 svn_error_t *err, *child_err;
704 apr_array_header_t *output, *eraser, *whiteboard;
706 /* Struct for svn_rangelist_remove test data.
707 Parse WHITEBOARD and ERASER to hashes and then get the rangelist for
708 path 'A' from both.
710 Remove ERASER's rangelist from WHITEBOARD's twice, once while
711 considering inheritance and once while not. In the first case the
712 resulting rangelist should have EXPECTED_RANGES_CONSIDER_INHERITANCE
713 number of ranges and these ranges should match the ranges in
714 EXPECTED_REMOVED_CONSIDER_INHERITANCE. In the second case there
715 should be EXPECTED_RANGES_IGNORE_INHERITANCE number of ranges and
716 these should match EXPECTED_REMOVED_IGNORE_INHERITANCE */
717 struct rangelist_remove_test_data
719 const char *whiteboard;
720 const char *eraser;
721 int expected_ranges_consider_inheritance;
722 svn_merge_range_t expected_removed_consider_inheritance[10];
723 int expected_ranges_ignore_inheritance;
724 svn_merge_range_t expected_removed_ignore_inheritance[10];
727 #define SIZE_OF_RANGE_REMOVE_TEST_ARRAY 15
729 /* The actual test data */
730 struct rangelist_remove_test_data test_data[SIZE_OF_RANGE_REMOVE_TEST_ARRAY] =
732 /* Eraser is a proper subset of whiteboard */
733 {"/A: 1-44", "/A: 5", 2, { {0, 4, TRUE }, {5, 44, TRUE }},
734 2, { {0, 4, TRUE }, {5, 44, TRUE }}},
735 {"/A: 1-44*", "/A: 5", 1, { {0, 44, FALSE} },
736 2, { {0, 4, FALSE}, {5, 44, FALSE}}},
737 {"/A: 1-44", "/A: 5*", 1, { {0, 44, TRUE } },
738 2, { {0, 4, TRUE }, {5, 44, TRUE }}},
739 {"/A: 1-44*", "/A: 5*", 2, { {0, 4, FALSE}, {5, 44, FALSE}},
740 2, { {0, 4, FALSE}, {5, 44, FALSE}}},
741 /* Non-intersecting ranges...nothing is removed */
742 {"/A: 2-9,14-19", "/A: 12", 2, { {1, 9, TRUE }, {13, 19, TRUE }},
743 2, { {1, 9, TRUE }, {13, 19, TRUE }}},
744 {"/A: 2-9*,14-19*", "/A: 12", 2, { {1, 9, FALSE}, {13, 19, FALSE}},
745 2, { {1, 9, FALSE}, {13, 19, FALSE}}},
746 {"/A: 2-9,14-19", "/A: 12*", 2, { {1, 9, TRUE }, {13, 19, TRUE }},
747 2, { {1, 9, TRUE }, {13, 19, TRUE }}},
748 {"/A: 2-9*,14-19*", "/A: 12*", 2, { {1, 9, FALSE}, {13, 19, FALSE}},
749 2, { {1, 9, FALSE}, {13, 19, FALSE}}},
750 /* Eraser overlaps whiteboard */
751 {"/A: 1,9-17", "/A: 12-20", 2, { {0, 1, TRUE }, {8, 11, TRUE }},
752 2, { {0, 1, TRUE }, {8, 11, TRUE }}},
753 {"/A: 1,9-17*", "/A: 12-20", 2, { {0, 1, TRUE }, {8, 17, FALSE}},
754 2, { {0, 1, TRUE }, {8, 11, FALSE}}},
755 {"/A: 1,9-17", "/A: 12-20*", 2, { {0, 1, TRUE }, {8, 17, TRUE }},
756 2, { {0, 1, TRUE }, {8, 11, TRUE }}},
757 {"/A: 1,9-17*", "/A: 12-20*", 2, { {0, 1, TRUE }, {8, 11, FALSE}},
758 2, { {0, 1, TRUE }, {8, 11, FALSE}}},
759 /* Empty mergeinfo (i.e. empty rangelist) */
760 {"", "", 0, { {0, 0, FALSE}},
761 0, { {0, 0, FALSE}}},
762 {"", "/A: 5-8,10-100", 0, { {0, 0, FALSE}},
763 0, { {0, 0, FALSE}}},
764 {"/A: 5-8,10-100", "", 2, { {4, 8, TRUE }, {9, 100, TRUE }},
765 2, { {4, 8, TRUE }, {9, 100, TRUE }}}
768 *msg = "remove rangelists";
769 err = child_err = SVN_NO_ERROR;
770 for (j = 0; j < 2; j++)
772 for (i = 0; i < SIZE_OF_RANGE_REMOVE_TEST_ARRAY; i++)
774 int expected_nbr_ranges;
775 svn_merge_range_t *expected_ranges;
777 SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).eraser, pool));
778 SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).whiteboard, pool));
779 eraser = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING);
780 whiteboard = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING);
782 /* Represent empty mergeinfo with an empty rangelist. */
783 if (eraser == NULL)
784 eraser = apr_array_make(pool, 0, sizeof(*eraser));
785 if (whiteboard == NULL)
786 whiteboard = apr_array_make(pool, 0, sizeof(*whiteboard));
788 /* First pass try removal considering inheritance, on the
789 second pass ignore it. */
790 if (j == 0)
792 expected_nbr_ranges = (test_data[i]).expected_ranges_consider_inheritance;
793 expected_ranges = (test_data[i]).expected_removed_consider_inheritance;
796 else
798 expected_nbr_ranges = (test_data[i]).expected_ranges_ignore_inheritance;
799 expected_ranges = (test_data[i]).expected_removed_ignore_inheritance;
802 SVN_ERR(svn_rangelist_remove(&output, eraser, whiteboard,
803 j == 0 ? TRUE : FALSE,
804 pool));
805 child_err = verify_ranges_match(output, expected_ranges,
806 expected_nbr_ranges,
807 apr_psprintf(pool,
808 "svn_rangelist_remove "
809 "case %i", i),
810 "remove", pool);
812 /* Collect all the errors rather than returning on the first. */
813 if (child_err)
815 if (err)
816 svn_error_compose(err, child_err);
817 else
818 err = child_err;
822 return err;
825 /* ### Share code with test_diff_mergeinfo() and test_remove_rangelist(). */
826 static svn_error_t *
827 test_remove_mergeinfo(const char **msg,
828 svn_boolean_t msg_only,
829 svn_test_opts_t *opts,
830 apr_pool_t *pool)
832 apr_hash_t *output, *whiteboard, *eraser;
833 svn_merge_range_t expected_rangelist_remainder[NBR_RANGELIST_DELTAS] =
834 { {6, 7, TRUE}, {8, 9, TRUE}, {10, 11, TRUE}, {32, 34, TRUE} };
836 *msg = "remove of mergeinfo";
837 if (msg_only)
838 return SVN_NO_ERROR;
840 SVN_ERR(svn_mergeinfo_parse(&whiteboard,
841 "/trunk: 1,3-4,7,9,11-12,31-34", pool));
842 SVN_ERR(svn_mergeinfo_parse(&eraser, "/trunk: 1-6,12-16,30-32", pool));
844 /* Leftover on /trunk should be the set (7, 9, 11, 33-34) */
845 SVN_ERR(svn_mergeinfo_remove(&output, eraser, whiteboard, pool));
847 /* Verify calculation of range list remainder. */
848 return verify_mergeinfo_deltas(output, expected_rangelist_remainder,
849 "svn_mergeinfo_remove", "leftover", pool);
851 #undef NBR_RANGELIST_DELTAS
853 static svn_error_t *
854 test_rangelist_to_string(const char **msg,
855 svn_boolean_t msg_only,
856 svn_test_opts_t *opts,
857 apr_pool_t *pool)
859 apr_array_header_t *result;
860 svn_stringbuf_t *output;
861 svn_stringbuf_t *expected = svn_stringbuf_create("3,5,7-11,13-14", pool);
863 *msg = "turning rangelist back into a string";
865 if (msg_only)
866 return SVN_NO_ERROR;
868 SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo1, pool));
870 result = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
871 if (!result)
872 return fail(pool, "Missing path in parsed mergeinfo");
874 SVN_ERR(svn_rangelist_to_stringbuf(&output, result, pool));
876 if (svn_stringbuf_compare(expected, output) != TRUE)
877 return fail(pool, "Rangelist string not what we expected");
879 return SVN_NO_ERROR;
882 static svn_error_t *
883 test_mergeinfo_to_string(const char **msg,
884 svn_boolean_t msg_only,
885 svn_test_opts_t *opts,
886 apr_pool_t *pool)
888 svn_string_t *output;
889 svn_string_t *expected;
890 expected = svn_string_create("/fred:8-10\n/trunk:3,5,7-11,13-14", pool);
892 *msg = "turning mergeinfo back into a string";
894 if (msg_only)
895 return SVN_NO_ERROR;
897 SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo1, pool));
899 SVN_ERR(svn_mergeinfo__to_string(&output, info1, pool));
901 if (svn_string_compare(expected, output) != TRUE)
902 return fail(pool, "Mergeinfo string not what we expected");
904 return SVN_NO_ERROR;
907 static svn_error_t *
908 test_range_compact(const char **msg,
909 svn_boolean_t msg_only,
910 svn_test_opts_t *opts,
911 apr_pool_t *pool)
913 #define SIZE_OF_TEST_ARRAY 44
914 svn_merge_range_t rangelist[SIZE_OF_TEST_ARRAY][4] =
915 /* For each ith element of rangelist[][], try to combine/compact
916 rangelist[i][0] and rangelist[i][1]. If the combined ranges can
917 be combined, then the expected range is rangelist[i][2] and
918 rangelist[i][3] is {-1, -1, TRUE}. If the ranges cancel each
919 other out, then both rangelist[i][2] and rangelist[i][3] are
920 {-1, -1, TRUE}.
921 range1 + range2 = range3 , range4 */
922 { /* Non-intersecting ranges */
923 { { 2, 4, TRUE}, { 6, 13, TRUE}, { 2, 4, TRUE}, { 6, 13, TRUE} },
924 { { 4, 2, TRUE}, { 6, 13, TRUE}, { 4, 2, TRUE}, { 6, 13, TRUE} },
925 { { 4, 2, TRUE}, {13, 6, TRUE}, { 4, 2, TRUE}, {13, 6, TRUE} },
926 { { 2, 4, TRUE}, {13, 6, TRUE}, { 2, 4, TRUE}, {13, 6, TRUE} },
927 { { 6, 13, TRUE}, { 2, 4, TRUE}, { 6, 13, TRUE}, { 2, 4, TRUE} },
928 { { 6, 13, TRUE}, { 4, 2, TRUE}, { 6, 13, TRUE}, { 4, 2, TRUE} },
929 { {13, 6, TRUE}, { 4, 2, TRUE}, {13, 6, TRUE}, { 4, 2, TRUE} },
930 { {13, 6, TRUE}, { 2, 4, TRUE}, {13, 6, TRUE}, { 2, 4, TRUE} },
931 /* Intersecting ranges with no common start or end points */
932 { { 2, 5, TRUE}, { 4, 6, TRUE}, { 2, 6, TRUE}, {-1, -1, TRUE} },
933 { { 2, 5, TRUE}, { 6, 4, TRUE}, { 2, 4, TRUE}, { 6, 5, TRUE} },
934 { { 5, 2, TRUE}, { 4, 6, TRUE}, { 4, 2, TRUE}, { 5, 6, TRUE} },
935 { { 5, 2, TRUE}, { 6, 4, TRUE}, { 6, 2, TRUE}, {-1, -1, TRUE} },
936 { { 4, 6, TRUE}, { 2, 5, TRUE}, { 2, 6, TRUE}, {-1, -1, TRUE} },
937 { { 6, 4, TRUE}, { 2, 5, TRUE}, { 6, 5, TRUE}, { 2, 4, TRUE} },
938 { { 4, 6, TRUE}, { 5, 2, TRUE}, { 5, 6, TRUE}, { 4, 2, TRUE} },
939 { { 6, 4, TRUE}, { 5, 2, TRUE}, { 6, 2, TRUE}, {-1, -1, TRUE} },
940 /* One range is a proper subset of the other. */
941 { {33, 43, TRUE}, {37, 38, TRUE}, {33, 43, TRUE}, {-1, -1, TRUE} },
942 { {33, 43, TRUE}, {38, 37, TRUE}, {33, 37, TRUE}, {38, 43, TRUE} },
943 { {43, 33, TRUE}, {37, 38, TRUE}, {37, 33, TRUE}, {43, 38, TRUE} },
944 { {43, 33, TRUE}, {38, 37, TRUE}, {43, 33, TRUE}, {-1, -1, TRUE} },
945 { {37, 38, TRUE}, {33, 43, TRUE}, {33, 43, TRUE}, {-1, -1, TRUE} },
946 { {38, 37, TRUE}, {33, 43, TRUE}, {33, 37, TRUE}, {38, 43, TRUE} },
947 { {37, 38, TRUE}, {43, 33, TRUE}, {37, 33, TRUE}, {43, 38, TRUE} },
948 { {38, 37, TRUE}, {43, 33, TRUE}, {43, 33, TRUE}, {-1, -1, TRUE} },
949 /* Intersecting ranges share same start and end points */
950 { { 4, 20, TRUE}, { 4, 20, TRUE}, { 4, 20, TRUE}, {-1, -1, TRUE} },
951 { { 4, 20, TRUE}, {20, 4, TRUE}, {-1, -1, TRUE}, {-1, -1, TRUE} },
952 { {20, 4, TRUE}, { 4, 20, TRUE}, {-1, -1, TRUE}, {-1, -1, TRUE} },
953 { {20, 4, TRUE}, {20, 4, TRUE}, {20, 4, TRUE}, {-1, -1, TRUE} },
954 /* Intersecting ranges share same start point */
955 { { 7, 13, TRUE}, { 7, 19, TRUE}, { 7, 19, TRUE}, {-1, -1, TRUE} },
956 { { 7, 13, TRUE}, {19, 7, TRUE}, {19, 13, TRUE}, {-1, -1, TRUE} },
957 { {13, 7, TRUE}, {7, 19, TRUE}, {13, 19, TRUE}, {-1, -1, TRUE} },
958 { {13, 7, TRUE}, {19, 7, TRUE}, {19, 7, TRUE}, {-1, -1, TRUE} },
959 { { 7, 19, TRUE}, { 7, 13, TRUE}, { 7, 19, TRUE}, {-1, -1, TRUE} },
960 { {19, 7, TRUE}, { 7, 13, TRUE}, {19, 13, TRUE}, {-1, -1, TRUE} },
961 { { 7, 19, TRUE}, {13, 7, TRUE}, {13, 19, TRUE}, {-1, -1, TRUE} },
962 { {19, 7, TRUE}, {13, 7, TRUE}, {19, 7, TRUE}, {-1, -1, TRUE} },
963 /* Intersecting ranges share same end point */
964 { {12, 23, TRUE}, {18, 23, TRUE}, {12, 23, TRUE}, {-1, -1, TRUE} },
965 { {12, 23, TRUE}, {23, 18, TRUE}, {12, 18, TRUE}, {-1, -1, TRUE} },
966 { {23, 12, TRUE}, {18, 23, TRUE}, {18, 12, TRUE}, {-1, -1, TRUE} },
967 { {23, 12, TRUE}, {23, 18, TRUE}, {23, 12, TRUE}, {-1, -1, TRUE} },
968 { {18, 23, TRUE}, {12, 23, TRUE}, {12, 23, TRUE}, {-1, -1, TRUE} },
969 { {23, 18, TRUE}, {12, 23, TRUE}, {12, 18, TRUE}, {-1, -1, TRUE} },
970 { {18, 23, TRUE}, {23, 12, TRUE}, {18, 12, TRUE}, {-1, -1, TRUE} },
971 { {23, 18, TRUE}, {23, 12, TRUE}, {23, 12, TRUE}, {-1, -1, TRUE} } };
972 int i;
974 *msg = "combination of ranges";
975 if (msg_only)
976 return SVN_NO_ERROR;
978 for (i = 0; i < SIZE_OF_TEST_ARRAY; i++)
980 svn_merge_range_t *r1 = apr_palloc(pool, sizeof(*r1));
981 svn_merge_range_t *r2 = apr_palloc(pool, sizeof(*r2));
982 svn_merge_range_t *r1_expected = &(rangelist[i][2]);
983 svn_merge_range_t *r2_expected = &(rangelist[i][3]);
985 r1->start = rangelist[i][0].start;
986 r1->end = rangelist[i][0].end;
987 r1->inheritable = TRUE;
989 r2->start = rangelist[i][1].start;
990 r2->end = rangelist[i][1].end;
991 r2->inheritable = TRUE;
993 svn_range_compact(&r1, &r2);
994 if (!(((!r1 && r1_expected->start == -1
995 && r1_expected->end == -1)
996 || (r1 && (r1->start == r1_expected->start
997 && r1->end == r1_expected->end)))
998 && ((!r2 && r2_expected->start == -1
999 && r2_expected->end == -1)
1000 || (r2 && (r2->start == r2_expected->start
1001 && r2->end == r2_expected->end)))))
1003 const char *fail_msg = "svn_range_compact() should combine ranges ";
1004 fail_msg = apr_pstrcat(pool, fail_msg,
1005 apr_psprintf(pool, "(%ld-%ld),(%ld-%ld) "
1006 "into ",
1007 rangelist[i][0].start,
1008 rangelist[i][0].end,
1009 rangelist[i][1].start,
1010 rangelist[i][1].end), NULL);
1011 if (r1_expected->start == -1)
1012 fail_msg = apr_pstrcat(pool, fail_msg, "(NULL),",NULL);
1013 else
1014 fail_msg = apr_pstrcat(pool, fail_msg,
1015 apr_psprintf(pool, "(%ld-%ld),",
1016 r1_expected->start,
1017 r1_expected->end), NULL);
1018 if (r2_expected->start == -1)
1019 fail_msg = apr_pstrcat(pool, fail_msg, "(NULL) ",NULL);
1020 else
1021 fail_msg = apr_pstrcat(pool, fail_msg,
1022 apr_psprintf(pool, "(%ld-%ld) ",
1023 r2_expected->start,
1024 r2_expected->end), NULL);
1025 fail_msg = apr_pstrcat(pool, fail_msg, "but instead resulted in ",
1026 NULL);
1027 if (r1)
1028 fail_msg = apr_pstrcat(pool, fail_msg,
1029 apr_psprintf(pool, "(%ld-%ld),",
1030 r1->start, r1->end), NULL);
1031 else
1032 fail_msg = apr_pstrcat(pool, fail_msg, "(NULL),",NULL);
1033 if (r2)
1034 fail_msg = apr_pstrcat(pool, fail_msg,
1035 apr_psprintf(pool, "(%ld-%ld),",
1036 r2->start, r2->end), NULL);
1037 else
1038 fail_msg = apr_pstrcat(pool, fail_msg, "(NULL)",NULL);
1040 return fail(pool, fail_msg);
1043 return SVN_NO_ERROR;
1046 static svn_error_t *
1047 test_rangelist_merge(const char **msg,
1048 svn_boolean_t msg_only,
1049 svn_test_opts_t *opts,
1050 apr_pool_t *pool)
1052 int i;
1053 svn_error_t *err, *child_err;
1054 apr_array_header_t *rangelist1, *rangelist2;
1056 /* Struct for svn_rangelist_merge test data. Similar to
1057 mergeinfo_merge_test_data struct in svn_mergeinfo_merge() test. */
1058 struct rangelist_merge_test_data
1060 const char *mergeinfo1;
1061 const char *mergeinfo2;
1062 int expected_ranges;
1063 svn_merge_range_t expected_merge[6];
1066 #define SIZE_OF_RANGE_MERGE_TEST_ARRAY 52
1067 /* The actual test data. */
1068 struct rangelist_merge_test_data test_data[SIZE_OF_RANGE_MERGE_TEST_ARRAY] =
1070 /* Non-intersecting ranges */
1071 {"/A: 1-44", "/A: 70-101", 2, {{ 0, 44, TRUE }, {69, 101, TRUE }}},
1072 {"/A: 1-44*", "/A: 70-101", 2, {{ 0, 44, FALSE}, {69, 101, TRUE }}},
1073 {"/A: 1-44", "/A: 70-101*", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}},
1074 {"/A: 1-44*", "/A: 70-101*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}},
1075 {"/A: 70-101", "/A: 1-44", 2, {{ 0, 44, TRUE }, {69, 101, TRUE }}},
1076 {"/A: 70-101*", "/A: 1-44", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}},
1077 {"/A: 70-101", "/A: 1-44*", 2, {{ 0, 44, FALSE}, {69, 101, TRUE }}},
1078 {"/A: 70-101*", "/A: 1-44*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}},
1080 /* Intersecting ranges with same starting and ending revisions */
1081 {"/A: 4-20", "/A: 4-20", 1, {{3, 20, TRUE }}},
1082 {"/A: 4-20*", "/A: 4-20", 1, {{3, 20, TRUE }}},
1083 {"/A: 4-20", "/A: 4-20*", 1, {{3, 20, TRUE }}},
1084 {"/A: 4-20*", "/A: 4-20*", 1, {{3, 20, FALSE}}},
1086 /* Intersecting ranges with same starting revision */
1087 {"/A: 6-17", "/A: 6-12", 1, {{5, 17, TRUE}}},
1088 {"/A: 6-17*", "/A: 6-12", 2, {{5, 12, TRUE }, {12, 17, FALSE}}},
1089 {"/A: 6-17", "/A: 6-12*", 1, {{5, 17, TRUE }}},
1090 {"/A: 6-17*", "/A: 6-12*", 1, {{5, 17, FALSE}}},
1091 {"/A: 6-12", "/A: 6-17", 1, {{5, 17, TRUE }}},
1092 {"/A: 6-12*", "/A: 6-17", 1, {{5, 17, TRUE }}},
1093 {"/A: 6-12", "/A: 6-17*", 2, {{5, 12, TRUE }, {12, 17, FALSE}}},
1094 {"/A: 6-12*", "/A: 6-17*", 1, {{5, 17, FALSE}}},
1096 /* Intersecting ranges with same ending revision */
1097 {"/A: 5-77", "/A: 44-77", 1, {{4, 77, TRUE }}},
1098 {"/A: 5-77*", "/A: 44-77", 2, {{4, 43, FALSE}, {43, 77, TRUE}}},
1099 {"/A: 5-77", "/A: 44-77*", 1, {{4, 77, TRUE }}},
1100 {"/A: 5-77*", "/A: 44-77*", 1, {{4, 77, FALSE}}},
1101 {"/A: 44-77", "/A: 5-77", 1, {{4, 77, TRUE }}},
1102 {"/A: 44-77*", "/A: 5-77", 1, {{4, 77, TRUE }}},
1103 {"/A: 44-77", "/A: 5-77*", 2, {{4, 43, FALSE}, {43, 77, TRUE}}},
1104 {"/A: 44-77*", "/A: 5-77*", 1, {{4, 77, FALSE}}},
1106 /* Intersecting ranges with different starting and ending revision
1107 where one range is a proper subset of the other. */
1108 {"/A: 12-24", "/A: 20-23", 1, {{11, 24, TRUE }}},
1109 {"/A: 12-24*", "/A: 20-23", 3, {{11, 19, FALSE}, {19, 23, TRUE },
1110 {23, 24, FALSE}}},
1111 {"/A: 12-24", "/A: 20-23*", 1, {{11, 24, TRUE }}},
1112 {"/A: 12-24*", "/A: 20-23*", 1, {{11, 24, FALSE}}},
1113 {"/A: 20-23", "/A: 12-24", 1, {{11, 24, TRUE }}},
1114 {"/A: 20-23*", "/A: 12-24", 1, {{11, 24, TRUE }}},
1115 {"/A: 20-23", "/A: 12-24*", 3, {{11, 19, FALSE}, {19, 23, TRUE },
1116 {23, 24, FALSE}}},
1117 {"/A: 20-23*", "/A: 12-24*", 1, {{11, 24, FALSE}}},
1119 /* Intersecting ranges with different starting and ending revision
1120 where neither range is a proper subset of the other. */
1121 {"/A: 50-73", "/A: 60-99", 1, {{49, 99, TRUE }}},
1122 {"/A: 50-73*", "/A: 60-99", 2, {{49, 59, FALSE}, {59, 99, TRUE }}},
1123 {"/A: 50-73", "/A: 60-99*", 2, {{49, 73, TRUE }, {73, 99, FALSE}}},
1124 {"/A: 50-73*", "/A: 60-99*", 1, {{49, 99, FALSE}}},
1125 {"/A: 60-99", "/A: 50-73", 1, {{49, 99, TRUE }}},
1126 {"/A: 60-99*", "/A: 50-73", 2, {{49, 73, TRUE }, {73, 99, FALSE}}},
1127 {"/A: 60-99", "/A: 50-73*", 2, {{49, 59, FALSE}, {59, 99, TRUE }}},
1128 {"/A: 60-99*", "/A: 50-73*", 1, {{49, 99, FALSE}}},
1130 /* Multiple ranges. */
1131 {"/A: 1-5,7,12-13", "/A: 2-17", 1, {{0, 17, TRUE }}},
1132 {"/A: 1-5*,7*,12-13*", "/A: 2-17*", 1, {{0, 17, FALSE}}},
1134 {"/A: 1-5,7,12-13", "/A: 2-17*", 6,
1135 {{0, 5, TRUE }, { 5, 6, FALSE}, { 6, 7, TRUE },
1136 {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}},
1138 {"/A: 1-5*,7*,12-13*", "/A: 2-17", 2,
1139 {{0, 1, FALSE}, {1, 17, TRUE }}},
1141 {"/A: 2-17", "/A: 1-5,7,12-13", 1, {{0, 17, TRUE }}},
1142 {"/A: 2-17*", "/A: 1-5*,7*,12-13*", 1, {{0, 17, FALSE}}},
1144 {"/A: 2-17*", "/A: 1-5,7,12-13", 6,
1145 {{0, 5, TRUE }, { 5, 6, FALSE}, { 6, 7, TRUE },
1146 {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}},
1148 {"/A: 2-17", "/A: 1-5*,7*,12-13*", 2,
1149 {{0, 1, FALSE}, {1, 17, TRUE}}},
1151 *msg = "merge of rangelists";
1152 if (msg_only)
1153 return SVN_NO_ERROR;
1155 err = child_err = SVN_NO_ERROR;
1156 for (i = 0; i < SIZE_OF_RANGE_MERGE_TEST_ARRAY; i++)
1158 SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).mergeinfo1, pool));
1159 SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).mergeinfo2, pool));
1160 rangelist1 = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING);
1161 rangelist2 = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING);
1162 SVN_ERR(svn_rangelist_merge(&rangelist1, rangelist2, pool));
1163 child_err = verify_ranges_match(rangelist1,
1164 (test_data[i]).expected_merge,
1165 (test_data[i]).expected_ranges,
1166 apr_psprintf(pool,
1167 "svn_rangelist_merge "
1168 "case %i", i),
1169 "merge", pool);
1171 /* Collect all the errors rather than returning on the first. */
1172 if (child_err)
1174 if (err)
1175 svn_error_compose(err, child_err);
1176 else
1177 err = child_err;
1180 return err;
1183 static svn_error_t *
1184 test_rangelist_diff(const char **msg,
1185 svn_boolean_t msg_only,
1186 svn_test_opts_t *opts,
1187 apr_pool_t *pool)
1189 int i;
1190 svn_error_t *err, *child_err;
1191 apr_array_header_t *from, *to, *added, *deleted;
1193 /* Structure containing two ranges to diff and the expected output of the
1194 diff both when considering and ignoring range inheritance. */
1195 struct rangelist_diff_test_data
1197 /* svn:mergeinfo string representations */
1198 const char *from;
1199 const char *to;
1201 /* Expected results for performing svn_rangelist_diff
1202 while considering differences in inheritability to be real
1203 differences. */
1204 int expected_add_ranges;
1205 svn_merge_range_t expected_adds[10];
1206 int expected_del_ranges;
1207 svn_merge_range_t expected_dels[10];
1209 /* Expected results for performing svn_rangelist_diff
1210 while ignoring differences in inheritability. */
1211 int expected_add_ranges_ignore_inheritance;
1212 svn_merge_range_t expected_adds_ignore_inheritance[10];
1213 int expected_del_ranges_ignore_inheritance;
1214 svn_merge_range_t expected_dels_ignore_inheritance[10];
1217 #define SIZE_OF_RANGE_DIFF_TEST_ARRAY 16
1218 /* The actual test data array.
1220 'from' --> {"/A: 1,5-8", "/A: 1,6,10-12", <-- 'to'
1221 Number of adds when --> 1, { { 9, 12, TRUE } },
1222 considering inheritance
1224 Number of dels when --> 2, { { 4, 5, TRUE }, { 6, 8, TRUE } },
1225 considering inheritance
1227 Number of adds when --> 1, { { 9, 12, TRUE } },
1228 ignoring inheritance
1230 Number of dels when --> 2, { { 4, 5, TRUE }, { 6, 8, TRUE } } },
1231 ignoring inheritance
1233 The expected svn_merge_range_t's
1235 struct rangelist_diff_test_data test_data[SIZE_OF_RANGE_DIFF_TEST_ARRAY] =
1237 /* Add and Delete */
1238 {"/A: 1", "/A: 3",
1239 1, { { 2, 3, TRUE } },
1240 1, { { 0, 1, TRUE } },
1241 1, { { 2, 3, TRUE } },
1242 1, { { 0, 1, TRUE } } },
1244 /* Add only */
1245 {"/A: 1", "/A: 1,3",
1246 1, { { 2, 3, TRUE } },
1247 0, { { 0, 0, FALSE } },
1248 1, { { 2, 3, TRUE } },
1249 0, { { 0, 0, FALSE } } },
1251 /* Delete only */
1252 {"/A: 1,3", "/A: 1",
1253 0, { { 0, 0, FALSE } },
1254 1, { { 2, 3, TRUE } },
1255 0, { { 0, 0, FALSE } },
1256 1, { { 2, 3, TRUE } } },
1258 /* No diff */
1259 {"/A: 1,3", "/A: 1,3",
1260 0, { { 0, 0, FALSE } },
1261 0, { { 0, 0, FALSE } },
1262 0, { { 0, 0, FALSE } },
1263 0, { { 0, 0, FALSE } } },
1265 {"/A: 1,3*", "/A: 1,3*",
1266 0, { { 0, 0, FALSE } },
1267 0, { { 0, 0, FALSE } },
1268 0, { { 0, 0, FALSE } },
1269 0, { { 0, 0, FALSE } } },
1271 /* Adds and Deletes */
1272 {"/A: 1,5-8", "/A: 1,6,10-12",
1273 1, { { 9, 12, TRUE } },
1274 2, { { 4, 5, TRUE }, { 6, 8, TRUE } },
1275 1, { { 9, 12, TRUE } },
1276 2, { { 4, 5, TRUE }, { 6, 8, TRUE } } },
1278 {"/A: 6*", "/A: 6",
1279 1, { { 5, 6, TRUE } },
1280 1, { { 5, 6, FALSE } },
1281 0, { { 0, 0, FALSE } },
1282 0, { { 0, 0, FALSE } } },
1284 /* Intersecting range with different inheritability */
1285 {"/A: 6", "/A: 6*",
1286 1, { { 5, 6, FALSE } },
1287 1, { { 5, 6, TRUE } },
1288 0, { { 0, 0, FALSE } },
1289 0, { { 0, 0, FALSE } } },
1291 {"/A: 6*", "/A: 6",
1292 1, { { 5, 6, TRUE } },
1293 1, { { 5, 6, FALSE } },
1294 0, { { 0, 0, FALSE } },
1295 0, { { 0, 0, FALSE } } },
1297 {"/A: 1,5-8", "/A: 1,6*,10-12",
1298 2, { { 5, 6, FALSE }, { 9, 12, TRUE } },
1299 1, { { 4, 8, TRUE } },
1300 1, { { 9, 12, TRUE } },
1301 2, { { 4, 5, TRUE }, { 6, 8, TRUE } } },
1303 {"/A: 1,5-8*", "/A: 1,6,10-12",
1304 2, { { 5, 6, TRUE }, { 9, 12, TRUE } },
1305 1, { { 4, 8, FALSE } },
1306 1, { { 9, 12, TRUE } },
1307 2, { { 4, 5, FALSE }, { 6, 8, FALSE } } },
1309 /* Empty range diffs */
1310 {"/A: 3-9", "",
1311 0, { { 0, 0, FALSE } },
1312 1, { { 2, 9, TRUE } },
1313 0, { { 0, 0, FALSE } },
1314 1, { { 2, 9, TRUE } } },
1316 {"/A: 3-9*", "",
1317 0, { { 0, 0, FALSE } },
1318 1, { { 2, 9, FALSE } },
1319 0, { { 0, 0, FALSE } },
1320 1, { { 2, 9, FALSE } } },
1322 {"", "/A: 3-9",
1323 1, { { 2, 9, TRUE } },
1324 0, { { 0, 0, FALSE } },
1325 1, { { 2, 9, TRUE } },
1326 0, { { 0, 0, FALSE } } },
1328 {"", "/A: 3-9*",
1329 1, { { 2, 9, FALSE } },
1330 0, { { 0, 0, FALSE } },
1331 1, { { 2, 9, FALSE } },
1332 0, { { 0, 0, FALSE } } },
1334 /* Empty range no diff */
1335 {"", "",
1336 0, { { 0, 0, FALSE } },
1337 0, { { 0, 0, FALSE } },
1338 0, { { 0, 0, FALSE } },
1339 0, { { 0, 0, FALSE } } },
1342 *msg = "diff of rangelists";
1343 if (msg_only)
1344 return SVN_NO_ERROR;
1346 err = child_err = SVN_NO_ERROR;
1347 for (i = 0; i < SIZE_OF_RANGE_DIFF_TEST_ARRAY; i++)
1349 SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).to, pool));
1350 SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).from, pool));
1351 to = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING);
1352 from = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING);
1354 /* Represent empty mergeinfo with an empty rangelist. */
1355 if (to == NULL)
1356 to = apr_array_make(pool, 0, sizeof(*to));
1357 if (from == NULL)
1358 from = apr_array_make(pool, 0, sizeof(*from));
1360 /* First diff the ranges while considering
1361 differences in inheritance. */
1362 SVN_ERR(svn_rangelist_diff(&deleted, &added, from, to, TRUE, pool));
1364 child_err = verify_ranges_match(added,
1365 (test_data[i]).expected_adds,
1366 (test_data[i]).expected_add_ranges,
1367 apr_psprintf(pool,
1368 "svn_rangelist_diff"
1369 "case %i", i),
1370 "diff", pool);
1371 if (!child_err)
1372 child_err = verify_ranges_match(deleted,
1373 (test_data[i]).expected_dels,
1374 (test_data[i]).expected_del_ranges,
1375 apr_psprintf(pool,
1376 "svn_rangelist_diff"
1377 "case %i", i),
1378 "diff", pool);
1379 if (!child_err)
1381 /* Now do the diff while ignoring differences in inheritance. */
1382 SVN_ERR(svn_rangelist_diff(&deleted, &added, from, to, FALSE,
1383 pool));
1384 child_err = verify_ranges_match(
1385 added,
1386 (test_data[i]).expected_adds_ignore_inheritance,
1387 (test_data[i]).expected_add_ranges_ignore_inheritance,
1388 apr_psprintf(pool, "svn_rangelist_diff case %i", i),
1389 "diff", pool);
1391 if (!child_err)
1392 child_err = verify_ranges_match(
1393 deleted,
1394 (test_data[i]).expected_dels_ignore_inheritance,
1395 (test_data[i]).expected_del_ranges_ignore_inheritance,
1396 apr_psprintf(pool, "svn_rangelist_diff case %i", i),
1397 "diff", pool);
1400 /* Collect all the errors rather than returning on the first. */
1401 if (child_err)
1403 if (err)
1404 svn_error_compose(err, child_err);
1405 else
1406 err = child_err;
1409 return err;
1412 /* The test table. */
1414 struct svn_test_descriptor_t test_funcs[] =
1416 SVN_TEST_NULL,
1417 SVN_TEST_PASS(test_parse_single_line_mergeinfo),
1418 SVN_TEST_PASS(test_mergeinfo_dup),
1419 SVN_TEST_PASS(test_parse_combine_rangeinfo),
1420 SVN_TEST_PASS(test_parse_broken_mergeinfo),
1421 SVN_TEST_PASS(test_remove_rangelist),
1422 SVN_TEST_PASS(test_remove_mergeinfo),
1423 SVN_TEST_PASS(test_rangelist_reverse),
1424 SVN_TEST_PASS(test_rangelist_count_revs),
1425 SVN_TEST_PASS(test_rangelist_to_revs),
1426 SVN_TEST_PASS(test_rangelist_intersect),
1427 SVN_TEST_PASS(test_diff_mergeinfo),
1428 SVN_TEST_PASS(test_merge_mergeinfo),
1429 SVN_TEST_PASS(test_rangelist_to_string),
1430 SVN_TEST_PASS(test_mergeinfo_to_string),
1431 SVN_TEST_PASS(test_range_compact),
1432 SVN_TEST_PASS(test_rangelist_merge),
1433 SVN_TEST_PASS(test_rangelist_diff),
1434 SVN_TEST_NULL