In the command-line client, forbid
[svn.git] / subversion / tests / cmdline / special_tests.py
blobb97a16b11378177d06fbc8012daa70f384d897e4
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 None, None, None, None, 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,
121 None, None, None, None, wc_dir)
124 def replace_file_with_symlink(sbox):
125 "replace a normal file with a special file"
127 sbox.build()
128 wc_dir = sbox.wc_dir
130 # First replace a normal file with a symlink and make sure we get an
131 # error
132 iota_path = os.path.join(wc_dir, 'iota')
133 os.remove(iota_path)
134 os.symlink('A', iota_path)
136 # Does status show the obstruction?
137 was_cwd = os.getcwd()
138 os.chdir(wc_dir)
139 svntest.actions.run_and_verify_svn(None, [ "~ iota\n" ], [], 'st')
141 # And does a commit fail?
142 os.chdir(was_cwd)
143 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
144 'log msg', wc_dir)
146 regex = 'svn: Commit failed'
147 for line in stderr_lines:
148 if re.match(regex, line):
149 break
150 else:
151 raise svntest.Failure
154 def import_export_symlink(sbox):
155 "import and export a symlink"
157 sbox.build()
158 wc_dir = sbox.wc_dir
160 # create a new symlink to import
161 new_path = os.path.join(wc_dir, 'new_file')
163 os.symlink('linktarget', new_path)
165 # import this symlink into the repository
166 url = sbox.repo_url + "/dirA/dirB/new_link"
167 output, errput = svntest.actions.run_and_verify_svn(
168 'Import a symlink', None, [], 'import',
169 '-m', 'log msg', new_path, url)
171 regex = "(Committed|Imported) revision [0-9]+."
172 for line in output:
173 if re.match(regex, line):
174 break
175 else:
176 raise svntest.Failure
178 # remove the unversioned link
179 os.remove(new_path)
181 # run update and verify that the symlink is put back into place
182 svntest.actions.run_and_verify_svn(None, None, [],
183 'up', wc_dir)
185 # Is the symlink back?
186 link_path = wc_dir + "/dirA/dirB/new_link"
187 new_target = os.readlink(link_path)
188 if new_target != 'linktarget':
189 raise svntest.Failure
191 ## Now we will try exporting from both the working copy and the
192 ## repository directly, verifying that the symlink is created in
193 ## both cases.
195 for export_src, dest_dir in [(sbox.wc_dir, 'export-wc'),
196 (sbox.repo_url, 'export-url')]:
197 export_target = sbox.add_wc_path(dest_dir)
198 svntest.actions.run_and_verify_svn(None, None, [],
199 'export', export_src, export_target)
201 # is the link at the correct place?
202 link_path = os.path.join(export_target, "dirA/dirB/new_link")
203 new_target = os.readlink(link_path)
204 if new_target != 'linktarget':
205 raise svntest.Failure
208 #----------------------------------------------------------------------
209 # Regression test for issue 1986
211 def copy_tree_with_symlink(sbox):
212 "'svn cp dir1 dir2' which contains a symlink"
214 sbox.build()
215 wc_dir = sbox.wc_dir
217 # Create a versioned symlink within directory 'A/D/H'.
218 newfile_path = os.path.join(wc_dir, 'A', 'D', 'H', 'newfile')
219 linktarget_path = os.path.join(wc_dir, 'A', 'D', 'H', 'linktarget')
220 svntest.main.file_append(linktarget_path, 'this is just a link target')
221 os.symlink('linktarget', newfile_path)
222 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
224 expected_output = svntest.wc.State(wc_dir, {
225 'A/D/H/newfile' : Item(verb='Adding'),
226 'A/D/H/linktarget' : Item(verb='Adding'),
229 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
230 expected_status.add({
231 'A/D/H/newfile' : Item(status=' ', wc_rev=2),
232 'A/D/H/linktarget' : Item(status=' ', wc_rev=2),
235 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
236 expected_status, None,
237 None, None, None, None, wc_dir)
238 # Copy H to H2
239 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
240 H2_path = os.path.join(wc_dir, 'A', 'D', 'H2')
241 svntest.actions.run_and_verify_svn(None, None, [], 'cp', H_path, H2_path)
243 # 'svn status' should show just "A/D/H2 A +". Nothing broken.
244 expected_status.add({
245 'A/D/H2' : Item(status='A ', copied='+', wc_rev='-'),
246 'A/D/H2/chi' : Item(status=' ', copied='+', wc_rev='-'),
247 'A/D/H2/omega' : Item(status=' ', copied='+', wc_rev='-'),
248 'A/D/H2/psi' : Item(status=' ', copied='+', wc_rev='-'),
249 'A/D/H2/linktarget' : Item(status=' ', copied='+', wc_rev='-'),
250 'A/D/H2/newfile' : Item(status=' ', copied='+', wc_rev='-'),
252 svntest.actions.run_and_verify_status(wc_dir, expected_status)
255 def replace_symlink_with_file(sbox):
256 "replace a special file with a non-special file"
258 sbox.build()
259 wc_dir = sbox.wc_dir
261 # Create a new special file and commit it.
262 newfile_path = os.path.join(wc_dir, 'newfile')
263 linktarget_path = os.path.join(wc_dir, 'linktarget')
264 svntest.main.file_append(linktarget_path, 'this is just a link target')
265 os.symlink('linktarget', newfile_path)
266 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
268 expected_output = svntest.wc.State(wc_dir, {
269 'newfile' : Item(verb='Adding'),
270 'linktarget' : Item(verb='Adding'),
273 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
274 expected_status.add({
275 'newfile' : Item(status=' ', wc_rev=2),
276 'linktarget' : Item(status=' ', wc_rev=2),
279 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
280 expected_status, None,
281 None, None, None, None, wc_dir)
284 # Now replace the symlink with a normal file and try to commit, we
285 # should get an error
286 os.remove(newfile_path);
287 svntest.main.file_append(newfile_path, "text of actual file");
289 # Does status show the obstruction?
290 was_cwd = os.getcwd()
291 os.chdir(wc_dir)
292 svntest.actions.run_and_verify_svn(None, [ "~ newfile\n" ], [], 'st')
294 # And does a commit fail?
295 os.chdir(was_cwd)
296 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
297 'log msg', wc_dir)
299 regex = 'svn: Commit failed'
300 for line in stderr_lines:
301 if re.match(regex, line):
302 break
303 else:
304 raise svntest.Failure
307 def remove_symlink(sbox):
308 "remove a symlink"
310 sbox.build()
311 wc_dir = sbox.wc_dir
313 # Commit a symlink
314 newfile_path = os.path.join(wc_dir, 'newfile')
315 linktarget_path = os.path.join(wc_dir, 'linktarget')
316 svntest.main.file_append(linktarget_path, 'this is just a link target')
317 os.symlink('linktarget', newfile_path)
318 svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
320 expected_output = svntest.wc.State(wc_dir, {
321 'newfile' : Item(verb='Adding'),
322 'linktarget' : Item(verb='Adding'),
325 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
326 expected_status.add({
327 'newfile' : Item(status=' ', wc_rev=2),
328 'linktarget' : Item(status=' ', wc_rev=2),
331 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
332 expected_status, None,
333 None, None, None, None, wc_dir)
335 # Now remove it
336 svntest.actions.run_and_verify_svn(None, None, [], 'rm', newfile_path)
338 # Commit and verify that it worked
339 expected_output = svntest.wc.State(wc_dir, {
340 'newfile' : Item(verb='Deleting'),
343 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
344 expected_status.add({
345 'linktarget' : Item(status=' ', wc_rev=2),
348 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
349 expected_status, None,
350 None, None, None, None, wc_dir)
352 def merge_symlink_into_file(sbox):
353 "merge symlink into file"
355 sbox.build()
356 wc_dir = sbox.wc_dir
357 d_url = sbox.repo_url + '/A/D'
358 dprime_url = sbox.repo_url + '/A/Dprime'
360 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
361 gamma_prime_path = os.path.join(wc_dir, 'A', 'Dprime', 'gamma')
363 # create a copy of the D directory to play with
364 svntest.main.run_svn(None,
365 'copy', d_url, dprime_url, '-m', 'copy')
366 svntest.main.run_svn(None,
367 'update', sbox.wc_dir)
369 # remove A/Dprime/gamma
370 svntest.main.run_svn(None, 'delete', gamma_prime_path)
372 expected_output = svntest.wc.State(wc_dir, {
373 'A/Dprime/gamma' : Item(verb='Deleting'),
376 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
377 None, None, None, None, wc_dir)
379 # Commit a symlink in its place
380 linktarget_path = os.path.join(wc_dir, 'linktarget')
381 svntest.main.file_append(linktarget_path, 'this is just a link target')
382 os.symlink('linktarget', gamma_prime_path)
383 svntest.main.run_svn(None, 'add', gamma_prime_path)
385 expected_output = svntest.wc.State(wc_dir, {
386 'A/Dprime/gamma' : Item(verb='Adding'),
389 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
390 None, None, None, None, wc_dir)
392 # merge the creation of the symlink into the original directory
393 svntest.main.run_svn(None,
394 'merge', '-r', '2:4', dprime_url,
395 os.path.join(wc_dir, 'A', 'D'))
397 # now revert, and we'll get a strange error
398 svntest.main.run_svn(None, 'revert', '-R', wc_dir)
400 # assuming we got past the revert because someone fixed that bug, lets
401 # try the merge and a commit, since that apparently used to throw us for
402 # a loop, see issue 2530
403 svntest.main.run_svn(None,
404 'merge', '-r', '2:4', dprime_url,
405 os.path.join(wc_dir, 'A', 'D'))
407 expected_output = svntest.wc.State(wc_dir, {
408 'A/D' : Item(verb='Sending'),
409 'A/D/gamma' : Item(verb='Replacing'),
412 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
413 None, None, None, None, wc_dir)
417 def merge_file_into_symlink(sbox):
418 "merge file into symlink"
420 sbox.build()
421 wc_dir = sbox.wc_dir
422 d_url = sbox.repo_url + '/A/D'
423 dprime_url = sbox.repo_url + '/A/Dprime'
425 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
426 gamma_prime_path = os.path.join(wc_dir, 'A', 'Dprime', 'gamma')
428 # create a copy of the D directory to play with
429 svntest.main.run_svn(None,
430 'copy', d_url, dprime_url, '-m', 'copy')
431 svntest.main.run_svn(None,
432 'update', sbox.wc_dir)
434 # remove A/Dprime/gamma
435 svntest.main.run_svn(None, 'delete', gamma_prime_path)
437 expected_output = svntest.wc.State(wc_dir, {
438 'A/Dprime/gamma' : Item(verb='Deleting'),
441 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
442 None, None, None, None, wc_dir)
444 # Commit a symlink in its place
445 linktarget_path = os.path.join(wc_dir, 'linktarget')
446 svntest.main.file_append(linktarget_path, 'this is just a link target')
447 os.symlink('linktarget', gamma_prime_path)
448 svntest.main.run_svn(None, 'add', gamma_prime_path)
450 expected_output = svntest.wc.State(wc_dir, {
451 'A/Dprime/gamma' : Item(verb='Adding'),
454 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
455 None, None, None, None, wc_dir)
457 svntest.main.file_write(gamma_path, 'changed file', 'w+')
459 expected_output = svntest.wc.State(wc_dir, {
460 'A/D/gamma' : Item(verb='Sending'),
463 svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None,
464 None, None, None, None, wc_dir)
466 # ok, now merge the change to the file into the symlink we created, this
467 # gives us a weird error
468 svntest.main.run_svn(None,
469 'merge', '-r', '4:5', d_url,
470 os.path.join(wc_dir, 'A', 'Dprime'))
472 # Issue 2701: Tests to see repository with symlinks can be checked out on all
473 # platforms.
474 def checkout_repo_with_symlinks(sbox):
475 "checkout a repository containing symlinks"
477 svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
478 'special_tests_data',
479 'symlink.dump'))
481 expected_output = svntest.wc.State(sbox.wc_dir, {
482 'from': Item(status='A '),
483 'to': Item(status='A '),
486 if svntest.main.is_os_windows():
487 expected_link_contents = 'link to'
488 else:
489 expected_link_contents = ''
491 expected_wc = svntest.wc.State('', {
492 'from' : Item(contents=expected_link_contents),
493 'to' : Item(contents=''),
495 svntest.actions.run_and_verify_checkout(sbox.repo_url,
496 sbox.wc_dir,
497 expected_output,
498 expected_wc)
500 # Issue 2716: 'svn diff' against a symlink to a directory within the wc
501 def diff_symlink_to_dir(sbox):
502 "diff a symlink to a directory"
504 sbox.build()
505 wc_dir = sbox.wc_dir
507 # Create a symlink to A/D/.
508 d_path = os.path.join('A', 'D')
509 link_path = os.path.join(wc_dir, 'link')
510 os.symlink(d_path, link_path)
512 # Add the symlink.
513 svntest.main.run_svn(None, 'add', link_path)
515 # Now diff the wc itself and check the results.
516 expected_output = [
517 "Index: svn-test-work/working_copies/special_tests-10/link\n",
518 "===================================================================\n",
519 "--- svn-test-work/working_copies/special_tests-10/link\t(revision 0)\n",
520 "+++ svn-test-work/working_copies/special_tests-10/link\t(revision 0)\n",
521 "@@ -0,0 +1 @@\n",
522 "+link " + d_path + "\n",
523 "\ No newline at end of file\n",
524 "\n",
525 "Property changes on: svn-test-work/working_copies/special_tests-10/link\n",
526 "___________________________________________________________________\n",
527 "Added: svn:special\n",
528 " + *\n",
529 "\n" ]
530 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
531 wc_dir)
532 # We should get the same output if we the diff the symlink itself.
533 svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
534 link_path)
536 # Issue 2692 (part of): Check that the client can check out a repository
537 # that contains an unknown special file type.
538 def checkout_repo_with_unknown_special_type(sbox):
539 "checkout repository with unknown special file type"
541 svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
542 'special_tests_data',
543 'bad-special-type.dump'))
545 expected_output = svntest.wc.State(sbox.wc_dir, {
546 'special': Item(status='A '),
548 expected_wc = svntest.wc.State('', {
549 'special' : Item(contents='gimble wabe'),
551 svntest.actions.run_and_verify_checkout(sbox.repo_url,
552 sbox.wc_dir,
553 expected_output,
554 expected_wc)
556 def replace_symlink_with_dir(sbox):
557 "replace a special file with a directory"
559 svntest.actions.load_repo(sbox, os.path.join(os.path.dirname(sys.argv[0]),
560 'special_tests_data',
561 'symlink.dump'))
563 wc_dir = sbox.wc_dir
564 from_path = os.path.join(wc_dir, 'from')
566 # Now replace the symlink with a directory and try to commit, we
567 # should get an error
568 os.remove(from_path);
569 os.mkdir(from_path);
571 # Does status show the obstruction?
572 was_cwd = os.getcwd()
573 os.chdir(wc_dir)
574 svntest.actions.run_and_verify_svn(None, [ "~ from\n" ], [], 'st')
576 # The commit shouldn't do anything.
577 # I'd expect a failed commit here, but replacing a file locally with a
578 # directory seems to make svn think the file is unchanged.
579 os.chdir(was_cwd)
580 stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
581 'log msg', wc_dir)
582 if not (stdout_lines == [] or stderr_lines == []):
583 raise svntest.Failure
585 # test for issue #1808: svn up deletes local symlink that obstructs
586 # versioned file
587 def update_obstructing_symlink(sbox):
588 "symlink obstructs incoming delete"
590 sbox.build()
591 wc_dir = sbox.wc_dir
592 mu_path = os.path.join(wc_dir, 'A', 'mu')
593 mu_url = sbox.repo_url + '/A/mu'
594 iota_path = os.path.join(wc_dir, 'iota')
596 # delete A/mu and replace it with a symlink
597 svntest.main.run_svn(None, 'rm', mu_path)
598 os.symlink(iota_path, mu_path)
600 svntest.main.run_svn(None, 'rm', mu_url,
601 '-m', 'log msg')
603 svntest.main.run_svn(None,
604 'up', wc_dir)
606 # check that the symlink is still there
607 target = os.readlink(mu_path)
608 if target != iota_path:
609 raise svntest.Failure
612 ########################################################################
613 # Run the tests
616 # list all tests here, starting with None:
617 test_list = [ None,
618 SkipUnless(general_symlink, svntest.main.is_posix_os),
619 SkipUnless(replace_file_with_symlink, svntest.main.is_posix_os),
620 SkipUnless(import_export_symlink, svntest.main.is_posix_os),
621 SkipUnless(copy_tree_with_symlink, svntest.main.is_posix_os),
622 SkipUnless(replace_symlink_with_file, svntest.main.is_posix_os),
623 SkipUnless(remove_symlink, svntest.main.is_posix_os),
624 SkipUnless(merge_symlink_into_file, svntest.main.is_posix_os),
625 SkipUnless(merge_file_into_symlink, svntest.main.is_posix_os),
626 checkout_repo_with_symlinks,
627 XFail(SkipUnless(diff_symlink_to_dir, svntest.main.is_posix_os)),
628 checkout_repo_with_unknown_special_type,
629 replace_symlink_with_dir,
630 SkipUnless(update_obstructing_symlink, svntest.main.is_posix_os),
633 if __name__ == '__main__':
634 svntest.main.run_tests(test_list)
635 # NOTREACHED
638 ### End of file.