Skip a test when run against old servers.
[svn.git] / subversion / tests / cmdline / svnsync_tests.py
blob92b6ba39c37b01fc8443ed0befaff8fd3732a451
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 exit_code, 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_copy_revprops(url, expected_error=None):
69 "Copy revprops to the mirror repository from the master"
70 exit_code, output, errput = svntest.main.run_svnsync(
71 "copy-revprops", url,
72 "--username", svntest.main.wc_author,
73 "--password", svntest.main.wc_passwd)
74 if errput:
75 if expected_error is None:
76 raise SVNUnexpectedStderr(errput)
77 else:
78 expected_error = svntest.verify.RegexOutput(expected_error,
79 match_all=False)
80 svntest.verify.compare_and_display_lines(None, "STDERR",
81 expected_error, errput)
82 elif expected_error is not None:
83 raise SVNExpectedStderr
84 if not output and not expected_error:
85 # should be: ['Copied properties for revision 1.\n',
86 # 'Copied properties for revision 2.\n']
87 raise SVNUnexpectedStdout("Missing stdout")
89 def run_init(dst_url, src_url):
90 "Initialize the mirror repository from the master"
91 exit_code, output, errput = svntest.main.run_svnsync(
92 "initialize", dst_url, src_url,
93 "--username", svntest.main.wc_author,
94 "--password", svntest.main.wc_passwd)
95 if errput:
96 raise SVNUnexpectedStderr(errput)
97 if output != ['Copied properties for revision 0.\n']:
98 raise SVNUnexpectedStdout(output)
101 def run_test(sbox, dump_file_name, subdir = None, exp_dump_file_name = None):
102 """Load a dump file, sync repositories, and compare contents with the original
103 or another dump file."""
105 # Create the empty master repository.
106 build_repos(sbox)
108 # This directory contains all the dump files
109 svnsync_tests_dir = os.path.join(os.path.dirname(sys.argv[0]),
110 'svnsync_tests_data')
111 # Load the specified dump file into the master repository.
112 master_dumpfile_contents = file(os.path.join(svnsync_tests_dir,
113 dump_file_name)).readlines()
114 svntest.actions.run_and_verify_load(sbox.repo_dir, master_dumpfile_contents)
116 # Create the empty destination repository.
117 dest_sbox = sbox.clone_dependent()
118 build_repos(dest_sbox)
120 # Setup the mirror repository. Feed it the UUID of the source repository.
121 exit_code, output, errput = svntest.main.run_svnlook("uuid", sbox.repo_dir)
122 mirror_cfg = ["SVN-fs-dump-format-version: 2\n",
123 "UUID: " + output[0],
125 svntest.actions.run_and_verify_load(dest_sbox.repo_dir, mirror_cfg)
127 # Create the revprop-change hook for this test
128 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
130 repo_url = sbox.repo_url
131 if subdir:
132 repo_url = repo_url + subdir
133 run_init(dest_sbox.repo_url, repo_url)
135 run_sync(dest_sbox.repo_url)
136 run_copy_revprops(dest_sbox.repo_url)
138 # Remove some SVNSync-specific housekeeping properties from the
139 # mirror repository in preparation for the comparison dump.
140 for prop_name in ("svn:sync-from-url", "svn:sync-from-uuid",
141 "svn:sync-last-merged-rev"):
142 svntest.actions.run_and_verify_svn(
143 None, None, [], "propdel", "--revprop", "-r", "0",
144 prop_name, dest_sbox.repo_url)
146 # Create a dump file from the mirror repository.
147 dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
149 # Compare the dump produced by the mirror repository with either the original
150 # dump file (used to create the master repository) or another specified dump
151 # file.
152 if exp_dump_file_name:
153 exp_master_dumpfile_contents = file(os.path.join(svnsync_tests_dir,
154 exp_dump_file_name)).readlines()
155 else:
156 exp_master_dumpfile_contents = master_dumpfile_contents
158 svntest.verify.compare_and_display_lines(
159 "Dump files", "DUMP", exp_master_dumpfile_contents, dest_dump)
162 ######################################################################
163 # Tests
165 #----------------------------------------------------------------------
167 def copy_and_modify(sbox):
168 "copy and modify"
169 run_test(sbox, "copy-and-modify.dump")
171 #----------------------------------------------------------------------
173 def copy_from_previous_version_and_modify(sbox):
174 "copy from previous version and modify"
175 run_test(sbox, "copy-from-previous-version-and-modify.dump")
177 #----------------------------------------------------------------------
179 def copy_from_previous_version(sbox):
180 "copy from previous version"
181 run_test(sbox, "copy-from-previous-version.dump")
183 #----------------------------------------------------------------------
185 def modified_in_place(sbox):
186 "modified in place"
187 run_test(sbox, "modified-in-place.dump")
189 #----------------------------------------------------------------------
191 def tag_empty_trunk(sbox):
192 "tag empty trunk"
193 run_test(sbox, "tag-empty-trunk.dump")
195 #----------------------------------------------------------------------
197 def tag_trunk_with_dir(sbox):
198 "tag trunk containing a sub-directory"
199 run_test(sbox, "tag-trunk-with-dir.dump")
201 #----------------------------------------------------------------------
203 def tag_trunk_with_file(sbox):
204 "tag trunk containing a file"
205 run_test(sbox, "tag-trunk-with-file.dump")
207 #----------------------------------------------------------------------
209 def tag_trunk_with_file2(sbox):
210 "tag trunk containing a file (#2)"
211 run_test(sbox, "tag-trunk-with-file2.dump")
213 #----------------------------------------------------------------------
215 def tag_with_modified_file(sbox):
216 "tag with a modified file"
217 run_test(sbox, "tag-with-modified-file.dump")
219 #----------------------------------------------------------------------
221 def dir_prop_change(sbox):
222 "directory property changes"
223 run_test(sbox, "dir_prop_change.dump")
225 #----------------------------------------------------------------------
227 def file_dir_file(sbox):
228 "files and dirs mixed together"
229 run_test(sbox, "file-dir-file.dump")
231 #----------------------------------------------------------------------
233 def copy_parent_modify_prop(sbox):
234 "copy parent and modify prop"
235 run_test(sbox, "copy-parent-modify-prop.dump")
237 #----------------------------------------------------------------------
239 def detect_meddling(sbox):
240 "detect non-svnsync commits in destination"
242 sbox.build("svnsync-meddling")
244 dest_sbox = sbox.clone_dependent()
245 build_repos(dest_sbox)
247 # Make our own destination checkout (have to do it ourself because
248 # it is not greek).
250 svntest.main.safe_rmtree(dest_sbox.wc_dir)
251 svntest.actions.run_and_verify_svn(None,
252 None,
254 'co',
255 dest_sbox.repo_url,
256 dest_sbox.wc_dir)
258 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
260 run_init(dest_sbox.repo_url, sbox.repo_url)
261 run_sync(dest_sbox.repo_url)
263 svntest.actions.run_and_verify_svn(None,
264 None,
266 'up',
267 dest_sbox.wc_dir)
269 # Commit some change to the destination, which should be detected by svnsync
270 svntest.main.file_append(os.path.join(dest_sbox.wc_dir, 'A', 'B', 'lambda'),
271 'new lambda text')
272 svntest.actions.run_and_verify_svn(None,
273 None,
275 'ci',
276 '-m', 'msg',
277 dest_sbox.wc_dir)
279 run_sync(dest_sbox.repo_url,
280 ".*Destination HEAD \\(2\\) is not the last merged revision \\(1\\).*")
282 #----------------------------------------------------------------------
284 def basic_authz(sbox):
285 "verify that unreadable content is not synced"
287 sbox.build("svnsync-basic-authz")
289 write_restrictive_svnserve_conf(sbox.repo_dir)
291 dest_sbox = sbox.clone_dependent()
292 build_repos(dest_sbox)
294 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
296 run_init(dest_sbox.repo_url, sbox.repo_url)
298 svntest.main.file_write(sbox.authz_file,
299 "[svnsync-basic-authz:/]\n"
300 "* = r\n"
301 "\n"
302 "[svnsync-basic-authz:/A/B]\n"
303 "* = \n"
304 "\n"
305 "[svnsync-basic-authz-1:/]\n"
306 "* = rw\n")
308 run_sync(dest_sbox.repo_url)
310 lambda_url = dest_sbox.repo_url + '/A/B/lambda'
312 # this file should have been blocked by authz
313 svntest.actions.run_and_verify_svn(None,
314 [], svntest.verify.AnyOutput,
315 'cat',
316 lambda_url)
318 #----------------------------------------------------------------------
320 def copy_from_unreadable_dir(sbox):
321 "verify that copies from unreadable dirs work"
323 sbox.build("svnsync-copy-from-unreadable-dir")
325 B_url = sbox.repo_url + '/A/B'
326 P_url = sbox.repo_url + '/A/P'
328 # Set a property on the directory we're going to copy, and a file in it, to
329 # confirm that they're transmitted when we later sync the copied directory
330 svntest.actions.run_and_verify_svn(None,
331 None,
333 'pset',
334 'foo',
335 'bar',
336 sbox.wc_dir + '/A/B/lambda')
338 svntest.actions.run_and_verify_svn(None,
339 None,
341 'pset',
342 'baz',
343 'zot',
344 sbox.wc_dir + '/A/B')
346 svntest.actions.run_and_verify_svn(None,
347 None,
349 'ci',
350 sbox.wc_dir + '/A/B',
351 '-m', 'log_msg')
353 # Now copy that directory so we'll see it in our synced copy
354 svntest.actions.run_and_verify_svn(None,
355 None,
357 'cp',
358 B_url,
359 P_url,
360 '-m', 'Copy B to P')
362 write_restrictive_svnserve_conf(sbox.repo_dir)
364 dest_sbox = sbox.clone_dependent()
365 build_repos(dest_sbox)
367 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
369 fp = open(sbox.authz_file, 'w')
371 # For mod_dav_svn's parent path setup we need per-repos permissions in
372 # the authz file...
373 if sbox.repo_url.startswith('http'):
374 fp.write("[svnsync-copy-from-unreadable-dir:/]\n" +
375 "* = r\n" +
376 "\n" +
377 "[svnsync-copy-from-unreadable-dir:/A/B]\n" +
378 "* = \n" +
379 "\n" +
380 "[svnsync-copy-from-unreadable-dir-1:/]\n" +
381 "* = rw")
383 # Otherwise we can just go with the permissions needed for the source
384 # repository.
385 else:
386 fp.write("[/]\n" +
387 "* = r\n" +
388 "\n" +
389 "[/A/B]\n" +
390 "* =\n")
391 fp.close()
393 run_init(dest_sbox.repo_url, sbox.repo_url)
395 run_sync(dest_sbox.repo_url)
397 expected_out = [
398 'Changed paths:\n',
399 ' A /A/P\n',
400 ' A /A/P/E\n',
401 ' A /A/P/E/alpha\n',
402 ' A /A/P/E/beta\n',
403 ' A /A/P/F\n',
404 ' A /A/P/lambda\n',
405 '\n',
406 '\n', # log message is stripped
409 exit_code, out, err = svntest.main.run_svn(None,
410 'log',
411 '-r', '3',
412 '-v',
413 dest_sbox.repo_url)
415 if err:
416 raise SVNUnexpectedStderr(err)
418 svntest.verify.compare_and_display_lines(None,
419 'LOG',
420 expected_out,
421 out[2:11])
423 svntest.actions.run_and_verify_svn(None,
424 ['bar\n'],
426 'pget',
427 'foo',
428 dest_sbox.repo_url + '/A/P/lambda')
430 svntest.actions.run_and_verify_svn(None,
431 ['zot\n'],
433 'pget',
434 'baz',
435 dest_sbox.repo_url + '/A/P')
437 # Issue 2705.
438 def copy_with_mod_from_unreadable_dir(sbox):
439 "verify copies with mods from unreadable dirs"
441 sbox.build("svnsync-copy-with-mod-from-unreadable-dir")
443 # Make a copy of the B directory.
444 svntest.actions.run_and_verify_svn(None,
445 None,
447 'cp',
448 sbox.wc_dir + '/A/B',
449 sbox.wc_dir + '/A/P')
451 # Set a property inside the copied directory.
452 svntest.actions.run_and_verify_svn(None,
453 None,
455 'pset',
456 'foo',
457 'bar',
458 sbox.wc_dir + '/A/P/lambda')
460 # Add a new directory and file inside the copied directory.
461 svntest.actions.run_and_verify_svn(None,
462 None,
464 'mkdir',
465 sbox.wc_dir + '/A/P/NEW-DIR')
467 svntest.main.file_append(sbox.wc_dir + '/A/P/E/new-file', "bla bla")
468 svntest.main.run_svn(None, 'add', sbox.wc_dir + '/A/P/E/new-file')
470 # Delete a file inside the copied directory.
471 svntest.actions.run_and_verify_svn(None,
472 None,
474 'rm',
475 sbox.wc_dir + '/A/P/E/beta')
477 # Commit the copy-with-modification.
478 svntest.actions.run_and_verify_svn(None,
479 None,
481 'ci',
482 sbox.wc_dir,
483 '-m', 'log_msg')
485 # Lock down the source repository.
486 write_restrictive_svnserve_conf(sbox.repo_dir)
488 dest_sbox = sbox.clone_dependent()
489 build_repos(dest_sbox)
491 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
493 fp = open(sbox.authz_file, 'w')
495 # For mod_dav_svn's parent path setup we need per-repos permissions in
496 # the authz file...
497 if sbox.repo_url.startswith('http'):
498 fp.write("[svnsync-copy-with-mod-from-unreadable-dir:/]\n" +
499 "* = r\n" +
500 "\n" +
501 "[svnsync-copy-with-mod-from-unreadable-dir:/A/B]\n" +
502 "* = \n" +
503 "\n" +
504 "[svnsync-copy-with-mod-from-unreadable-dir-1:/]\n" +
505 "* = rw")
507 # Otherwise we can just go with the permissions needed for the source
508 # repository.
509 else:
510 fp.write("[/]\n" +
511 "* = r\n" +
512 "\n" +
513 "[/A/B]\n" +
514 "* =\n")
515 fp.close()
517 run_init(dest_sbox.repo_url, sbox.repo_url)
519 run_sync(dest_sbox.repo_url)
521 expected_out = [
522 'Changed paths:\n',
523 ' A /A/P\n',
524 ' A /A/P/E\n',
525 ' A /A/P/E/alpha\n',
526 ' A /A/P/E/new-file\n',
527 ' A /A/P/F\n',
528 ' A /A/P/NEW-DIR\n',
529 ' A /A/P/lambda\n',
530 '\n',
531 '\n', # log message is stripped
534 exit_code, out, err = svntest.main.run_svn(None,
535 'log',
536 '-r', '2',
537 '-v',
538 dest_sbox.repo_url)
540 if err:
541 raise SVNUnexpectedStderr(err)
543 svntest.verify.compare_and_display_lines(None,
544 'LOG',
545 expected_out,
546 out[2:12])
548 svntest.actions.run_and_verify_svn(None,
549 ['bar\n'],
551 'pget',
552 'foo',
553 dest_sbox.repo_url + '/A/P/lambda')
555 # Issue 2705.
556 def copy_with_mod_from_unreadable_dir_and_copy(sbox):
557 "verify copies with mods from unreadable dirs +copy"
559 sbox.build("svnsync-copy-with-mod-from-unreadable-dir-and-copy")
561 # Make a copy of the B directory.
562 svntest.actions.run_and_verify_svn(None,
563 None,
565 'cp',
566 sbox.wc_dir + '/A/B',
567 sbox.wc_dir + '/A/P')
570 # Copy a (readable) file into the copied directory.
571 svntest.actions.run_and_verify_svn(None,
572 None,
574 'cp',
575 sbox.wc_dir + '/A/D/gamma',
576 sbox.wc_dir + '/A/P/E')
579 # Commit the copy-with-modification.
580 svntest.actions.run_and_verify_svn(None,
581 None,
583 'ci',
584 sbox.wc_dir,
585 '-m', 'log_msg')
587 # Lock down the source repository.
588 write_restrictive_svnserve_conf(sbox.repo_dir)
590 dest_sbox = sbox.clone_dependent()
591 build_repos(dest_sbox)
593 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
595 fp = open(sbox.authz_file, 'w')
597 # For mod_dav_svn's parent path setup we need per-repos permissions in
598 # the authz file...
599 if sbox.repo_url.startswith('http'):
600 fp.write("[svnsync-copy-with-mod-from-unreadable-dir-and-copy:/]\n" +
601 "* = r\n" +
602 "\n" +
603 "[svnsync-copy-with-mod-from-unreadable-dir-and-copy:/A/B]\n" +
604 "* = \n" +
605 "\n" +
606 "[svnsync-copy-with-mod-from-unreadable-dir-and-copy-1:/]\n" +
607 "* = rw")
609 # Otherwise we can just go with the permissions needed for the source
610 # repository.
611 else:
612 fp.write("[/]\n" +
613 "* = r\n" +
614 "\n" +
615 "[/A/B]\n" +
616 "* =\n")
617 fp.close()
619 run_init(dest_sbox.repo_url, sbox.repo_url)
621 run_sync(dest_sbox.repo_url)
623 expected_out = [
624 'Changed paths:\n',
625 ' A /A/P\n',
626 ' A /A/P/E\n',
627 ' A /A/P/E/alpha\n',
628 ' A /A/P/E/beta\n',
629 ' A /A/P/E/gamma (from /A/D/gamma:1)\n',
630 ' A /A/P/F\n',
631 ' A /A/P/lambda\n',
632 '\n',
633 '\n', # log message is stripped
636 exit_code, out, err = svntest.main.run_svn(None,
637 'log',
638 '-r', '2',
639 '-v',
640 dest_sbox.repo_url)
642 if err:
643 raise SVNUnexpectedStderr(err)
645 svntest.verify.compare_and_display_lines(None,
646 'LOG',
647 expected_out,
648 out[2:12])
650 def url_encoding(sbox):
651 "test url encoding issues"
652 run_test(sbox, "url-encoding-bug.dump")
655 # A test for copying revisions that lack a property that already exists
656 # on the destination rev as part of the commit (i.e. svn:author in this
657 # case, but svn:date would also work).
658 def no_author(sbox):
659 "test copying revs with no svn:author revprops"
660 run_test(sbox, "no-author.dump")
662 def copy_revprops(sbox):
663 "test copying revprops other than svn:*"
664 run_test(sbox, "revprops.dump")
666 def only_trunk(sbox):
667 "test syncing subdirectories"
668 run_test(sbox, "svnsync-trunk-only.dump", "/trunk",
669 "svnsync-trunk-only.expected.dump")
671 def only_trunk_A_with_changes(sbox):
672 "test syncing subdirectories with changes on root"
673 run_test(sbox, "svnsync-trunk-A-changes.dump", "/trunk/A",
674 "svnsync-trunk-A-changes.expected.dump")
676 # test for issue #2904
677 def move_and_modify_in_the_same_revision(sbox):
678 "test move parent and modify child file in same rev"
679 run_test(sbox, "svnsync-move-and-modify.dump")
681 ########################################################################
682 # Run the tests
685 # list all tests here, starting with None:
686 test_list = [ None,
687 copy_and_modify,
688 copy_from_previous_version_and_modify,
689 copy_from_previous_version,
690 modified_in_place,
691 tag_empty_trunk,
692 tag_trunk_with_dir,
693 tag_trunk_with_file2,
694 tag_trunk_with_file,
695 tag_with_modified_file,
696 dir_prop_change,
697 file_dir_file,
698 copy_parent_modify_prop,
699 detect_meddling,
700 Skip(basic_authz, svntest.main.is_ra_type_file),
701 Skip(copy_from_unreadable_dir, svntest.main.is_ra_type_file),
702 Skip(copy_with_mod_from_unreadable_dir,
703 svntest.main.is_ra_type_file),
704 Skip(copy_with_mod_from_unreadable_dir_and_copy,
705 svntest.main.is_ra_type_file),
706 url_encoding,
707 no_author,
708 copy_revprops,
709 only_trunk,
710 only_trunk_A_with_changes,
711 move_and_modify_in_the_same_revision,
714 if __name__ == '__main__':
715 svntest.main.run_tests(test_list, serial_only = True)
716 # NOTREACHED
719 ### End of file.