Fix compiler warning due to missing function prototype.
[svn.git] / subversion / tests / cmdline / svnsync_tests.py
bloba3f6366f2956632f20bec39a0e17b48b1d168470
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
27 from svntest.main import server_has_partial_replay
29 # (abbreviation)
30 Skip = svntest.testcase.Skip
31 SkipUnless = svntest.testcase.SkipUnless
32 XFail = svntest.testcase.XFail
33 Item = svntest.wc.StateItem
36 ######################################################################
37 # Helper routines
40 def build_repos(sbox):
41 """Avoid the use sbox.build() because we're working with a repos
42 other than the Greek tree."""
43 # Cleanup after the last run by removing any left-over repository.
44 svntest.main.safe_rmtree(sbox.repo_dir)
46 # Create an empty repository.
47 svntest.main.create_repos(sbox.repo_dir)
50 def run_sync(url, expected_error=None):
51 "Synchronize the mirror repository with the master"
52 exit_code, output, errput = svntest.main.run_svnsync(
53 "synchronize", url,
54 "--username", svntest.main.wc_author,
55 "--password", svntest.main.wc_passwd)
56 if errput:
57 if expected_error is None:
58 raise SVNUnexpectedStderr(errput)
59 else:
60 expected_error = svntest.verify.RegexOutput(expected_error,
61 match_all=False)
62 svntest.verify.compare_and_display_lines(None, "STDERR",
63 expected_error, errput)
64 elif expected_error is not None:
65 raise SVNExpectedStderr
66 if not output and not expected_error:
67 # should be: ['Committed revision 1.\n', 'Committed revision 2.\n']
68 raise SVNUnexpectedStdout("Missing stdout")
70 def run_copy_revprops(url, expected_error=None):
71 "Copy revprops to the mirror repository from the master"
72 exit_code, output, errput = svntest.main.run_svnsync(
73 "copy-revprops", url,
74 "--username", svntest.main.wc_author,
75 "--password", svntest.main.wc_passwd)
76 if errput:
77 if expected_error is None:
78 raise SVNUnexpectedStderr(errput)
79 else:
80 expected_error = svntest.verify.RegexOutput(expected_error,
81 match_all=False)
82 svntest.verify.compare_and_display_lines(None, "STDERR",
83 expected_error, errput)
84 elif expected_error is not None:
85 raise SVNExpectedStderr
86 if not output and not expected_error:
87 # should be: ['Copied properties for revision 1.\n',
88 # 'Copied properties for revision 2.\n']
89 raise SVNUnexpectedStdout("Missing stdout")
91 def run_init(dst_url, src_url):
92 "Initialize the mirror repository from the master"
93 exit_code, output, errput = svntest.main.run_svnsync(
94 "initialize", dst_url, src_url,
95 "--username", svntest.main.wc_author,
96 "--password", svntest.main.wc_passwd)
97 if errput:
98 raise SVNUnexpectedStderr(errput)
99 if output != ['Copied properties for revision 0.\n']:
100 raise SVNUnexpectedStdout(output)
103 def run_test(sbox, dump_file_name, subdir = None, exp_dump_file_name = None):
104 """Load a dump file, sync repositories, and compare contents with the original
105 or another dump file."""
107 # Create the empty master repository.
108 build_repos(sbox)
110 # This directory contains all the dump files
111 svnsync_tests_dir = os.path.join(os.path.dirname(sys.argv[0]),
112 'svnsync_tests_data')
113 # Load the specified dump file into the master repository.
114 master_dumpfile_contents = file(os.path.join(svnsync_tests_dir,
115 dump_file_name)).readlines()
116 svntest.actions.run_and_verify_load(sbox.repo_dir, master_dumpfile_contents)
118 # Create the empty destination repository.
119 dest_sbox = sbox.clone_dependent()
120 build_repos(dest_sbox)
122 # Setup the mirror repository. Feed it the UUID of the source repository.
123 exit_code, output, errput = svntest.main.run_svnlook("uuid", sbox.repo_dir)
124 mirror_cfg = ["SVN-fs-dump-format-version: 2\n",
125 "UUID: " + output[0],
127 svntest.actions.run_and_verify_load(dest_sbox.repo_dir, mirror_cfg)
129 # Create the revprop-change hook for this test
130 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
132 repo_url = sbox.repo_url
133 if subdir:
134 repo_url = repo_url + subdir
135 run_init(dest_sbox.repo_url, repo_url)
137 run_sync(dest_sbox.repo_url)
138 run_copy_revprops(dest_sbox.repo_url)
140 # Remove some SVNSync-specific housekeeping properties from the
141 # mirror repository in preparation for the comparison dump.
142 for prop_name in ("svn:sync-from-url", "svn:sync-from-uuid",
143 "svn:sync-last-merged-rev"):
144 svntest.actions.run_and_verify_svn(
145 None, None, [], "propdel", "--revprop", "-r", "0",
146 prop_name, dest_sbox.repo_url)
148 # Create a dump file from the mirror repository.
149 dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
151 # Compare the dump produced by the mirror repository with either the original
152 # dump file (used to create the master repository) or another specified dump
153 # file.
154 if exp_dump_file_name:
155 exp_master_dumpfile_contents = file(os.path.join(svnsync_tests_dir,
156 exp_dump_file_name)).readlines()
157 else:
158 exp_master_dumpfile_contents = master_dumpfile_contents
160 svntest.verify.compare_and_display_lines(
161 "Dump files", "DUMP", exp_master_dumpfile_contents, dest_dump)
164 ######################################################################
165 # Tests
167 #----------------------------------------------------------------------
169 def copy_and_modify(sbox):
170 "copy and modify"
171 run_test(sbox, "copy-and-modify.dump")
173 #----------------------------------------------------------------------
175 def copy_from_previous_version_and_modify(sbox):
176 "copy from previous version and modify"
177 run_test(sbox, "copy-from-previous-version-and-modify.dump")
179 #----------------------------------------------------------------------
181 def copy_from_previous_version(sbox):
182 "copy from previous version"
183 run_test(sbox, "copy-from-previous-version.dump")
185 #----------------------------------------------------------------------
187 def modified_in_place(sbox):
188 "modified in place"
189 run_test(sbox, "modified-in-place.dump")
191 #----------------------------------------------------------------------
193 def tag_empty_trunk(sbox):
194 "tag empty trunk"
195 run_test(sbox, "tag-empty-trunk.dump")
197 #----------------------------------------------------------------------
199 def tag_trunk_with_dir(sbox):
200 "tag trunk containing a sub-directory"
201 run_test(sbox, "tag-trunk-with-dir.dump")
203 #----------------------------------------------------------------------
205 def tag_trunk_with_file(sbox):
206 "tag trunk containing a file"
207 run_test(sbox, "tag-trunk-with-file.dump")
209 #----------------------------------------------------------------------
211 def tag_trunk_with_file2(sbox):
212 "tag trunk containing a file (#2)"
213 run_test(sbox, "tag-trunk-with-file2.dump")
215 #----------------------------------------------------------------------
217 def tag_with_modified_file(sbox):
218 "tag with a modified file"
219 run_test(sbox, "tag-with-modified-file.dump")
221 #----------------------------------------------------------------------
223 def dir_prop_change(sbox):
224 "directory property changes"
225 run_test(sbox, "dir_prop_change.dump")
227 #----------------------------------------------------------------------
229 def file_dir_file(sbox):
230 "files and dirs mixed together"
231 run_test(sbox, "file-dir-file.dump")
233 #----------------------------------------------------------------------
235 def copy_parent_modify_prop(sbox):
236 "copy parent and modify prop"
237 run_test(sbox, "copy-parent-modify-prop.dump")
239 #----------------------------------------------------------------------
241 def detect_meddling(sbox):
242 "detect non-svnsync commits in destination"
244 sbox.build("svnsync-meddling")
246 dest_sbox = sbox.clone_dependent()
247 build_repos(dest_sbox)
249 # Make our own destination checkout (have to do it ourself because
250 # it is not greek).
252 svntest.main.safe_rmtree(dest_sbox.wc_dir)
253 svntest.actions.run_and_verify_svn(None,
254 None,
256 'co',
257 dest_sbox.repo_url,
258 dest_sbox.wc_dir)
260 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
262 run_init(dest_sbox.repo_url, sbox.repo_url)
263 run_sync(dest_sbox.repo_url)
265 svntest.actions.run_and_verify_svn(None,
266 None,
268 'up',
269 dest_sbox.wc_dir)
271 # Commit some change to the destination, which should be detected by svnsync
272 svntest.main.file_append(os.path.join(dest_sbox.wc_dir, 'A', 'B', 'lambda'),
273 'new lambda text')
274 svntest.actions.run_and_verify_svn(None,
275 None,
277 'ci',
278 '-m', 'msg',
279 dest_sbox.wc_dir)
281 run_sync(dest_sbox.repo_url,
282 ".*Destination HEAD \\(2\\) is not the last merged revision \\(1\\).*")
284 #----------------------------------------------------------------------
286 def basic_authz(sbox):
287 "verify that unreadable content is not synced"
289 sbox.build("svnsync-basic-authz")
291 write_restrictive_svnserve_conf(sbox.repo_dir)
293 dest_sbox = sbox.clone_dependent()
294 build_repos(dest_sbox)
296 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
298 run_init(dest_sbox.repo_url, sbox.repo_url)
300 svntest.main.file_write(sbox.authz_file,
301 "[svnsync-basic-authz:/]\n"
302 "* = r\n"
303 "\n"
304 "[svnsync-basic-authz:/A/B]\n"
305 "* = \n"
306 "\n"
307 "[svnsync-basic-authz-1:/]\n"
308 "* = rw\n")
310 run_sync(dest_sbox.repo_url)
312 lambda_url = dest_sbox.repo_url + '/A/B/lambda'
314 # this file should have been blocked by authz
315 svntest.actions.run_and_verify_svn(None,
316 [], svntest.verify.AnyOutput,
317 'cat',
318 lambda_url)
320 #----------------------------------------------------------------------
322 def copy_from_unreadable_dir(sbox):
323 "verify that copies from unreadable dirs work"
325 sbox.build("svnsync-copy-from-unreadable-dir")
327 B_url = sbox.repo_url + '/A/B'
328 P_url = sbox.repo_url + '/A/P'
330 # Set a property on the directory we're going to copy, and a file in it, to
331 # confirm that they're transmitted when we later sync the copied directory
332 svntest.actions.run_and_verify_svn(None,
333 None,
335 'pset',
336 'foo',
337 'bar',
338 sbox.wc_dir + '/A/B/lambda')
340 svntest.actions.run_and_verify_svn(None,
341 None,
343 'pset',
344 'baz',
345 'zot',
346 sbox.wc_dir + '/A/B')
348 svntest.actions.run_and_verify_svn(None,
349 None,
351 'ci',
352 sbox.wc_dir + '/A/B',
353 '-m', 'log_msg')
355 # Now copy that directory so we'll see it in our synced copy
356 svntest.actions.run_and_verify_svn(None,
357 None,
359 'cp',
360 B_url,
361 P_url,
362 '-m', 'Copy B to P')
364 write_restrictive_svnserve_conf(sbox.repo_dir)
366 dest_sbox = sbox.clone_dependent()
367 build_repos(dest_sbox)
369 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
371 fp = open(sbox.authz_file, 'w')
373 # For mod_dav_svn's parent path setup we need per-repos permissions in
374 # the authz file...
375 if sbox.repo_url.startswith('http'):
376 fp.write("[svnsync-copy-from-unreadable-dir:/]\n" +
377 "* = r\n" +
378 "\n" +
379 "[svnsync-copy-from-unreadable-dir:/A/B]\n" +
380 "* = \n" +
381 "\n" +
382 "[svnsync-copy-from-unreadable-dir-1:/]\n" +
383 "* = rw")
385 # Otherwise we can just go with the permissions needed for the source
386 # repository.
387 else:
388 fp.write("[/]\n" +
389 "* = r\n" +
390 "\n" +
391 "[/A/B]\n" +
392 "* =\n")
393 fp.close()
395 run_init(dest_sbox.repo_url, sbox.repo_url)
397 run_sync(dest_sbox.repo_url)
399 expected_out = [
400 'Changed paths:\n',
401 ' A /A/P\n',
402 ' A /A/P/E\n',
403 ' A /A/P/E/alpha\n',
404 ' A /A/P/E/beta\n',
405 ' A /A/P/F\n',
406 ' A /A/P/lambda\n',
407 '\n',
408 '\n', # log message is stripped
411 exit_code, out, err = svntest.main.run_svn(None,
412 'log',
413 '-r', '3',
414 '-v',
415 dest_sbox.repo_url)
417 if err:
418 raise SVNUnexpectedStderr(err)
420 svntest.verify.compare_and_display_lines(None,
421 'LOG',
422 expected_out,
423 out[2:11])
425 svntest.actions.run_and_verify_svn(None,
426 ['bar\n'],
428 'pget',
429 'foo',
430 dest_sbox.repo_url + '/A/P/lambda')
432 svntest.actions.run_and_verify_svn(None,
433 ['zot\n'],
435 'pget',
436 'baz',
437 dest_sbox.repo_url + '/A/P')
439 # Issue 2705.
440 def copy_with_mod_from_unreadable_dir(sbox):
441 "verify copies with mods from unreadable dirs"
443 sbox.build("svnsync-copy-with-mod-from-unreadable-dir")
445 # Make a copy of the B directory.
446 svntest.actions.run_and_verify_svn(None,
447 None,
449 'cp',
450 sbox.wc_dir + '/A/B',
451 sbox.wc_dir + '/A/P')
453 # Set a property inside the copied directory.
454 svntest.actions.run_and_verify_svn(None,
455 None,
457 'pset',
458 'foo',
459 'bar',
460 sbox.wc_dir + '/A/P/lambda')
462 # Add a new directory and file inside the copied directory.
463 svntest.actions.run_and_verify_svn(None,
464 None,
466 'mkdir',
467 sbox.wc_dir + '/A/P/NEW-DIR')
469 svntest.main.file_append(sbox.wc_dir + '/A/P/E/new-file', "bla bla")
470 svntest.main.run_svn(None, 'add', sbox.wc_dir + '/A/P/E/new-file')
472 # Delete a file inside the copied directory.
473 svntest.actions.run_and_verify_svn(None,
474 None,
476 'rm',
477 sbox.wc_dir + '/A/P/E/beta')
479 # Commit the copy-with-modification.
480 svntest.actions.run_and_verify_svn(None,
481 None,
483 'ci',
484 sbox.wc_dir,
485 '-m', 'log_msg')
487 # Lock down the source repository.
488 write_restrictive_svnserve_conf(sbox.repo_dir)
490 dest_sbox = sbox.clone_dependent()
491 build_repos(dest_sbox)
493 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
495 fp = open(sbox.authz_file, 'w')
497 # For mod_dav_svn's parent path setup we need per-repos permissions in
498 # the authz file...
499 if sbox.repo_url.startswith('http'):
500 fp.write("[svnsync-copy-with-mod-from-unreadable-dir:/]\n" +
501 "* = r\n" +
502 "\n" +
503 "[svnsync-copy-with-mod-from-unreadable-dir:/A/B]\n" +
504 "* = \n" +
505 "\n" +
506 "[svnsync-copy-with-mod-from-unreadable-dir-1:/]\n" +
507 "* = rw")
509 # Otherwise we can just go with the permissions needed for the source
510 # repository.
511 else:
512 fp.write("[/]\n" +
513 "* = r\n" +
514 "\n" +
515 "[/A/B]\n" +
516 "* =\n")
517 fp.close()
519 run_init(dest_sbox.repo_url, sbox.repo_url)
521 run_sync(dest_sbox.repo_url)
523 expected_out = [
524 'Changed paths:\n',
525 ' A /A/P\n',
526 ' A /A/P/E\n',
527 ' A /A/P/E/alpha\n',
528 ' A /A/P/E/new-file\n',
529 ' A /A/P/F\n',
530 ' A /A/P/NEW-DIR\n',
531 ' A /A/P/lambda\n',
532 '\n',
533 '\n', # log message is stripped
536 exit_code, out, err = svntest.main.run_svn(None,
537 'log',
538 '-r', '2',
539 '-v',
540 dest_sbox.repo_url)
542 if err:
543 raise SVNUnexpectedStderr(err)
545 svntest.verify.compare_and_display_lines(None,
546 'LOG',
547 expected_out,
548 out[2:12])
550 svntest.actions.run_and_verify_svn(None,
551 ['bar\n'],
553 'pget',
554 'foo',
555 dest_sbox.repo_url + '/A/P/lambda')
557 # Issue 2705.
558 def copy_with_mod_from_unreadable_dir_and_copy(sbox):
559 "verify copies with mods from unreadable dirs +copy"
561 sbox.build("svnsync-copy-with-mod-from-unreadable-dir-and-copy")
563 # Make a copy of the B directory.
564 svntest.actions.run_and_verify_svn(None,
565 None,
567 'cp',
568 sbox.wc_dir + '/A/B',
569 sbox.wc_dir + '/A/P')
572 # Copy a (readable) file into the copied directory.
573 svntest.actions.run_and_verify_svn(None,
574 None,
576 'cp',
577 sbox.wc_dir + '/A/D/gamma',
578 sbox.wc_dir + '/A/P/E')
581 # Commit the copy-with-modification.
582 svntest.actions.run_and_verify_svn(None,
583 None,
585 'ci',
586 sbox.wc_dir,
587 '-m', 'log_msg')
589 # Lock down the source repository.
590 write_restrictive_svnserve_conf(sbox.repo_dir)
592 dest_sbox = sbox.clone_dependent()
593 build_repos(dest_sbox)
595 svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
597 fp = open(sbox.authz_file, 'w')
599 # For mod_dav_svn's parent path setup we need per-repos permissions in
600 # the authz file...
601 if sbox.repo_url.startswith('http'):
602 fp.write("[svnsync-copy-with-mod-from-unreadable-dir-and-copy:/]\n" +
603 "* = r\n" +
604 "\n" +
605 "[svnsync-copy-with-mod-from-unreadable-dir-and-copy:/A/B]\n" +
606 "* = \n" +
607 "\n" +
608 "[svnsync-copy-with-mod-from-unreadable-dir-and-copy-1:/]\n" +
609 "* = rw")
611 # Otherwise we can just go with the permissions needed for the source
612 # repository.
613 else:
614 fp.write("[/]\n" +
615 "* = r\n" +
616 "\n" +
617 "[/A/B]\n" +
618 "* =\n")
619 fp.close()
621 run_init(dest_sbox.repo_url, sbox.repo_url)
623 run_sync(dest_sbox.repo_url)
625 expected_out = [
626 'Changed paths:\n',
627 ' A /A/P\n',
628 ' A /A/P/E\n',
629 ' A /A/P/E/alpha\n',
630 ' A /A/P/E/beta\n',
631 ' A /A/P/E/gamma (from /A/D/gamma:1)\n',
632 ' A /A/P/F\n',
633 ' A /A/P/lambda\n',
634 '\n',
635 '\n', # log message is stripped
638 exit_code, out, err = svntest.main.run_svn(None,
639 'log',
640 '-r', '2',
641 '-v',
642 dest_sbox.repo_url)
644 if err:
645 raise SVNUnexpectedStderr(err)
647 svntest.verify.compare_and_display_lines(None,
648 'LOG',
649 expected_out,
650 out[2:12])
652 def url_encoding(sbox):
653 "test url encoding issues"
654 run_test(sbox, "url-encoding-bug.dump")
657 # A test for copying revisions that lack a property that already exists
658 # on the destination rev as part of the commit (i.e. svn:author in this
659 # case, but svn:date would also work).
660 def no_author(sbox):
661 "test copying revs with no svn:author revprops"
662 run_test(sbox, "no-author.dump")
664 def copy_revprops(sbox):
665 "test copying revprops other than svn:*"
666 run_test(sbox, "revprops.dump")
668 def only_trunk(sbox):
669 "test syncing subdirectories"
670 run_test(sbox, "svnsync-trunk-only.dump", "/trunk",
671 "svnsync-trunk-only.expected.dump")
673 def only_trunk_A_with_changes(sbox):
674 "test syncing subdirectories with changes on root"
675 run_test(sbox, "svnsync-trunk-A-changes.dump", "/trunk/A",
676 "svnsync-trunk-A-changes.expected.dump")
678 # test for issue #2904
679 def move_and_modify_in_the_same_revision(sbox):
680 "test move parent and modify child file in same rev"
681 run_test(sbox, "svnsync-move-and-modify.dump")
683 ########################################################################
684 # Run the tests
687 # list all tests here, starting with None:
688 test_list = [ None,
689 copy_and_modify,
690 copy_from_previous_version_and_modify,
691 copy_from_previous_version,
692 modified_in_place,
693 tag_empty_trunk,
694 tag_trunk_with_dir,
695 tag_trunk_with_file2,
696 tag_trunk_with_file,
697 tag_with_modified_file,
698 dir_prop_change,
699 file_dir_file,
700 copy_parent_modify_prop,
701 detect_meddling,
702 Skip(basic_authz, svntest.main.is_ra_type_file),
703 Skip(copy_from_unreadable_dir, svntest.main.is_ra_type_file),
704 Skip(copy_with_mod_from_unreadable_dir,
705 svntest.main.is_ra_type_file),
706 Skip(copy_with_mod_from_unreadable_dir_and_copy,
707 svntest.main.is_ra_type_file),
708 url_encoding,
709 no_author,
710 copy_revprops,
711 SkipUnless(only_trunk,
712 server_has_partial_replay),
713 SkipUnless(only_trunk_A_with_changes,
714 server_has_partial_replay),
715 move_and_modify_in_the_same_revision,
718 if __name__ == '__main__':
719 svntest.main.run_tests(test_list, serial_only = True)
720 # NOTREACHED
723 ### End of file.