2 * Incomplete regression tests for the diff/diff3 library.
4 * ====================================================================
5 * Copyright (c) 2003-2006 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 * ====================================================================
21 #include "svn_pools.h"
24 #include "../svn_test.h"
26 /* Used to terminate lines in large multi-line string literals. */
27 #define NL APR_EOL_STR
29 /* Random number seed. Yes, it's global, just pretend you can't see it. */
30 static apr_uint32_t diff_diff3_seed
;
32 /* Return the value of the current random number seed, initializing it if
37 static svn_boolean_t first
= TRUE
;
41 diff_diff3_seed
= (apr_uint32_t
) apr_time_now();
45 return diff_diff3_seed
;
48 /* Return a random number N such that MIN_VAL <= N <= MAX_VAL */
50 range_rand(apr_uint32_t min_val
,
53 apr_uint64_t diff
= max_val
- min_val
;
54 apr_uint64_t val
= diff
* svn_test_rand(&diff_diff3_seed
);
56 return min_val
+ (apr_uint32_t
) val
;
59 /* Make a file that is between MIN_LINES and MAX_LINES lines long, with at
60 most VAR_LINES distinct lines. If BLOCK_LINES is non-zero then every
61 other block of BLOCK_LINES lines will be identical, if BLOCK_LINES is
62 zero all lines will have contents chosen at random. If TRAILING_NEWLINE
63 is TRUE then the file will have a trailing newline, if not then it wont. */
65 make_random_file(const char *filename
,
70 svn_boolean_t trailing_newline
,
77 num_lines
= range_rand(min_lines
, max_lines
);
79 status
= apr_file_open(&file
, filename
,
80 APR_WRITE
| APR_CREATE
| APR_TRUNCATE
, APR_OS_DEFAULT
,
83 return svn_error_createf(status
, NULL
, "failed to open '%s'", filename
);
88 if (! (block_lines
&& (num_lines
/ block_lines
% 2)))
89 x
= range_rand(1, var_lines
);
92 if (num_lines
|| trailing_newline
)
93 apr_file_printf(file
, "line %d line %d line %d\n", x
, x
, x
);
95 apr_file_printf(file
, "line %d line %d line %d", x
, x
, x
);
98 status
= apr_file_close(file
);
100 return svn_error_createf(status
, NULL
, "failed to close '%s'", filename
);
106 /* Create a file called FILENAME containing CONTENTS */
108 make_file(const char *filename
,
109 const char *contents
,
115 status
= apr_file_open(&file
, filename
,
116 APR_WRITE
| APR_CREATE
| APR_TRUNCATE
, APR_OS_DEFAULT
,
119 return svn_error_createf(status
, NULL
, "failed to open '%s'", filename
);
121 status
= apr_file_write_full(file
, contents
, strlen(contents
), NULL
);
123 return svn_error_createf(status
, NULL
, "failed to write '%s'", filename
);
125 status
= apr_file_close(file
);
127 return svn_error_createf(status
, NULL
, "failed to close '%s'", filename
);
133 /* Create three files called FILENAME1, FILENAME2 and FILENAME3
134 containing CONTENTS1, CONTENTS2 and CONTENTS3 respectively. Run a
135 three way merge to merge the difference between CONTENTS1 and
136 CONTENTS2 into CONTENTS3, using OPTIONS, and verify that it results
137 in EXPECTED. The files FILENAME1, FILENAME2 and FILENAME3 will be
138 deleted if the merge is successful, and preserved otherwise. If
139 the merge fails the merge output will be in a file called
140 "merge-FILENAME1-FILENAME2-FILENAME3". */
142 three_way_merge(const char *filename1
,
143 const char *filename2
,
144 const char *filename3
,
145 const char *contents1
,
146 const char *contents2
,
147 const char *contents3
,
148 const char *expected
,
149 const svn_diff_file_options_t
*options
,
154 svn_stream_t
*ostream
;
156 svn_stringbuf_t
*actual
;
157 char *merge_name
= apr_psprintf(pool
, "merge-%s-%s-%s",
158 filename1
, filename2
, filename3
);
160 /* We have an EXPECTED string we can match, because we don't support
161 any other combinations (yet) than the ones above. */
162 svn_string_t
*original
= svn_string_create(contents1
, pool
);
163 svn_string_t
*modified
= svn_string_create(contents2
, pool
);
164 svn_string_t
*latest
= svn_string_create(contents3
, pool
);
166 options
= options
? options
: svn_diff_file_options_create(pool
);
168 SVN_ERR(svn_diff_mem_string_diff3(&diff
,
169 original
, modified
, latest
, options
, pool
));
171 actual
= svn_stringbuf_create("", pool
);
172 ostream
= svn_stream_from_stringbuf(actual
, pool
);
174 SVN_ERR(svn_diff_mem_string_output_merge
175 (ostream
, diff
, original
, modified
, latest
,
176 apr_psprintf(pool
, "||||||| %s", filename1
),
177 apr_psprintf(pool
, "<<<<<<< %s", filename2
),
178 apr_psprintf(pool
, ">>>>>>> %s", filename3
),
179 NULL
, /* separator */
180 FALSE
, FALSE
, pool
));
182 SVN_ERR(svn_stream_close(ostream
));
183 if (strcmp(actual
->data
, expected
) != 0)
184 return svn_error_createf(SVN_ERR_TEST_FAILED
, NULL
,
185 "Failed mem-diff, expected and actual "
186 "outputs differ.\nEXPECTED:\n%s\n"
187 "ACTUAL:\n%s\n", expected
, actual
->data
);
189 SVN_ERR(make_file(filename1
, contents1
, pool
));
190 SVN_ERR(make_file(filename2
, contents2
, pool
));
191 SVN_ERR(make_file(filename3
, contents3
, pool
));
193 SVN_ERR(svn_diff_file_diff3_2(&diff
, filename1
, filename2
, filename3
,
195 status
= apr_file_open(&output
, merge_name
,
196 APR_WRITE
| APR_CREATE
| APR_TRUNCATE
, APR_OS_DEFAULT
,
199 return svn_error_createf(status
, NULL
, "failed to open '%s'", merge_name
);
201 ostream
= svn_stream_from_aprfile(output
, pool
);
202 SVN_ERR(svn_diff_file_output_merge(ostream
, diff
,
203 filename1
, filename2
, filename3
,
204 NULL
, NULL
, NULL
, NULL
,
208 SVN_ERR(svn_stream_close(ostream
));
209 status
= apr_file_close(output
);
211 return svn_error_createf(status
, NULL
, "failed to close '%s'", merge_name
);
212 SVN_ERR(svn_stringbuf_from_file(&actual
, merge_name
, pool
));
213 if (strcmp(actual
->data
, expected
))
214 return svn_error_createf(SVN_ERR_TEST_FAILED
, NULL
,
215 "failed merging diff '%s' to '%s' into '%s'",
216 filename1
, filename2
, filename3
);
218 SVN_ERR(svn_io_remove_file(filename1
, pool
));
219 if (strcmp(filename1
, filename2
))
220 SVN_ERR(svn_io_remove_file(filename2
, pool
));
221 if (strcmp(filename1
, filename3
) && strcmp(filename2
, filename3
))
222 SVN_ERR(svn_io_remove_file(filename3
, pool
));
223 SVN_ERR(svn_io_remove_file(merge_name
, pool
));
229 /* Create two files called FILENAME1 and FILENAME2 containing
230 CONTENTS1 and CONTENTS2 respectively. Run a two way diff between
231 CONTENTS1 and CONTENTS2, using OPTIONS, and verify that it results
232 in EXPECTED. Then run the trivial merges to update CONTENTS1 to
233 CONTENTS2 and CONTENTS2 to CONTENTS1. The files FILENAME1,
234 FILENAME2 and be deleted if the diff and merges are successful, and
235 preserved otherwise. If the diff fails the diff output will be in
236 a file called "diff-FILENAME1-FILENAME2". */
238 two_way_diff(const char *filename1
,
239 const char *filename2
,
240 const char *contents1
,
241 const char *contents2
,
242 const char *expected
,
243 const svn_diff_file_options_t
*options
,
248 svn_stream_t
*ostream
;
250 svn_stringbuf_t
*actual
;
251 char *diff_name
= apr_psprintf(pool
, "diff-%s-%s", filename1
, filename2
);
253 /* We have an EXPECTED string we can match, because we don't support
254 any other combinations (yet) than the ones above. */
255 svn_string_t
*original
= svn_string_create(contents1
, pool
);
256 svn_string_t
*modified
= svn_string_create(contents2
, pool
);
258 options
= options
? options
: svn_diff_file_options_create(pool
);
260 SVN_ERR(svn_diff_mem_string_diff(&diff
, original
, modified
, options
, pool
));
262 actual
= svn_stringbuf_create("", pool
);
263 ostream
= svn_stream_from_stringbuf(actual
, pool
);
265 SVN_ERR(svn_diff_mem_string_output_unified(ostream
, diff
,
266 filename1
, filename2
,
267 SVN_APR_LOCALE_CHARSET
,
268 original
, modified
, pool
));
269 SVN_ERR(svn_stream_close(ostream
));
270 if (strcmp(actual
->data
, expected
) != 0)
271 return svn_error_createf(SVN_ERR_TEST_FAILED
, NULL
,
272 "Failed mem-diff, expected and actual "
273 "outputs differ.\nEXPECTED:\n%s\n"
274 "ACTUAL:\n%s\n", expected
, actual
->data
);
276 SVN_ERR(make_file(filename1
, contents1
, pool
));
277 SVN_ERR(make_file(filename2
, contents2
, pool
));
279 /* Check that two-way diff between contents1 and contents2 produces
281 SVN_ERR(svn_diff_file_diff_2(&diff
, filename1
, filename2
, options
, pool
));
282 status
= apr_file_open(&output
, diff_name
,
283 APR_WRITE
| APR_CREATE
| APR_TRUNCATE
, APR_OS_DEFAULT
,
286 return svn_error_createf(status
, NULL
, "failed to open '%s'", diff_name
);
288 ostream
= svn_stream_from_aprfile(output
, pool
);
289 SVN_ERR(svn_diff_file_output_unified2(ostream
, diff
,
290 filename1
, filename2
,
291 filename1
, filename2
,
292 SVN_APR_LOCALE_CHARSET
, pool
));
293 SVN_ERR(svn_stream_close(ostream
));
294 status
= apr_file_close(output
);
296 return svn_error_createf(status
, NULL
, "failed to close '%s'", diff_name
);
298 SVN_ERR(svn_stringbuf_from_file(&actual
, diff_name
, pool
));
299 if (strcmp(actual
->data
, expected
))
300 return svn_error_createf(SVN_ERR_TEST_FAILED
, NULL
,
301 "failed comparing '%s' and '%s'",
302 filename1
, filename2
);
304 /* May as well do the trivial merges while we are here */
305 SVN_ERR(three_way_merge(filename1
, filename2
, filename1
,
306 contents1
, contents2
, contents1
, contents2
, NULL
,
308 SVN_ERR(three_way_merge(filename2
, filename1
, filename2
,
309 contents2
, contents1
, contents2
, contents1
, NULL
,
312 SVN_ERR(svn_io_remove_file(diff_name
, pool
));
319 int index
; /* Zero based line number */
320 int mod
; /* Type of mod: 0, 1, 2 (can be interpreted as you like just
321 do it consistently) */
324 /* Fill the SELECTED array of length NUM to select with randomly chosen
325 values, ensuring that none of SELECTED.INDEX are duplicates and that all
326 the SELECTED.INDEX values are less than NUM_LINES. Also ensure that for
327 each SELECTED.INDEX the three elements of LINES from SELECTED.INDEX-1 to
328 SELECTED.INDEX+1 are unset. Set all LINES[SELECTED.INDEX]. */
330 select_lines(struct random_mod
*selected
,
332 svn_boolean_t
*lines
,
336 for (i
= 0; i
< num_to_select
; ++i
)
341 j
= range_rand(0, num_lines
- 1);
342 if (lines
[j
] /* already selected */
344 (j
> 0 && lines
[j
- 1]) /* previous selected */
346 (j
< num_lines
- 1 && lines
[j
+ 1])) /* next selected */
347 continue; /* try again */
350 selected
[i
].index
= j
;
351 selected
[i
].mod
= range_rand(0, 2);
357 /* Create a file called FILENAME where the contents are obtained by
358 applying the modifications in MOD_LINES, of which there are NUM_MODS, to
359 a theoretical pristine file of length NUM_LINES lines. */
361 make_random_merge_file(const char *filename
,
363 struct random_mod
*mod_lines
,
371 status
= apr_file_open(&file
, filename
,
372 APR_WRITE
| APR_CREATE
| APR_TRUNCATE
, APR_OS_DEFAULT
,
375 return svn_error_createf(status
, NULL
, "failed to open '%s'", filename
);
377 for (i
= 0; i
< num_lines
; ++i
)
380 for (j
= 0; j
< num_mods
; ++j
)
381 if (mod_lines
[j
].index
== i
)
386 switch (mod_lines
[j
].mod
)
389 apr_file_printf(file
, "replace line %d\n", i
);
392 apr_file_printf(file
,
394 "unmodified line %d\n"
399 ; /* Delete the line */
404 apr_file_printf(file
, "unmodified line %d\n", i
);
408 status
= apr_file_close(file
);
410 return svn_error_createf(status
, NULL
, "failed to close '%s'", filename
);
416 /* ========================================================================== */
419 dump_core(const char **msg
,
420 svn_boolean_t msg_only
,
421 svn_test_opts_t
*opts
,
424 *msg
= "these dump core";
428 SVN_ERR(two_way_diff("foo1", "bar1",
434 SVN_ERR(two_way_diff("foo2", "bar2",
449 SVN_ERR(two_way_diff("foo3", "bar3",
469 test_two_way_unified(const char **msg
,
470 svn_boolean_t msg_only
,
471 svn_test_opts_t
*opts
,
474 svn_diff_file_options_t
*diff_opts
= svn_diff_file_options_create(pool
);
475 *msg
= "2-way unified diff and trivial merge";
479 SVN_ERR(two_way_diff("foo4", "bar4",
494 SVN_ERR(two_way_diff("foo4b", "bar4b",
509 diff_opts
->ignore_eol_style
= TRUE
;
510 SVN_ERR(two_way_diff("foo4c", "bar4c",
524 diff_opts
->ignore_eol_style
= FALSE
;
526 SVN_ERR(two_way_diff("foo5", "bar5",
541 SVN_ERR(two_way_diff("foo5b", "bar5b",
556 diff_opts
->ignore_eol_style
= TRUE
;
557 SVN_ERR(two_way_diff("foo5c", "bar5c",
573 SVN_ERR(two_way_diff("foo5d", "bar5d",
590 diff_opts
->ignore_eol_style
= FALSE
;
592 SVN_ERR(two_way_diff("foo6", "bar6",
604 SVN_ERR(two_way_diff("foo6b", "bar6b",
622 SVN_ERR(two_way_diff("foo6c", "bar6c",
640 SVN_ERR(two_way_diff("foo6d", "bar6d",
658 diff_opts
->ignore_space
= svn_diff_file_ignore_space_change
;
659 SVN_ERR(two_way_diff("foo6e", "bar6e",
670 diff_opts
->ignore_space
= svn_diff_file_ignore_space_none
;
672 diff_opts
->ignore_space
= svn_diff_file_ignore_space_all
;
673 SVN_ERR(two_way_diff("foo6f", "bar6f",
684 diff_opts
->ignore_space
= svn_diff_file_ignore_space_none
;
686 diff_opts
->ignore_space
= svn_diff_file_ignore_space_all
;
687 diff_opts
->ignore_eol_style
= TRUE
;
688 SVN_ERR(two_way_diff("foo6f", "bar6f",
699 diff_opts
->ignore_space
= svn_diff_file_ignore_space_none
;
700 diff_opts
->ignore_eol_style
= FALSE
;
702 SVN_ERR(two_way_diff("foo7", "bar7",
714 SVN_ERR(two_way_diff("foo7a", "bar7a",
729 SVN_ERR(two_way_diff("foo7b", "bar7b",
744 SVN_ERR(two_way_diff("foo8", "bar8",
762 SVN_ERR(two_way_diff("foo9", "bar9",
777 SVN_ERR(two_way_diff("foo10", "bar10",
792 "\\ No newline at end of file" NL
797 SVN_ERR(two_way_diff("foo11", "bar11",
814 "\\ No newline at end of file" NL
,
817 SVN_ERR(two_way_diff("foo12", "bar12",
832 "\\ No newline at end of file" NL
835 "\\ No newline at end of file" NL
,
838 SVN_ERR(two_way_diff("foo13", "bar13",
871 SVN_ERR(two_way_diff("foo14", "bar14",
902 SVN_ERR(two_way_diff("foo16", "bar16",
925 test_two_way_unified_suspect(const char **msg
,
926 svn_boolean_t msg_only
,
927 svn_test_opts_t
*opts
,
930 *msg
= "2-way unified diff where output is suspect";
934 SVN_ERR(two_way_diff("foo15a", "bar15a",
966 SVN_ERR(two_way_diff("foo15b", "bar15b",
1002 SVN_ERR(two_way_diff("foo15c", "bar15c",
1026 "@@ -2,7 +2,8 @@" NL
1038 return SVN_NO_ERROR
;
1042 static svn_error_t
*
1043 test_three_way_merge_no_overlap(const char **msg
,
1044 svn_boolean_t msg_only
,
1045 svn_test_opts_t
*opts
,
1048 svn_diff_file_options_t
*diff_opts
= svn_diff_file_options_create(pool
);
1049 *msg
= "3-way merge, non-overlapping changes";
1051 return SVN_NO_ERROR
;
1053 SVN_ERR(three_way_merge("zig1", "zag1", "zog1",
1075 SVN_ERR(three_way_merge("zig1a", "zag1a", "zog1a",
1097 SVN_ERR(three_way_merge("zig1b", "zag1b", "zog1b",
1119 diff_opts
->ignore_space
= svn_diff_file_ignore_space_all
;
1120 SVN_ERR(three_way_merge("zig1c", "zag1c", "zog1c",
1141 diff_opts
->ignore_space
= svn_diff_file_ignore_space_none
;
1143 SVN_ERR(three_way_merge("zig2", "zag2", "zog2",
1167 SVN_ERR(three_way_merge("zig3a", "zag3a", "zog3a",
1185 SVN_ERR(three_way_merge("zig3b", "zag3b", "zog3b",
1203 diff_opts
->ignore_space
= svn_diff_file_ignore_space_all
;
1204 diff_opts
->ignore_eol_style
= TRUE
;
1205 SVN_ERR(three_way_merge("zig2c", "zag2c", "zog2c",
1228 diff_opts
->ignore_space
= svn_diff_file_ignore_space_none
;
1229 diff_opts
->ignore_eol_style
= FALSE
;
1231 SVN_ERR(three_way_merge("zig4", "zag4", "zog4",
1273 SVN_ERR(three_way_merge("zig5", "zag5", "zog5",
1295 SVN_ERR(three_way_merge("zig6", "zag6", "zog6",
1315 SVN_ERR(three_way_merge("zig7", "zag7", "zog7",
1335 diff_opts
->ignore_space
= svn_diff_file_ignore_space_all
;
1336 diff_opts
->ignore_eol_style
= FALSE
;
1337 SVN_ERR(three_way_merge("zig8", "zag8", "zog8",
1349 "New line in zog8\n",
1354 "New line in zog8\n",
1357 return SVN_NO_ERROR
;
1361 static svn_error_t
*
1362 test_three_way_merge_with_overlap(const char **msg
,
1363 svn_boolean_t msg_only
,
1364 svn_test_opts_t
*opts
,
1367 *msg
= "3-way merge, non-conflicting overlapping changes";
1369 return SVN_NO_ERROR
;
1371 SVN_ERR(three_way_merge("splish1", "splash1", "splosh1",
1401 SVN_ERR(three_way_merge("splish2", "splash2", "splosh2",
1442 SVN_ERR(three_way_merge("splish3", "splash3", "splosh3",
1464 SVN_ERR(three_way_merge("splish4", "splash4", "splosh4",
1515 return SVN_NO_ERROR
;
1519 static svn_error_t
*
1520 test_three_way_merge_with_conflict(const char **msg
,
1521 svn_boolean_t msg_only
,
1522 svn_test_opts_t
*opts
,
1525 *msg
= "3-way merge, conflicting overlapping changes";
1527 return SVN_NO_ERROR
;
1529 SVN_ERR(three_way_merge("dig1", "dug1", "dag1",
1541 SVN_ERR(three_way_merge("dig2", "dug2", "dag2",
1566 SVN_ERR(three_way_merge("dig2a", "dug2a", "dag2a",
1588 ">>>>>>> dag2a\r\n",
1591 SVN_ERR(three_way_merge("dig2b", "dug2b", "dag2b",
1616 SVN_ERR(three_way_merge("dig3", "dug3", "dag3",
1642 SVN_ERR(three_way_merge("dig4", "dug4", "dag4",
1665 return SVN_NO_ERROR
;
1669 static svn_error_t
*
1670 random_trivial_merge(const char **msg
,
1671 svn_boolean_t msg_only
,
1672 svn_test_opts_t
*opts
,
1676 apr_pool_t
*subpool
= svn_pool_create(pool
);
1678 *msg
= apr_psprintf(pool
, "random trivial merge (seed:%u)", seed_val());
1680 return SVN_NO_ERROR
;
1682 for (i
= 0; i
< 5; ++i
)
1684 const char *filename1
= "trivial1";
1685 const char *filename2
= "trivial2";
1686 int min_lines
= 1000;
1687 int max_lines
= 1100;
1689 int block_lines
= 10;
1690 svn_stringbuf_t
*contents1
, *contents2
;
1692 SVN_ERR(make_random_file(filename1
,
1693 min_lines
, max_lines
, var_lines
, block_lines
,
1695 SVN_ERR(make_random_file(filename2
,
1696 min_lines
, max_lines
, var_lines
, block_lines
,
1699 SVN_ERR(svn_stringbuf_from_file(&contents1
, filename1
, subpool
));
1700 SVN_ERR(svn_stringbuf_from_file(&contents2
, filename2
, subpool
));
1702 SVN_ERR(three_way_merge(filename1
, filename2
, filename1
,
1703 contents1
->data
, contents2
->data
,
1704 contents1
->data
, contents2
->data
, NULL
,
1706 SVN_ERR(three_way_merge(filename2
, filename1
, filename2
,
1707 contents2
->data
, contents1
->data
,
1708 contents2
->data
, contents1
->data
, NULL
,
1710 svn_pool_clear(subpool
);
1712 svn_pool_destroy(subpool
);
1714 return SVN_NO_ERROR
;
1718 /* The "original" file has a number of distinct lines. We generate two
1719 random modifications by selecting two subsets of the original lines and
1720 for each selected line either adding an additional line, replacing the
1721 line, or deleting the line. The two subsets are chosen so that each
1722 selected line is distinct and no two selected lines are adjacent. This
1723 means the two sets of changes should merge without conflict. */
1724 static svn_error_t
*
1725 random_three_way_merge(const char **msg
,
1726 svn_boolean_t msg_only
,
1727 svn_test_opts_t
*opts
,
1731 apr_pool_t
*subpool
= svn_pool_create(pool
);
1733 *msg
= apr_psprintf(pool
, "random 3-way merge (seed:%u)", seed_val());
1735 return SVN_NO_ERROR
;
1737 for (i
= 0; i
< 20; ++i
)
1739 const char *filename1
= "original";
1740 const char *filename2
= "modified1";
1741 const char *filename3
= "modified2";
1742 const char *filename4
= "combined";
1743 svn_stringbuf_t
*original
, *modified1
, *modified2
, *combined
;
1744 int num_lines
= 100, num_src
= 10, num_dst
= 10;
1745 svn_boolean_t
*lines
= apr_pcalloc(subpool
, sizeof(*lines
) * num_lines
);
1746 struct random_mod
*src_lines
= apr_palloc(subpool
,
1747 sizeof(*src_lines
) * num_src
);
1748 struct random_mod
*dst_lines
= apr_palloc(subpool
,
1749 sizeof(*dst_lines
) * num_dst
);
1750 struct random_mod
*mrg_lines
= apr_palloc(subpool
,
1752 * (num_src
+ num_dst
)));
1754 select_lines(src_lines
, num_src
, lines
, num_lines
);
1755 select_lines(dst_lines
, num_dst
, lines
, num_lines
);
1756 memcpy(mrg_lines
, src_lines
, sizeof(*mrg_lines
) * num_src
);
1757 memcpy(mrg_lines
+ num_src
, dst_lines
, sizeof(*mrg_lines
) * num_dst
);
1759 SVN_ERR(make_random_merge_file(filename1
, num_lines
, NULL
, 0, pool
));
1760 SVN_ERR(make_random_merge_file(filename2
, num_lines
, src_lines
, num_src
,
1762 SVN_ERR(make_random_merge_file(filename3
, num_lines
, dst_lines
, num_dst
,
1764 SVN_ERR(make_random_merge_file(filename4
, num_lines
, mrg_lines
,
1765 num_src
+ num_dst
, pool
));
1767 SVN_ERR(svn_stringbuf_from_file(&original
, filename1
, pool
));
1768 SVN_ERR(svn_stringbuf_from_file(&modified1
, filename2
, pool
));
1769 SVN_ERR(svn_stringbuf_from_file(&modified2
, filename3
, pool
));
1770 SVN_ERR(svn_stringbuf_from_file(&combined
, filename4
, pool
));
1772 SVN_ERR(three_way_merge(filename1
, filename2
, filename3
,
1773 original
->data
, modified1
->data
,
1774 modified2
->data
, combined
->data
, NULL
, subpool
));
1775 SVN_ERR(three_way_merge(filename1
, filename3
, filename2
,
1776 original
->data
, modified2
->data
,
1777 modified1
->data
, combined
->data
, NULL
, subpool
));
1779 SVN_ERR(svn_io_remove_file(filename4
, pool
));
1781 svn_pool_clear(subpool
);
1783 svn_pool_destroy(subpool
);
1785 return SVN_NO_ERROR
;
1788 /* This is similar to random_three_way_merge above, except this time half
1789 of the original-to-modified1 changes are already present in modified2
1790 (or, equivalently, half the original-to-modified2 changes are already
1791 present in modified1). Since the overlapping changes match exactly the
1792 merge should work without a conflict. */
1793 static svn_error_t
*
1794 merge_with_part_already_present(const char **msg
,
1795 svn_boolean_t msg_only
,
1796 svn_test_opts_t
*opts
,
1800 apr_pool_t
*subpool
= svn_pool_create(pool
);
1802 *msg
= apr_psprintf(pool
, "merge with part already present (seed:%u)",
1805 return SVN_NO_ERROR
;
1807 for (i
= 0; i
< 20; ++i
)
1809 const char *filename1
= "pap-original";
1810 const char *filename2
= "pap-modified1";
1811 const char *filename3
= "pap-modified2";
1812 const char *filename4
= "pap-combined";
1813 svn_stringbuf_t
*original
, *modified1
, *modified2
, *combined
;
1814 int num_lines
= 200, num_src
= 20, num_dst
= 20;
1815 svn_boolean_t
*lines
= apr_pcalloc(subpool
, sizeof(*lines
) * num_lines
);
1816 struct random_mod
*src_lines
= apr_palloc(subpool
,
1817 sizeof(*src_lines
) * num_src
);
1818 struct random_mod
*dst_lines
= apr_palloc(subpool
,
1819 sizeof(*dst_lines
) * num_dst
);
1820 struct random_mod
*mrg_lines
= apr_palloc(subpool
,
1822 * (num_src
+ num_dst
/ 2)));
1824 select_lines(src_lines
, num_src
, lines
, num_lines
);
1825 /* Select half the destination changes at random */
1826 select_lines(dst_lines
, num_dst
/ 2, lines
, num_lines
);
1827 /* Copy the other half from the source changes */
1828 memcpy(dst_lines
+ num_dst
/ 2, src_lines
,
1829 sizeof(*dst_lines
) * (num_dst
- num_dst
/ 2));
1830 memcpy(mrg_lines
, src_lines
, sizeof(*mrg_lines
) * num_src
);
1831 memcpy(mrg_lines
+ num_src
, dst_lines
,
1832 sizeof(*mrg_lines
) * num_dst
/ 2);
1834 SVN_ERR(make_random_merge_file(filename1
, num_lines
, NULL
, 0, pool
));
1835 SVN_ERR(make_random_merge_file(filename2
, num_lines
, src_lines
, num_src
,
1837 SVN_ERR(make_random_merge_file(filename3
, num_lines
, dst_lines
, num_dst
,
1839 SVN_ERR(make_random_merge_file(filename4
, num_lines
, mrg_lines
,
1840 num_src
+ num_dst
/ 2, pool
));
1842 SVN_ERR(svn_stringbuf_from_file(&original
, filename1
, pool
));
1843 SVN_ERR(svn_stringbuf_from_file(&modified1
, filename2
, pool
));
1844 SVN_ERR(svn_stringbuf_from_file(&modified2
, filename3
, pool
));
1845 SVN_ERR(svn_stringbuf_from_file(&combined
, filename4
, pool
));
1847 SVN_ERR(three_way_merge(filename1
, filename2
, filename3
,
1848 original
->data
, modified1
->data
,
1849 modified2
->data
, combined
->data
, NULL
, subpool
));
1850 SVN_ERR(three_way_merge(filename1
, filename3
, filename2
,
1851 original
->data
, modified2
->data
,
1852 modified1
->data
, combined
->data
, NULL
, subpool
));
1854 SVN_ERR(svn_io_remove_file(filename4
, pool
));
1856 svn_pool_clear(subpool
);
1858 svn_pool_destroy(subpool
);
1860 return SVN_NO_ERROR
;
1863 /* Merge is more "aggressive" about resolving conflicts than traditional
1864 * patch or diff3. Some people consider this behaviour to be a bug, see
1865 * http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=35014
1867 static svn_error_t
*
1868 merge_adjacent_changes(const char **msg
,
1869 svn_boolean_t msg_only
,
1870 svn_test_opts_t
*opts
,
1873 *msg
= "3-way merge, adjacent changes";
1875 return SVN_NO_ERROR
;
1877 SVN_ERR(three_way_merge("adj1", "adj2", "adj3",
1899 return SVN_NO_ERROR
;
1904 /* ========================================================================== */
1906 struct svn_test_descriptor_t test_funcs
[] =
1909 SVN_TEST_PASS(dump_core
),
1910 SVN_TEST_PASS(test_two_way_unified
),
1911 SVN_TEST_PASS(test_two_way_unified_suspect
),
1912 SVN_TEST_PASS(test_three_way_merge_no_overlap
),
1913 SVN_TEST_PASS(test_three_way_merge_with_overlap
),
1914 SVN_TEST_PASS(test_three_way_merge_with_conflict
),
1915 SVN_TEST_PASS(random_trivial_merge
),
1916 SVN_TEST_PASS(random_three_way_merge
),
1917 SVN_TEST_PASS(merge_with_part_already_present
),
1918 SVN_TEST_PASS(merge_adjacent_changes
),