Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / cmdline / svnadmin_tests.py
blob2e96ee00566df214be469368310374ccbe837732
1 #!/usr/bin/env python
3 # svnadmin_tests.py: testing the 'svnadmin' tool.
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2006 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 os
21 import sys
23 # Our testing module
24 import svntest
25 from svntest.verify import SVNExpectedStdout, SVNExpectedStderr
27 # (abbreviation)
28 Skip = svntest.testcase.Skip
29 SkipUnless = svntest.testcase.SkipUnless
30 XFail = svntest.testcase.XFail
31 Item = svntest.wc.StateItem
34 #----------------------------------------------------------------------
36 # How we currently test 'svnadmin' --
38 # 'svnadmin create': Create an empty repository, test that the
39 # root node has a proper created-revision,
40 # because there was once a bug where it
41 # didn't.
43 # Note also that "svnadmin create" is tested
44 # implicitly every time we run a python test
45 # script. (An empty repository is always
46 # created and then imported into; if this
47 # subcommand failed catastrophically, every
48 # test would fail and we would know instantly.)
50 # 'svnadmin createtxn'
51 # 'svnadmin rmtxn': See below.
53 # 'svnadmin lstxns': We don't care about the contents of transactions;
54 # we only care that they exist or not.
55 # Therefore, we can simply parse transaction headers.
57 # 'svnadmin dump': A couple regression tests that ensure dump doesn't
58 # error out, and one to check that the --quiet option
59 # really does what it's meant to do. The actual
60 # contents of the dump aren't verified at all.
62 # ### TODO: someday maybe we could parse the contents of trees too.
64 ######################################################################
65 # Helper routines
68 def get_txns(repo_dir):
69 "Get the txn names using 'svnadmin lstxns'."
71 output_lines, error_lines = svntest.main.run_svnadmin('lstxns', repo_dir)
72 txns = map(output_lines.strip, output_lines)
74 # sort, just in case
75 txns.sort()
77 return txns
79 def load_and_verify_dumpstream(sbox, expected_stdout, expected_stderr,
80 revs, dump, *varargs):
81 """Load the array of lines passed in 'dump' into the
82 current tests' repository and verify the repository content
83 using the array of wc.States passed in revs. VARARGS are optional
84 arguments passed to the 'load' command"""
86 if type(dump) is type(""):
87 dump = [ dump ]
89 output, errput = \
90 svntest.main.run_command_stdin(
91 svntest.main.svnadmin_binary, expected_stderr, 1, dump,
92 'load', '--quiet', sbox.repo_dir, *varargs)
94 if expected_stdout:
95 if expected_stdout == svntest.verify.AnyOutput:
96 if len(output) == 0:
97 raise SVNExpectedStdout
98 else:
99 svntest.verify.compare_and_display_lines(
100 "Standard output", "STDOUT:", expected_stdout, output)
102 if expected_stderr:
103 if expected_stderr == svntest.verify.AnyOutput:
104 if len(errput) == 0:
105 raise SVNExpectedStderr
106 else:
107 svntest.verify.compare_and_display_lines(
108 "Standard error output", "STDERR:", expected_stderr, errput)
109 # The expected error occurred, so don't try to verify the result
110 return
112 if revs:
113 # verify revs as wc states
114 for rev in xrange(len(revs)):
115 svntest.actions.run_and_verify_svn("Updating to r%s" % (rev+1),
116 svntest.verify.AnyOutput, [],
117 "update", "-r%s" % (rev+1),
118 sbox.wc_dir)
120 wc_tree = svntest.tree.build_tree_from_wc(sbox.wc_dir)
121 rev_tree = revs[rev].old_tree()
123 try:
124 svntest.tree.compare_trees ("rev/disk", rev_tree, wc_tree)
125 except svntest.tree.SVNTreeError:
126 svntest.verify.display_trees(None, 'WC TREE', wc_tree, rev_tree)
127 raise
130 ######################################################################
131 # Tests
134 #----------------------------------------------------------------------
136 def test_create(sbox):
137 "'svnadmin create'"
140 repo_dir = sbox.repo_dir
141 wc_dir = sbox.wc_dir
143 svntest.main.safe_rmtree(repo_dir)
144 svntest.main.safe_rmtree(wc_dir)
146 svntest.main.create_repos(repo_dir)
148 svntest.actions.run_and_verify_svn("Creating rev 0 checkout",
149 ["Checked out revision 0.\n"], [],
150 "checkout",
151 sbox.repo_url, wc_dir)
154 svntest.actions.run_and_verify_svn(
155 "Running status",
156 [], [],
157 "status", wc_dir)
159 svntest.actions.run_and_verify_svn(
160 "Running verbose status",
161 [" 0 0 ? %s\n" % wc_dir], [],
162 "status", "--verbose", wc_dir)
164 # success
167 # dump stream tests need a dump file
169 def clean_dumpfile():
170 return \
171 [ "SVN-fs-dump-format-version: 2\n\n",
172 "UUID: 668cc64a-31ed-0310-8ccb-b75d75bb44e3\n\n",
173 "Revision-number: 0\n",
174 "Prop-content-length: 56\n",
175 "Content-length: 56\n\n",
176 "K 8\nsvn:date\nV 27\n2005-01-08T21:48:13.838745Z\nPROPS-END\n\n\n",
177 "Revision-number: 1\n",
178 "Prop-content-length: 98\n",
179 "Content-length: 98\n\n",
180 "K 7\nsvn:log\nV 0\n\nK 10\nsvn:author\nV 4\nerik\n",
181 "K 8\nsvn:date\nV 27\n2005-01-08T21:51:16.313791Z\nPROPS-END\n\n\n",
182 "Node-path: A\n",
183 "Node-kind: file\n",
184 "Node-action: add\n",
185 "Prop-content-length: 35\n",
186 "Text-content-length: 5\n",
187 "Text-content-md5: e1cbb0c3879af8347246f12c559a86b5\n",
188 "Content-length: 40\n\n",
189 "K 12\nsvn:keywords\nV 2\nId\nPROPS-END\ntext\n\n\n"]
191 dumpfile_revisions = \
192 [ svntest.wc.State('', { 'A' : svntest.wc.StateItem(contents="text\n") }) ]
194 #----------------------------------------------------------------------
195 def extra_headers(sbox):
196 "loading of dumpstream with extra headers"
198 test_create(sbox)
200 dumpfile = clean_dumpfile()
202 dumpfile[3:3] = \
203 [ "X-Comment-Header: Ignored header normally not in dump stream\n" ]
205 load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile)
207 #----------------------------------------------------------------------
208 # Ensure loading continues after skipping a bit of unknown extra content.
209 def extra_blockcontent(sbox):
210 "load success on oversized Content-length"
212 test_create(sbox)
214 dumpfile = clean_dumpfile()
216 # Replace "Content-length" line with two lines
217 dumpfile[8:9] = \
218 [ "Extra-content-length: 10\n",
219 "Content-length: 108\n\n" ]
220 # Insert the extra content after "PROPS-END\n"
221 dumpfile[11] = dumpfile[11][:-2] + "extra text\n\n\n"
223 load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile)
225 #----------------------------------------------------------------------
226 def inconsistent_headers(sbox):
227 "load failure on undersized Content-length"
229 test_create(sbox)
231 dumpfile = clean_dumpfile()
233 dumpfile[-2] = "Content-length: 30\n\n"
235 load_and_verify_dumpstream(sbox, [], svntest.verify.AnyOutput,
236 dumpfile_revisions, dumpfile)
238 #----------------------------------------------------------------------
239 # Test for issue #2729: Datestamp-less revisions in dump streams do
240 # not remain so after load
241 def empty_date(sbox):
242 "preserve date-less revisions in load (issue #2729)"
244 test_create(sbox)
246 dumpfile = clean_dumpfile()
248 # Replace portions of the revision data to drop the svn:date revprop.
249 dumpfile[7:11] = \
250 [ "Prop-content-length: 52\n",
251 "Content-length: 52\n\n",
252 "K 7\nsvn:log\nV 0\n\nK 10\nsvn:author\nV 4\nerik\nPROPS-END\n\n\n"
255 load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, dumpfile)
257 # Verify that the revision still lacks the svn:date property.
258 svntest.actions.run_and_verify_svn(None, [], [], "propget",
259 "--revprop", "-r1", "svn:date",
260 sbox.wc_dir)
262 #----------------------------------------------------------------------
264 def dump_copied_dir(sbox):
265 "'svnadmin dump' on copied directory"
267 sbox.build()
268 wc_dir = sbox.wc_dir
269 repo_dir = sbox.repo_dir
271 old_C_path = os.path.join(wc_dir, 'A', 'C')
272 new_C_path = os.path.join(wc_dir, 'A', 'B', 'C')
273 svntest.main.run_svn(None, 'cp', old_C_path, new_C_path)
274 svntest.main.run_svn(None, 'ci', wc_dir, '--quiet',
275 '-m', 'log msg')
277 output, errput = svntest.main.run_svnadmin("dump", repo_dir)
278 if svntest.verify.compare_and_display_lines(
279 "Output of 'svnadmin dump' is unexpected.",
280 'STDERR', ["* Dumped revision 0.\n",
281 "* Dumped revision 1.\n",
282 "* Dumped revision 2.\n"], errput):
283 raise svntest.Failure
285 #----------------------------------------------------------------------
287 def dump_move_dir_modify_child(sbox):
288 "'svnadmin dump' on modified child of copied dir"
290 sbox.build()
291 wc_dir = sbox.wc_dir
292 repo_dir = sbox.repo_dir
294 B_path = os.path.join(wc_dir, 'A', 'B')
295 Q_path = os.path.join(wc_dir, 'A', 'Q')
296 svntest.main.run_svn(None, 'cp', B_path, Q_path)
297 svntest.main.file_append(os.path.join(Q_path, 'lambda'), 'hello')
298 svntest.main.run_svn(None, 'ci', wc_dir, '--quiet',
299 '-m', 'log msg')
300 output, errput = svntest.main.run_svnadmin("dump", repo_dir)
301 svntest.verify.compare_and_display_lines(
302 "Output of 'svnadmin dump' is unexpected.",
303 'STDERR', ["* Dumped revision 0.\n",
304 "* Dumped revision 1.\n",
305 "* Dumped revision 2.\n"], errput)
307 output, errput = svntest.main.run_svnadmin("dump", "-r", "0:HEAD", repo_dir)
308 svntest.verify.compare_and_display_lines(
309 "Output of 'svnadmin dump' is unexpected.",
310 'STDERR', ["* Dumped revision 0.\n",
311 "* Dumped revision 1.\n",
312 "* Dumped revision 2.\n"], errput)
314 #----------------------------------------------------------------------
316 def dump_quiet(sbox):
317 "'svnadmin dump --quiet'"
319 sbox.build(create_wc = False)
321 output, errput = svntest.main.run_svnadmin("dump", sbox.repo_dir, '--quiet')
322 svntest.verify.compare_and_display_lines(
323 "Output of 'svnadmin dump --quiet' is unexpected.",
324 'STDERR', [], errput)
326 #----------------------------------------------------------------------
328 def hotcopy_dot(sbox):
329 "'svnadmin hotcopy PATH .'"
330 sbox.build()
332 backup_dir, backup_url = sbox.add_repo_path('backup')
333 os.mkdir(backup_dir)
334 cwd = os.getcwd()
336 os.chdir(backup_dir)
337 output, errput = svntest.main.run_svnadmin("hotcopy",
338 os.path.join(cwd, sbox.repo_dir),
339 '.')
340 if errput:
341 raise svntest.Failure
343 os.chdir(cwd)
345 origout, origerr = svntest.main.run_svnadmin("dump", sbox.repo_dir, '--quiet')
346 backout, backerr = svntest.main.run_svnadmin("dump", backup_dir, '--quiet')
347 if origerr or backerr or origout != backout:
348 raise svntest.Failure
350 #----------------------------------------------------------------------
352 def hotcopy_format(sbox):
353 "'svnadmin hotcopy' checking db/format file"
354 sbox.build()
356 backup_dir, backup_url = sbox.add_repo_path('backup')
357 output, errput = svntest.main.run_svnadmin("hotcopy", sbox.repo_dir,
358 backup_dir)
359 if errput:
360 print "Error: hotcopy failed"
361 raise svntest.Failure
363 # verify that the db/format files are the same
364 fp = open(os.path.join(sbox.repo_dir, "db", "format"))
365 contents1 = fp.read()
366 fp.close()
368 fp2 = open(os.path.join(backup_dir, "db", "format"))
369 contents2 = fp2.read()
370 fp2.close()
372 if contents1 != contents2:
373 print "Error: db/format file contents do not match after hotcopy"
374 raise svntest.Failure
376 #----------------------------------------------------------------------
378 def setrevprop(sbox):
379 "'setlog' and 'setrevprop', bypassing hooks'"
380 sbox.build()
382 # Try a simple log property modification.
383 iota_path = os.path.join(sbox.wc_dir, "iota")
384 output, errput = svntest.main.run_svnadmin("setlog", sbox.repo_dir,
385 "-r0", "--bypass-hooks",
386 iota_path)
387 if errput:
388 print "Error: 'setlog' failed"
389 raise svntest.Failure
391 # Verify that the revprop value matches what we set when retrieved
392 # through the client.
393 svntest.actions.run_and_verify_svn(None,
394 [ "This is the file 'iota'.\n", "\n" ],
395 [], "propget", "--revprop", "-r0",
396 "svn:log", sbox.wc_dir)
398 # Try an author property modification.
399 foo_path = os.path.join(sbox.wc_dir, "foo")
400 svntest.main.file_write(foo_path, "foo")
402 output, errput = svntest.main.run_svnadmin("setrevprop", sbox.repo_dir,
403 "-r0", "svn:author", foo_path)
404 if errput:
405 print "Error: 'setrevprop' failed"
406 raise svntest.Failure
408 # Verify that the revprop value matches what we set when retrieved
409 # through the client.
410 svntest.actions.run_and_verify_svn(None, [ "foo\n" ], [], "propget",
411 "--revprop", "-r0", "svn:author",
412 sbox.wc_dir)
414 def verify_windows_paths_in_repos(sbox):
415 "verify a repository containing paths like 'c:hi'"
417 # setup a repo with a directory 'c:hi'
418 sbox.build(create_wc = False)
419 repo_url = sbox.repo_url
420 chi_url = sbox.repo_url + '/c:hi'
422 svntest.actions.run_and_verify_svn(None, None, [],
423 'mkdir', '-m', 'log_msg',
424 chi_url)
426 output, errput = svntest.main.run_svnadmin("verify", sbox.repo_dir)
427 svntest.verify.compare_and_display_lines(
428 "Error while running 'svnadmin verify'.",
429 'STDERR', ["* Verified revision 0.\n",
430 "* Verified revision 1.\n",
431 "* Verified revision 2.\n"], errput)
433 #----------------------------------------------------------------------
435 def recover_fsfs(sbox):
436 "recover a repository (FSFS only)"
438 # Set up a repository containing the greek tree.
439 sbox.build(create_wc = False)
441 # Read the current contents of the current file.
442 current_path = os.path.join(sbox.repo_dir, 'db', 'current')
443 expected_current_contents = svntest.main.file_read(current_path)
445 # Remove the current file.
446 os.remove(current_path)
448 # Run 'svnadmin recover' and check that the current file is recreated.
449 output, errput = svntest.main.run_svnadmin("recover", sbox.repo_dir)
450 if errput:
451 raise SVNUnexpectedStderr
453 actual_current_contents = svntest.main.file_read(current_path)
454 svntest.verify.compare_and_display_lines(
455 "Contents of db/current is unexpected.",
456 'db/current', expected_current_contents, actual_current_contents)
458 #----------------------------------------------------------------------
460 def load_with_parent_dir(sbox):
461 "'svnadmin load --parent-dir' reparents mergeinfo"
463 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2983. ##
464 test_create(sbox)
466 dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
467 'svnadmin_tests_data',
468 'mergeinfo_included.dump')
469 dumpfile = svntest.main.file_read(dumpfile_location)
471 # Create 'sample' dir in sbox.repo_url
472 svntest.actions.run_and_verify_svn(None,
473 ['\n', 'Committed revision 1.\n'],
474 [], "mkdir", sbox.repo_url + "/sample",
475 "-m", "Create sample dir")
477 # Load the dump stream
478 load_and_verify_dumpstream(sbox,[],[], None, dumpfile, '--parent-dir',
479 '/sample')
481 # Verify the svn:mergeinfo properties for '--parent-dir'
482 svntest.actions.run_and_verify_svn(None,
483 [sbox.repo_url +
484 "/sample/branch - /sample/trunk:5-7\n"],
485 [], 'propget', 'svn:mergeinfo', '-R',
486 sbox.repo_url + '/sample/branch')
487 svntest.actions.run_and_verify_svn(None,
488 [sbox.repo_url +
489 "/sample/branch1 - " +
490 "/sample/branch:6-9\n"],
491 [], 'propget', 'svn:mergeinfo', '-R',
492 sbox.repo_url + '/sample/branch1')
494 #----------------------------------------------------------------------
496 def set_uuid(sbox):
497 "test 'svnadmin setuuid'"
499 sbox.build(create_wc=False)
501 # Squirrel away the original repository UUID.
502 output, errput = svntest.main.run_svnlook('uuid', sbox.repo_dir)
503 if errput:
504 raise SVNUnexpectedStderr
505 orig_uuid = output[0].rstrip()
507 # Try setting a new, bogus UUID.
508 svntest.actions.run_and_verify_svnadmin(None, None, '^.*Malformed UUID.*$',
509 'setuuid', sbox.repo_dir, 'abcdef')
511 # Try generating a brand new UUID.
512 svntest.actions.run_and_verify_svnadmin(None, [], None,
513 'setuuid', sbox.repo_dir)
514 output, errput = svntest.main.run_svnlook('uuid', sbox.repo_dir)
515 if errput:
516 raise SVNUnexpectedStderr
517 new_uuid = output[0].rstrip()
518 if new_uuid == orig_uuid:
519 print "Error: new UUID matches the original one"
520 raise svntest.Failure
522 # Now, try setting the UUID back to the original value.
523 svntest.actions.run_and_verify_svnadmin(None, [], None,
524 'setuuid', sbox.repo_dir, orig_uuid)
525 output, errput = svntest.main.run_svnlook('uuid', sbox.repo_dir)
526 if errput:
527 raise SVNUnexpectedStderr
528 new_uuid = output[0].rstrip()
529 if new_uuid != orig_uuid:
530 print "Error: new UUID doesn't match the original one"
531 raise svntest.Failure
533 #----------------------------------------------------------------------
535 def reflect_dropped_renumbered_revs(sbox):
536 "reflect dropped renumbered revs in svn:mergeinfo"
538 ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3020. ##
540 test_create(sbox)
542 dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
543 'svndumpfilter_tests_data',
544 'with_merges.dump')
545 dumpfile = svntest.main.file_read(dumpfile_location)
547 # Create 'toplevel' dir in sbox.repo_url
548 svntest.actions.run_and_verify_svn(None, ['\n', 'Committed revision 1.\n'],
549 [], "mkdir", sbox.repo_url + "/toplevel",
550 "-m", "Create toplevel dir")
552 # Load the dump stream in sbox.repo_url
553 load_and_verify_dumpstream(sbox,[],[], None, dumpfile)
555 # Load the dump stream in toplevel dir
556 load_and_verify_dumpstream(sbox,[],[], None, dumpfile, '--parent-dir',
557 '/toplevel')
559 # Verify the svn:mergeinfo properties
560 svntest.actions.run_and_verify_svn(None, ["/trunk:1-4\n"],
561 [], 'propget', 'svn:mergeinfo',
562 sbox.repo_url + '/branch2')
563 svntest.actions.run_and_verify_svn(None, ["/branch1:5-9\n"],
564 [], 'propget', 'svn:mergeinfo',
565 sbox.repo_url + '/trunk')
566 svntest.actions.run_and_verify_svn(None, ["/toplevel/trunk:1-13\n"],
567 [], 'propget', 'svn:mergeinfo',
568 sbox.repo_url + '/toplevel/branch2')
569 svntest.actions.run_and_verify_svn(None, ["/toplevel/branch1:14-18\n"],
570 [], 'propget', 'svn:mergeinfo',
571 sbox.repo_url + '/toplevel/trunk')
572 svntest.actions.run_and_verify_svn(None, ["/toplevel/trunk:1-12\n"],
573 [], 'propget', 'svn:mergeinfo',
574 sbox.repo_url + '/toplevel/branch1')
575 svntest.actions.run_and_verify_svn(None, ["/trunk:1-3\n"],
576 [], 'propget', 'svn:mergeinfo',
577 sbox.repo_url + '/branch1')
580 ########################################################################
581 # Run the tests
584 # list all tests here, starting with None:
585 test_list = [ None,
586 extra_headers,
587 extra_blockcontent,
588 inconsistent_headers,
589 empty_date,
590 dump_copied_dir,
591 dump_move_dir_modify_child,
592 dump_quiet,
593 hotcopy_dot,
594 hotcopy_format,
595 setrevprop,
596 verify_windows_paths_in_repos,
597 SkipUnless(recover_fsfs, svntest.main.is_fs_type_fsfs),
598 load_with_parent_dir,
599 set_uuid,
600 reflect_dropped_renumbered_revs,
603 if __name__ == '__main__':
604 svntest.main.run_tests(test_list)
605 # NOTREACHED
608 ### End of file.