Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / cmdline / svnsync_tests.py
blobc54f06c44541fcbe2778135d04672c1392fdec8e
1 #!/usr/bin/env python
3 # svnsync_tests.py: Tests SVNSync's repository mirroring capabilities.
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2005-2007 CollabNet. All rights reserved.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at http://subversion.tigris.org/license-1.html.
14 # If newer versions of this license are posted there, you may use a
15 # newer version instead, at your option.
17 ######################################################################
19 # General modules
20 import sys, os
22 # Our testing module
23 import svntest
24 from svntest.verify import SVNUnexpectedStdout, SVNUnexpectedStderr
25 from svntest.verify import SVNExpectedStderr
26 from svntest.main import write_restrictive_svnserve_conf
28 # (abbreviation)
29 Skip = svntest.testcase.Skip
30 XFail = svntest.testcase.XFail
31 Item = svntest.wc.StateItem
34 ######################################################################
35 # Helper routines
38 def build_repos(sbox):
39 """Avoid the use sbox.build() because we're working with a repos
40 other than the Greek tree."""
41 # Cleanup after the last run by removing any left-over repository.
42 svntest.main.safe_rmtree(sbox.repo_dir)
44 # Create an empty repository.
45 svntest.main.create_repos(sbox.repo_dir)
48 def run_sync(url, expected_error=None):
49 "Synchronize the mirror repository with the master"
50 output, errput = svntest.main.run_svnsync(
51 "synchronize", url,
52 "--username", svntest.main.wc_author,
53 "--password", svntest.main.wc_passwd)
54 if errput:
55 if expected_error is None:
56 raise SVNUnexpectedStderr(errput)
57 else:
58 expected_error = svntest.verify.RegexOutput(expected_error,
59 match_all=False)
60 svntest.verify.compare_and_display_lines(None, "STDERR",
61 expected_error, errput)
62 elif expected_error is not None:
63 raise SVNExpectedStderr
64 if not output and not expected_error:
65 # should be: ['Committed revision 1.\n', 'Committed revision 2.\n']
66 raise SVNUnexpectedStdout("Missing stdout")
68 def run_init(dst_url, src_url):
69 "Initialize the mirror repository from the master"
70 output, errput = svntest.main.run_svnsync(
71 "initialize", dst_url, src_url,
72 "--username", svntest.main.wc_author,
73 "--password", svntest.main.wc_passwd)
74 if errput:
75 raise SVNUnexpectedStderr(errput)
76 if output != ['Copied properties for revision 0.\n']:
77 raise SVNUnexpectedStdout(output)
80 def run_test(sbox, dump_file_name, subdir = None, exp_dump_file_name = None):
81 """Load a dump file, sync repositories, and compare contents with the original
82 or another dump file."""
84 # Create the empty master repository.
85 build_repos(sbox)
87 # This directory contains all the dump files
88 svnsync_tests_dir = os.path.join(os.path.dirname(sys.argv[0]),
89 'svnsync_tests_data')
90 # Load the specified dump file into the master repository.
91 master_dumpfile_contents = file(os.path.join(svnsync_tests_dir,
92 dump_file_name)).readlines()
93 svntest.actions.run_and_verify_load(sbox.repo_dir, master_dumpfile_contents)
95 # Create the empty destination repository.
96 dest_sbox = sbox.clone_dependent()
97 build_repos(dest_sbox)
99 # Setup the mirror repository. Feed it the UUID of the source repository.
100 output, errput = svntest.main.run_svnlook("uuid", sbox.repo_dir)
101 mirror_cfg = ["SVN-fs-dump-format-version: 2\n",
102 "UUID: " + output[0],
104 svntest.actions.run_and_verify_load(dest_sbox.repo_dir, mirror_cfg)
106 # Create the revprop-change hook for this test
107 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
109 repo_url = sbox.repo_url
110 if subdir:
111 repo_url = repo_url + subdir
112 run_init(dest_sbox.repo_url, repo_url)
114 run_sync(dest_sbox.repo_url)
116 # Remove some SVNSync-specific housekeeping properties from the
117 # mirror repository in preparation for the comparison dump.
118 for prop_name in ("svn:sync-from-url", "svn:sync-from-uuid",
119 "svn:sync-last-merged-rev"):
120 svntest.actions.run_and_verify_svn(
121 None, None, [], "propdel", "--revprop", "-r", "0",
122 prop_name, dest_sbox.repo_url)
124 # Create a dump file from the mirror repository.
125 dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
127 # Compare the dump produced by the mirror repository with either the original
128 # dump file (used to create the master repository) or another specified dump
129 # file.
130 if exp_dump_file_name:
131 exp_master_dumpfile_contents = file(os.path.join(svnsync_tests_dir,
132 exp_dump_file_name)).readlines()
133 else:
134 exp_master_dumpfile_contents = master_dumpfile_contents
136 svntest.verify.compare_and_display_lines(
137 "Dump files", "DUMP", exp_master_dumpfile_contents, dest_dump)
140 ######################################################################
141 # Tests
143 #----------------------------------------------------------------------
145 def copy_and_modify(sbox):
146 "copy and modify"
147 run_test(sbox, "copy-and-modify.dump")
149 #----------------------------------------------------------------------
151 def copy_from_previous_version_and_modify(sbox):
152 "copy from previous version and modify"
153 run_test(sbox, "copy-from-previous-version-and-modify.dump")
155 #----------------------------------------------------------------------
157 def copy_from_previous_version(sbox):
158 "copy from previous version"
159 run_test(sbox, "copy-from-previous-version.dump")
161 #----------------------------------------------------------------------
163 def modified_in_place(sbox):
164 "modified in place"
165 run_test(sbox, "modified-in-place.dump")
167 #----------------------------------------------------------------------
169 def tag_empty_trunk(sbox):
170 "tag empty trunk"
171 run_test(sbox, "tag-empty-trunk.dump")
173 #----------------------------------------------------------------------
175 def tag_trunk_with_dir(sbox):
176 "tag trunk containing a sub-directory"
177 run_test(sbox, "tag-trunk-with-dir.dump")
179 #----------------------------------------------------------------------
181 def tag_trunk_with_file(sbox):
182 "tag trunk containing a file"
183 run_test(sbox, "tag-trunk-with-file.dump")
185 #----------------------------------------------------------------------
187 def tag_trunk_with_file2(sbox):
188 "tag trunk containing a file (#2)"
189 run_test(sbox, "tag-trunk-with-file2.dump")
191 #----------------------------------------------------------------------
193 def tag_with_modified_file(sbox):
194 "tag with a modified file"
195 run_test(sbox, "tag-with-modified-file.dump")
197 #----------------------------------------------------------------------
199 def dir_prop_change(sbox):
200 "directory property changes"
201 run_test(sbox, "dir_prop_change.dump")
203 #----------------------------------------------------------------------
205 def file_dir_file(sbox):
206 "files and dirs mixed together"
207 run_test(sbox, "file-dir-file.dump")
209 #----------------------------------------------------------------------
211 def copy_parent_modify_prop(sbox):
212 "copy parent and modify prop"
213 run_test(sbox, "copy-parent-modify-prop.dump")
215 #----------------------------------------------------------------------
217 def detect_meddling(sbox):
218 "detect non-svnsync commits in destination"
220 sbox.build("svnsync-meddling")
222 dest_sbox = sbox.clone_dependent()
223 build_repos(dest_sbox)
225 # Make our own destination checkout (have to do it ourself because
226 # it is not greek).
228 svntest.main.safe_rmtree(dest_sbox.wc_dir)
229 svntest.actions.run_and_verify_svn(None,
230 None,
232 'co',
233 dest_sbox.repo_url,
234 dest_sbox.wc_dir)
236 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
238 run_init(dest_sbox.repo_url, sbox.repo_url)
239 run_sync(dest_sbox.repo_url)
241 svntest.actions.run_and_verify_svn(None,
242 None,
244 'up',
245 dest_sbox.wc_dir)
247 # Commit some change to the destination, which should be detected by svnsync
248 svntest.main.file_append(os.path.join(dest_sbox.wc_dir, 'A', 'B', 'lambda'),
249 'new lambda text')
250 svntest.actions.run_and_verify_svn(None,
251 None,
253 'ci',
254 '-m', 'msg',
255 dest_sbox.wc_dir)
257 run_sync(dest_sbox.repo_url,
258 ".*Destination HEAD \\(2\\) is not the last merged revision \\(1\\).*")
260 #----------------------------------------------------------------------
262 def basic_authz(sbox):
263 "verify that unreadable content is not synced"
265 sbox.build("svnsync-basic-authz")
267 write_restrictive_svnserve_conf(sbox.repo_dir)
269 dest_sbox = sbox.clone_dependent()
270 build_repos(dest_sbox)
272 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
274 run_init(dest_sbox.repo_url, sbox.repo_url)
276 svntest.main.file_write(sbox.authz_file,
277 "[svnsync-basic-authz:/]\n"
278 "* = r\n"
279 "\n"
280 "[svnsync-basic-authz:/A/B]\n"
281 "* = \n"
282 "\n"
283 "[svnsync-basic-authz-1:/]\n"
284 "* = rw\n")
286 run_sync(dest_sbox.repo_url)
288 lambda_url = dest_sbox.repo_url + '/A/B/lambda'
290 # this file should have been blocked by authz
291 svntest.actions.run_and_verify_svn(None,
292 [], svntest.verify.AnyOutput,
293 'cat',
294 lambda_url)
296 #----------------------------------------------------------------------
298 def copy_from_unreadable_dir(sbox):
299 "verify that copies from unreadable dirs work"
301 sbox.build("svnsync-copy-from-unreadable-dir")
303 B_url = sbox.repo_url + '/A/B'
304 P_url = sbox.repo_url + '/A/P'
306 # Set a property on the directory we're going to copy, and a file in it, to
307 # confirm that they're transmitted when we later sync the copied directory
308 svntest.actions.run_and_verify_svn(None,
309 None,
311 'pset',
312 'foo',
313 'bar',
314 sbox.wc_dir + '/A/B/lambda')
316 svntest.actions.run_and_verify_svn(None,
317 None,
319 'pset',
320 'baz',
321 'zot',
322 sbox.wc_dir + '/A/B')
324 svntest.actions.run_and_verify_svn(None,
325 None,
327 'ci',
328 sbox.wc_dir + '/A/B',
329 '-m', 'log_msg')
331 # Now copy that directory so we'll see it in our synced copy
332 svntest.actions.run_and_verify_svn(None,
333 None,
335 'cp',
336 B_url,
337 P_url,
338 '-m', 'Copy B to P')
340 write_restrictive_svnserve_conf(sbox.repo_dir)
342 dest_sbox = sbox.clone_dependent()
343 build_repos(dest_sbox)
345 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
347 fp = open(sbox.authz_file, 'w')
349 # For mod_dav_svn's parent path setup we need per-repos permissions in
350 # the authz file...
351 if sbox.repo_url.startswith('http'):
352 fp.write("[svnsync-copy-from-unreadable-dir:/]\n" +
353 "* = r\n" +
354 "\n" +
355 "[svnsync-copy-from-unreadable-dir:/A/B]\n" +
356 "* = \n" +
357 "\n" +
358 "[svnsync-copy-from-unreadable-dir-1:/]\n" +
359 "* = rw")
361 # Otherwise we can just go with the permissions needed for the source
362 # repository.
363 else:
364 fp.write("[/]\n" +
365 "* = r\n" +
366 "\n" +
367 "[/A/B]\n" +
368 "* =\n")
369 fp.close()
371 run_init(dest_sbox.repo_url, sbox.repo_url)
373 run_sync(dest_sbox.repo_url)
375 expected_out = [
376 'Changed paths:\n',
377 ' A /A/P\n',
378 ' A /A/P/E\n',
379 ' A /A/P/E/alpha\n',
380 ' A /A/P/E/beta\n',
381 ' A /A/P/F\n',
382 ' A /A/P/lambda\n',
383 '\n',
384 '\n', # log message is stripped
387 out, err = svntest.main.run_svn(None,
388 'log',
389 '-r', '3',
390 '-v',
391 dest_sbox.repo_url)
393 if err:
394 raise SVNUnexpectedStderr(err)
396 svntest.verify.compare_and_display_lines(None,
397 'LOG',
398 expected_out,
399 out[2:11])
401 svntest.actions.run_and_verify_svn(None,
402 ['bar\n'],
404 'pget',
405 'foo',
406 dest_sbox.repo_url + '/A/P/lambda')
408 svntest.actions.run_and_verify_svn(None,
409 ['zot\n'],
411 'pget',
412 'baz',
413 dest_sbox.repo_url + '/A/P')
415 # Issue 2705.
416 def copy_with_mod_from_unreadable_dir(sbox):
417 "verify copies with mods from unreadable dirs"
419 sbox.build("svnsync-copy-with-mod-from-unreadable-dir")
421 # Make a copy of the B directory.
422 svntest.actions.run_and_verify_svn(None,
423 None,
425 'cp',
426 sbox.wc_dir + '/A/B',
427 sbox.wc_dir + '/A/P')
429 # Set a property inside the copied directory.
430 svntest.actions.run_and_verify_svn(None,
431 None,
433 'pset',
434 'foo',
435 'bar',
436 sbox.wc_dir + '/A/P/lambda')
438 # Add a new directory and file inside the copied directory.
439 svntest.actions.run_and_verify_svn(None,
440 None,
442 'mkdir',
443 sbox.wc_dir + '/A/P/NEW-DIR')
445 svntest.main.file_append(sbox.wc_dir + '/A/P/E/new-file', "bla bla")
446 svntest.main.run_svn(None, 'add', sbox.wc_dir + '/A/P/E/new-file')
448 # Delete a file inside the copied directory.
449 svntest.actions.run_and_verify_svn(None,
450 None,
452 'rm',
453 sbox.wc_dir + '/A/P/E/beta')
455 # Commit the copy-with-modification.
456 svntest.actions.run_and_verify_svn(None,
457 None,
459 'ci',
460 sbox.wc_dir,
461 '-m', 'log_msg')
463 # Lock down the source repository.
464 write_restrictive_svnserve_conf(sbox.repo_dir)
466 dest_sbox = sbox.clone_dependent()
467 build_repos(dest_sbox)
469 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
471 fp = open(sbox.authz_file, 'w')
473 # For mod_dav_svn's parent path setup we need per-repos permissions in
474 # the authz file...
475 if sbox.repo_url.startswith('http'):
476 fp.write("[svnsync-copy-with-mod-from-unreadable-dir:/]\n" +
477 "* = r\n" +
478 "\n" +
479 "[svnsync-copy-with-mod-from-unreadable-dir:/A/B]\n" +
480 "* = \n" +
481 "\n" +
482 "[svnsync-copy-with-mod-from-unreadable-dir-1:/]\n" +
483 "* = rw")
485 # Otherwise we can just go with the permissions needed for the source
486 # repository.
487 else:
488 fp.write("[/]\n" +
489 "* = r\n" +
490 "\n" +
491 "[/A/B]\n" +
492 "* =\n")
493 fp.close()
495 run_init(dest_sbox.repo_url, sbox.repo_url)
497 run_sync(dest_sbox.repo_url)
499 expected_out = [
500 'Changed paths:\n',
501 ' A /A/P\n',
502 ' A /A/P/E\n',
503 ' A /A/P/E/alpha\n',
504 ' A /A/P/E/new-file\n',
505 ' A /A/P/F\n',
506 ' A /A/P/NEW-DIR\n',
507 ' A /A/P/lambda\n',
508 '\n',
509 '\n', # log message is stripped
512 out, err = svntest.main.run_svn(None,
513 'log',
514 '-r', '2',
515 '-v',
516 dest_sbox.repo_url)
518 if err:
519 raise SVNUnexpectedStderr(err)
521 svntest.verify.compare_and_display_lines(None,
522 'LOG',
523 expected_out,
524 out[2:12])
526 svntest.actions.run_and_verify_svn(None,
527 ['bar\n'],
529 'pget',
530 'foo',
531 dest_sbox.repo_url + '/A/P/lambda')
533 # Issue 2705.
534 def copy_with_mod_from_unreadable_dir_and_copy(sbox):
535 "verify copies with mods from unreadable dirs +copy"
537 sbox.build("svnsync-copy-with-mod-from-unreadable-dir-and-copy")
539 # Make a copy of the B directory.
540 svntest.actions.run_and_verify_svn(None,
541 None,
543 'cp',
544 sbox.wc_dir + '/A/B',
545 sbox.wc_dir + '/A/P')
548 # Copy a (readable) file into the copied directory.
549 svntest.actions.run_and_verify_svn(None,
550 None,
552 'cp',
553 sbox.wc_dir + '/A/D/gamma',
554 sbox.wc_dir + '/A/P/E')
557 # Commit the copy-with-modification.
558 svntest.actions.run_and_verify_svn(None,
559 None,
561 'ci',
562 sbox.wc_dir,
563 '-m', 'log_msg')
565 # Lock down the source repository.
566 write_restrictive_svnserve_conf(sbox.repo_dir)
568 dest_sbox = sbox.clone_dependent()
569 build_repos(dest_sbox)
571 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
573 fp = open(sbox.authz_file, 'w')
575 # For mod_dav_svn's parent path setup we need per-repos permissions in
576 # the authz file...
577 if sbox.repo_url.startswith('http'):
578 fp.write("[svnsync-copy-with-mod-from-unreadable-dir-and-copy:/]\n" +
579 "* = r\n" +
580 "\n" +
581 "[svnsync-copy-with-mod-from-unreadable-dir-and-copy:/A/B]\n" +
582 "* = \n" +
583 "\n" +
584 "[svnsync-copy-with-mod-from-unreadable-dir-and-copy-1:/]\n" +
585 "* = rw")
587 # Otherwise we can just go with the permissions needed for the source
588 # repository.
589 else:
590 fp.write("[/]\n" +
591 "* = r\n" +
592 "\n" +
593 "[/A/B]\n" +
594 "* =\n")
595 fp.close()
597 run_init(dest_sbox.repo_url, sbox.repo_url)
599 run_sync(dest_sbox.repo_url)
601 expected_out = [
602 'Changed paths:\n',
603 ' A /A/P\n',
604 ' A /A/P/E\n',
605 ' A /A/P/E/alpha\n',
606 ' A /A/P/E/beta\n',
607 ' A /A/P/E/gamma (from /A/D/gamma:1)\n',
608 ' A /A/P/F\n',
609 ' A /A/P/lambda\n',
610 '\n',
611 '\n', # log message is stripped
614 out, err = svntest.main.run_svn(None,
615 'log',
616 '-r', '2',
617 '-v',
618 dest_sbox.repo_url)
620 if err:
621 raise SVNUnexpectedStderr(err)
623 svntest.verify.compare_and_display_lines(None,
624 'LOG',
625 expected_out,
626 out[2:12])
628 def url_encoding(sbox):
629 "test url encoding issues"
630 run_test(sbox, "url-encoding-bug.dump")
633 # A test for copying revisions that lack a property that already exists
634 # on the destination rev as part of the commit (i.e. svn:author in this
635 # case, but svn:date would also work).
636 def no_author(sbox):
637 "test copying revs with no svn:author revprops"
638 run_test(sbox, "no-author.dump")
640 def copy_revprops(sbox):
641 "test copying revprops other than svn:*"
642 run_test(sbox, "revprops.dump")
644 def only_trunk(sbox):
645 "test syncing subdirectories"
646 run_test(sbox, "svnsync-trunk-only.dump", "/trunk",
647 "svnsync-trunk-only.expected.dump")
649 def only_trunk_A_with_changes(sbox):
650 "test syncing subdirectories with changes on root"
651 run_test(sbox, "svnsync-trunk-A-changes.dump", "/trunk/A",
652 "svnsync-trunk-A-changes.expected.dump")
654 # test for issue #2904
655 def move_and_modify_in_the_same_revision(sbox):
656 "test move parent and modify child file in same rev"
657 run_test(sbox, "svnsync-move-and-modify.dump")
659 ########################################################################
660 # Run the tests
663 # list all tests here, starting with None:
664 test_list = [ None,
665 copy_and_modify,
666 copy_from_previous_version_and_modify,
667 copy_from_previous_version,
668 modified_in_place,
669 tag_empty_trunk,
670 tag_trunk_with_dir,
671 tag_trunk_with_file2,
672 tag_trunk_with_file,
673 tag_with_modified_file,
674 dir_prop_change,
675 file_dir_file,
676 copy_parent_modify_prop,
677 detect_meddling,
678 Skip(basic_authz, svntest.main.is_ra_type_file),
679 Skip(copy_from_unreadable_dir, svntest.main.is_ra_type_file),
680 Skip(copy_with_mod_from_unreadable_dir,
681 svntest.main.is_ra_type_file),
682 Skip(copy_with_mod_from_unreadable_dir_and_copy,
683 svntest.main.is_ra_type_file),
684 url_encoding,
685 no_author,
686 copy_revprops,
687 only_trunk,
688 only_trunk_A_with_changes,
689 move_and_modify_in_the_same_revision,
692 if __name__ == '__main__':
693 svntest.main.run_tests(test_list, serial_only = True)
694 # NOTREACHED
697 ### End of file.