Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / cmdline / special_tests.py
blob4e88d7f7324b8b41957236024e2cd94daaad7605
1 #!/usr/bin/env python
3 # special_tests.py: testing special file handling
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-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, re
22 # Our testing module
23 import svntest
26 # (abbreviation)
27 Skip = svntest.testcase.Skip
28 SkipUnless = svntest.testcase.SkipUnless
29 XFail = svntest.testcase.XFail
30 Item = svntest.wc.StateItem
33 ######################################################################
34 # Tests
36 # Each test must return on success or raise on failure.
39 #----------------------------------------------------------------------
41 def general_symlink(sbox):
42 "general symlink handling"
44 sbox.build()
45 wc_dir = sbox.wc_dir
47 # First try to just commit a symlink
48 newfile_path = os.path.join(wc_dir, 'newfile')
49 linktarget_path = os.path.join(wc_dir, 'linktarget')
50 svntest.main.file_append(linktarget_path, 'this is just a link target')
51 os.symlink('linktarget', newfile_path)
52 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
54 expected_output = svntest.wc.State(wc_dir, {
55 'newfile' : Item(verb='Adding'),
56 'linktarget' : Item(verb='Adding'),
59 # Run a diff and verify that we get the correct output
60 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'diff', wc_dir)
62 regex = '^\+link linktarget'
63 for line in stdout_lines:
64 if re.match(regex, line):
65 break
66 else:
67 raise svntest.Failure
69 # Commit and make sure everything is good
70 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
71 expected_status.add({
72 'newfile' : Item(status=' ', wc_rev=2),
73 'linktarget' : Item(status=' ', wc_rev=2),
76 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
77 expected_status, None,
78 wc_dir)
80 ## Now we should update to the previous version, verify that no
81 ## symlink is present, then update back to HEAD and see if the symlink
82 ## is regenerated properly.
83 svntest.actions.run_and_verify_svn(None, None, [],
84 'up', '-r', '1', wc_dir)
86 # Is the symlink gone?
87 if os.path.isfile(newfile_path) or os.path.islink(newfile_path):
88 raise svntest.Failure
90 svntest.actions.run_and_verify_svn(None, None, [],
91 'up', '-r', '2', wc_dir)
93 # Is the symlink back?
94 new_target = os.readlink(newfile_path)
95 if new_target != 'linktarget':
96 raise svntest.Failure
98 ## Now change the target of the symlink, verify that it is shown as
99 ## modified and that a commit succeeds.
100 os.remove(newfile_path)
101 os.symlink('A', newfile_path)
103 was_cwd = os.getcwd()
104 os.chdir(wc_dir)
105 svntest.actions.run_and_verify_svn(None, [ "M newfile\n" ], [], 'st')
107 os.chdir(was_cwd)
109 expected_output = svntest.wc.State(wc_dir, {
110 'newfile' : Item(verb='Sending'),
113 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
114 expected_status.add({
115 'newfile' : Item(status=' ', wc_rev=3),
116 'linktarget' : Item(status=' ', wc_rev=2),
119 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
120 expected_status, None, wc_dir)
123 def replace_file_with_symlink(sbox):
124 "replace a normal file with a special file"
126 sbox.build()
127 wc_dir = sbox.wc_dir
129 # First replace a normal file with a symlink and make sure we get an
130 # error
131 iota_path = os.path.join(wc_dir, 'iota')
132 os.remove(iota_path)
133 os.symlink('A', iota_path)
135 # Does status show the obstruction?
136 was_cwd = os.getcwd()
137 os.chdir(wc_dir)
138 svntest.actions.run_and_verify_svn(None, [ "~ iota\n" ], [], 'st')
140 # And does a commit fail?
141 os.chdir(was_cwd)
142 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
143 'log msg', wc_dir)
145 regex = 'svn: Commit failed'
146 for line in stderr_lines:
147 if re.match(regex, line):
148 break
149 else:
150 raise svntest.Failure
153 def import_export_symlink(sbox):
154 "import and export a symlink"
156 sbox.build()
157 wc_dir = sbox.wc_dir
159 # create a new symlink to import
160 new_path = os.path.join(wc_dir, 'new_file')
162 os.symlink('linktarget', new_path)
164 # import this symlink into the repository
165 url = sbox.repo_url + "/dirA/dirB/new_link"
166 output, errput = svntest.actions.run_and_verify_svn(
167 'Import a symlink', None, [], 'import',
168 '-m', 'log msg', new_path, url)
170 regex = "(Committed|Imported) revision [0-9]+."
171 for line in output:
172 if re.match(regex, line):
173 break
174 else:
175 raise svntest.Failure
177 # remove the unversioned link
178 os.remove(new_path)
180 # run update and verify that the symlink is put back into place
181 svntest.actions.run_and_verify_svn(None, None, [],
182 'up', wc_dir)
184 # Is the symlink back?
185 link_path = wc_dir + "/dirA/dirB/new_link"
186 new_target = os.readlink(link_path)
187 if new_target != 'linktarget':
188 raise svntest.Failure
190 ## Now we will try exporting from both the working copy and the
191 ## repository directly, verifying that the symlink is created in
192 ## both cases.
194 for export_src, dest_dir in [(sbox.wc_dir, 'export-wc'),
195 (sbox.repo_url, 'export-url')]:
196 export_target = sbox.add_wc_path(dest_dir)
197 svntest.actions.run_and_verify_svn(None, None, [],
198 'export', export_src, export_target)
200 # is the link at the correct place?
201 link_path = os.path.join(export_target, "dirA/dirB/new_link")
202 new_target = os.readlink(link_path)
203 if new_target != 'linktarget':
204 raise svntest.Failure
207 #----------------------------------------------------------------------
208 # Regression test for issue 1986
210 def copy_tree_with_symlink(sbox):
211 "'svn cp dir1 dir2' which contains a symlink"
213 sbox.build()
214 wc_dir = sbox.wc_dir
216 # Create a versioned symlink within directory 'A/D/H'.
217 newfile_path = os.path.join(wc_dir, 'A', 'D', 'H', 'newfile')
218 linktarget_path = os.path.join(wc_dir, 'A', 'D', 'H', 'linktarget')
219 svntest.main.file_append(linktarget_path, 'this is just a link target')
220 os.symlink('linktarget', newfile_path)
221 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
223 expected_output = svntest.wc.State(wc_dir, {
224 'A/D/H/newfile' : Item(verb='Adding'),
225 'A/D/H/linktarget' : Item(verb='Adding'),
228 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
229 expected_status.add({
230 'A/D/H/newfile' : Item(status=' ', wc_rev=2),
231 'A/D/H/linktarget' : Item(status=' ', wc_rev=2),
234 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
235 expected_status, None, wc_dir)
236 # Copy H to H2
237 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
238 H2_path = os.path.join(wc_dir, 'A', 'D', 'H2')
239 svntest.actions.run_and_verify_svn(None, None, [], 'cp', H_path, H2_path)
241 # 'svn status' should show just "A/D/H2 A +". Nothing broken.
242 expected_status.add({
243 'A/D/H2' : Item(status='A ', copied='+', wc_rev='-'),
244 'A/D/H2/chi' : Item(status=' ', copied='+', wc_rev='-'),
245 'A/D/H2/omega' : Item(status=' ', copied='+', wc_rev='-'),
246 'A/D/H2/psi' : Item(status=' ', copied='+', wc_rev='-'),
247 'A/D/H2/linktarget' : Item(status=' ', copied='+', wc_rev='-'),
248 'A/D/H2/newfile' : Item(status=' ', copied='+', wc_rev='-'),
250 svntest.actions.run_and_verify_status(wc_dir, expected_status)
253 def replace_symlink_with_file(sbox):
254 "replace a special file with a non-special file"
256 sbox.build()
257 wc_dir = sbox.wc_dir
259 # Create a new special file and commit it.
260 newfile_path = os.path.join(wc_dir, 'newfile')
261 linktarget_path = os.path.join(wc_dir, 'linktarget')
262 svntest.main.file_append(linktarget_path, 'this is just a link target')
263 os.symlink('linktarget', newfile_path)
264 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
266 expected_output = svntest.wc.State(wc_dir, {
267 'newfile' : Item(verb='Adding'),
268 'linktarget' : Item(verb='Adding'),
271 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
272 expected_status.add({
273 'newfile' : Item(status=' ', wc_rev=2),
274 'linktarget' : Item(status=' ', wc_rev=2),
277 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
278 expected_status, None, wc_dir)
281 # Now replace the symlink with a normal file and try to commit, we
282 # should get an error
283 os.remove(newfile_path);
284 svntest.main.file_append(newfile_path, "text of actual file");
286 # Does status show the obstruction?
287 was_cwd = os.getcwd()
288 os.chdir(wc_dir)
289 svntest.actions.run_and_verify_svn(None, [ "~ newfile\n" ], [], 'st')
291 # And does a commit fail?
292 os.chdir(was_cwd)
293 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
294 'log msg', wc_dir)
296 regex = 'svn: Commit failed'
297 for line in stderr_lines:
298 if re.match(regex, line):
299 break
300 else:
301 raise svntest.Failure
304 def remove_symlink(sbox):
305 "remove a symlink"
307 sbox.build()
308 wc_dir = sbox.wc_dir
310 # Commit a symlink
311 newfile_path = os.path.join(wc_dir, 'newfile')
312 linktarget_path = os.path.join(wc_dir, 'linktarget')
313 svntest.main.file_append(linktarget_path, 'this is just a link target')
314 os.symlink('linktarget', newfile_path)
315 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
317 expected_output = svntest.wc.State(wc_dir, {
318 'newfile' : Item(verb='Adding'),
319 'linktarget' : Item(verb='Adding'),
322 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
323 expected_status.add({
324 'newfile' : Item(status=' ', wc_rev=2),
325 'linktarget' : Item(status=' ', wc_rev=2),
328 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
329 expected_status, None, wc_dir)
331 # Now remove it
332 svntest.actions.run_and_verify_svn(None, None, [], 'rm', newfile_path)
334 # Commit and verify that it worked
335 expected_output = svntest.wc.State(wc_dir, {
336 'newfile' : Item(verb='Deleting'),
339 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
340 expected_status.add({
341 'linktarget' : Item(status=' ', wc_rev=2),
344 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
345 expected_status, None, wc_dir)
347 def merge_symlink_into_file(sbox):
348 "merge symlink into file"
350 sbox.build()
351 wc_dir = sbox.wc_dir
352 d_url = sbox.repo_url + '/A/D'
353 dprime_url = sbox.repo_url + '/A/Dprime'
355 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
356 gamma_prime_path = os.path.join(wc_dir, 'A', 'Dprime', 'gamma')
358 # create a copy of the D directory to play with
359 svntest.main.run_svn(None,
360 'copy', d_url, dprime_url, '-m', 'copy')
361 svntest.main.run_svn(None,
362 'update', sbox.wc_dir)
364 # remove A/Dprime/gamma
365 svntest.main.run_svn(None, 'delete', gamma_prime_path)
367 expected_output = svntest.wc.State(wc_dir, {
368 'A/Dprime/gamma' : Item(verb='Deleting'),
371 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
372 wc_dir)
374 # Commit a symlink in its place
375 linktarget_path = os.path.join(wc_dir, 'linktarget')
376 svntest.main.file_append(linktarget_path, 'this is just a link target')
377 os.symlink('linktarget', gamma_prime_path)
378 svntest.main.run_svn(None, 'add', gamma_prime_path)
380 expected_output = svntest.wc.State(wc_dir, {
381 'A/Dprime/gamma' : Item(verb='Adding'),
384 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
385 wc_dir)
387 # merge the creation of the symlink into the original directory
388 svntest.main.run_svn(None,
389 'merge', '-r', '2:4', dprime_url,
390 os.path.join(wc_dir, 'A', 'D'))
392 # now revert, and we'll get a strange error
393 svntest.main.run_svn(None, 'revert', '-R', wc_dir)
395 # assuming we got past the revert because someone fixed that bug, lets
396 # try the merge and a commit, since that apparently used to throw us for
397 # a loop, see issue 2530
398 svntest.main.run_svn(None,
399 'merge', '-r', '2:4', dprime_url,
400 os.path.join(wc_dir, 'A', 'D'))
402 expected_output = svntest.wc.State(wc_dir, {
403 'A/D' : Item(verb='Sending'),
404 'A/D/gamma' : Item(verb='Replacing'),
407 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
408 wc_dir)
412 def merge_file_into_symlink(sbox):
413 "merge file into symlink"
415 sbox.build()
416 wc_dir = sbox.wc_dir
417 d_url = sbox.repo_url + '/A/D'
418 dprime_url = sbox.repo_url + '/A/Dprime'
420 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
421 gamma_prime_path = os.path.join(wc_dir, 'A', 'Dprime', 'gamma')
423 # create a copy of the D directory to play with
424 svntest.main.run_svn(None,
425 'copy', d_url, dprime_url, '-m', 'copy')
426 svntest.main.run_svn(None,
427 'update', sbox.wc_dir)
429 # remove A/Dprime/gamma
430 svntest.main.run_svn(None, 'delete', gamma_prime_path)
432 expected_output = svntest.wc.State(wc_dir, {
433 'A/Dprime/gamma' : Item(verb='Deleting'),
436 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
437 wc_dir)
439 # Commit a symlink in its place
440 linktarget_path = os.path.join(wc_dir, 'linktarget')
441 svntest.main.file_append(linktarget_path, 'this is just a link target')
442 os.symlink('linktarget', gamma_prime_path)
443 svntest.main.run_svn(None, 'add', gamma_prime_path)
445 expected_output = svntest.wc.State(wc_dir, {
446 'A/Dprime/gamma' : Item(verb='Adding'),
449 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
450 wc_dir)
452 svntest.main.file_write(gamma_path, 'changed file', 'w+')
454 expected_output = svntest.wc.State(wc_dir, {
455 'A/D/gamma' : Item(verb='Sending'),
458 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
459 wc_dir)
461 # ok, now merge the change to the file into the symlink we created, this
462 # gives us a weird error
463 svntest.main.run_svn(None,
464 'merge', '-r', '4:5', d_url,
465 os.path.join(wc_dir, 'A', 'Dprime'))
467 # Issue 2701: Tests to see repository with symlinks can be checked out on all
468 # platforms.
469 def checkout_repo_with_symlinks(sbox):
470 "checkout a repository containing symlinks"
472 svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
473 'special_tests_data',
474 'symlink.dump'))
476 expected_output = svntest.wc.State(sbox.wc_dir, {
477 'from': Item(status='A '),
478 'to': Item(status='A '),
481 if svntest.main.is_os_windows():
482 expected_link_contents = 'link to'
483 else:
484 expected_link_contents = ''
486 expected_wc = svntest.wc.State('', {
487 'from' : Item(contents=expected_link_contents),
488 'to' : Item(contents=''),
490 svntest.actions.run_and_verify_checkout(sbox.repo_url,
491 sbox.wc_dir,
492 expected_output,
493 expected_wc)
495 # Issue 2716: 'svn diff' against a symlink to a directory within the wc
496 def diff_symlink_to_dir(sbox):
497 "diff a symlink to a directory"
499 sbox.build(read_only = True)
500 wc_dir = sbox.wc_dir
502 # Create a symlink to A/D/.
503 d_path = os.path.join('A', 'D')
504 link_path = os.path.join(wc_dir, 'link')
505 os.symlink(d_path, link_path)
507 # Add the symlink.
508 svntest.main.run_svn(None, 'add', link_path)
510 # Now diff the wc itself and check the results.
511 expected_output = [
512 "Index: svn-test-work/working_copies/special_tests-10/link\n",
513 "===================================================================\n",
514 "--- svn-test-work/working_copies/special_tests-10/link\t(revision 0)\n",
515 "+++ svn-test-work/working_copies/special_tests-10/link\t(revision 0)\n",
516 "@@ -0,0 +1 @@\n",
517 "+link " + d_path + "\n",
518 "\ No newline at end of file\n",
519 "\n",
520 "Property changes on: svn-test-work/working_copies/special_tests-10/link\n",
521 "___________________________________________________________________\n",
522 "Added: svn:special\n",
523 " + *\n",
524 "\n" ]
525 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
526 wc_dir)
527 # We should get the same output if we the diff the symlink itself.
528 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
529 link_path)
531 # Issue 2692 (part of): Check that the client can check out a repository
532 # that contains an unknown special file type.
533 def checkout_repo_with_unknown_special_type(sbox):
534 "checkout repository with unknown special file type"
536 svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
537 'special_tests_data',
538 'bad-special-type.dump'))
540 expected_output = svntest.wc.State(sbox.wc_dir, {
541 'special': Item(status='A '),
543 expected_wc = svntest.wc.State('', {
544 'special' : Item(contents='gimble wabe'),
546 svntest.actions.run_and_verify_checkout(sbox.repo_url,
547 sbox.wc_dir,
548 expected_output,
549 expected_wc)
551 def replace_symlink_with_dir(sbox):
552 "replace a special file with a directory"
554 svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
555 'special_tests_data',
556 'symlink.dump'))
558 wc_dir = sbox.wc_dir
559 from_path = os.path.join(wc_dir, 'from')
561 # Now replace the symlink with a directory and try to commit, we
562 # should get an error
563 os.remove(from_path);
564 os.mkdir(from_path);
566 # Does status show the obstruction?
567 was_cwd = os.getcwd()
568 os.chdir(wc_dir)
569 svntest.actions.run_and_verify_svn(None, [ "~ from\n" ], [], 'st')
571 # The commit shouldn't do anything.
572 # I'd expect a failed commit here, but replacing a file locally with a
573 # directory seems to make svn think the file is unchanged.
574 os.chdir(was_cwd)
575 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
576 'log msg', wc_dir)
577 if not (stdout_lines == [] or stderr_lines == []):
578 raise svntest.Failure
580 # test for issue #1808: svn up deletes local symlink that obstructs
581 # versioned file
582 def update_obstructing_symlink(sbox):
583 "symlink obstructs incoming delete"
585 sbox.build()
586 wc_dir = sbox.wc_dir
587 mu_path = os.path.join(wc_dir, 'A', 'mu')
588 mu_url = sbox.repo_url + '/A/mu'
589 iota_path = os.path.join(wc_dir, 'iota')
591 # delete A/mu and replace it with a symlink
592 svntest.main.run_svn(None, 'rm', mu_path)
593 os.symlink(iota_path, mu_path)
595 svntest.main.run_svn(None, 'rm', mu_url,
596 '-m', 'log msg')
598 svntest.main.run_svn(None,
599 'up', wc_dir)
601 # check that the symlink is still there
602 target = os.readlink(mu_path)
603 if target != iota_path:
604 raise svntest.Failure
607 ########################################################################
608 # Run the tests
611 # list all tests here, starting with None:
612 test_list = [ None,
613 SkipUnless(general_symlink, svntest.main.is_posix_os),
614 SkipUnless(replace_file_with_symlink, svntest.main.is_posix_os),
615 SkipUnless(import_export_symlink, svntest.main.is_posix_os),
616 SkipUnless(copy_tree_with_symlink, svntest.main.is_posix_os),
617 SkipUnless(replace_symlink_with_file, svntest.main.is_posix_os),
618 SkipUnless(remove_symlink, svntest.main.is_posix_os),
619 SkipUnless(merge_symlink_into_file, svntest.main.is_posix_os),
620 SkipUnless(merge_file_into_symlink, svntest.main.is_posix_os),
621 checkout_repo_with_symlinks,
622 XFail(SkipUnless(diff_symlink_to_dir, svntest.main.is_posix_os)),
623 checkout_repo_with_unknown_special_type,
624 replace_symlink_with_dir,
625 SkipUnless(update_obstructing_symlink, svntest.main.is_posix_os),
628 if __name__ == '__main__':
629 svntest.main.run_tests(test_list)
630 # NOTREACHED
633 ### End of file.