In the command-line client, forbid
[svn.git] / subversion / tests / cmdline / schedule_tests.py
blob575196791ffceddbc31cd3a4164fd7377e346f59
1 #!/usr/bin/env python
3 # schedule_tests.py: testing working copy scheduling
4 # (adds, deletes, reversion)
6 # Subversion is a tool for revision control.
7 # See http://subversion.tigris.org for more information.
9 # ====================================================================
10 # Copyright (c) 2000-2004 CollabNet. All rights reserved.
12 # This software is licensed as described in the file COPYING, which
13 # you should have received as part of this distribution. The terms
14 # are also available at http://subversion.tigris.org/license-1.html.
15 # If newer versions of this license are posted there, you may use a
16 # newer version instead, at your option.
18 ######################################################################
20 # General modules
21 import os
23 # Our testing module
24 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 #######################################################################
40 # Stage I - Schedules and modifications, verified with `svn status'
42 # These tests make schedule changes and local mods, and verify that status
43 # output is as expected. In a second stage, reversion of these changes is
44 # tested. Potentially, a third stage could test committing these same
45 # changes.
47 # NOTE: these tests are run within the Stage II tests, not on their own.
50 def add_files(sbox):
51 "schedule: add some files"
53 sbox.build()
54 wc_dir = sbox.wc_dir
56 # Create some files, then schedule them for addition
57 delta_path = os.path.join(wc_dir, 'delta')
58 zeta_path = os.path.join(wc_dir, 'A', 'B', 'zeta')
59 epsilon_path = os.path.join(wc_dir, 'A', 'D', 'G', 'epsilon')
61 svntest.main.file_append(delta_path, "This is the file 'delta'.")
62 svntest.main.file_append(zeta_path, "This is the file 'zeta'.")
63 svntest.main.file_append(epsilon_path, "This is the file 'epsilon'.")
65 svntest.main.run_svn(None, 'add', delta_path, zeta_path, epsilon_path)
67 # Make sure the adds show up as such in status
68 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
69 expected_status.add({
70 'delta' : Item(status='A ', wc_rev=0),
71 'A/B/zeta' : Item(status='A ', wc_rev=0),
72 'A/D/G/epsilon' : Item(status='A ', wc_rev=0),
75 svntest.actions.run_and_verify_status(wc_dir, expected_status)
77 #----------------------------------------------------------------------
79 def add_directories(sbox):
80 "schedule: add some directories"
82 sbox.build()
83 wc_dir = sbox.wc_dir
85 # Create some directories, then schedule them for addition
86 X_path = os.path.join(wc_dir, 'X')
87 Y_path = os.path.join(wc_dir, 'A', 'C', 'Y')
88 Z_path = os.path.join(wc_dir, 'A', 'D', 'H', 'Z')
90 os.mkdir(X_path)
91 os.mkdir(Y_path)
92 os.mkdir(Z_path)
94 svntest.main.run_svn(None, 'add', X_path, Y_path, Z_path)
96 # Make sure the adds show up as such in status
97 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
98 expected_status.add({
99 'X' : Item(status='A ', wc_rev=0),
100 'A/C/Y' : Item(status='A ', wc_rev=0),
101 'A/D/H/Z' : Item(status='A ', wc_rev=0),
104 svntest.actions.run_and_verify_status(wc_dir, expected_status)
106 #----------------------------------------------------------------------
108 def nested_adds(sbox):
109 "schedule: add some nested files and directories"
111 sbox.build()
112 wc_dir = sbox.wc_dir
114 # Create some directories then schedule them for addition
115 X_path = os.path.join(wc_dir, 'X')
116 Y_path = os.path.join(wc_dir, 'A', 'C', 'Y')
117 Z_path = os.path.join(wc_dir, 'A', 'D', 'H', 'Z')
119 os.mkdir(X_path)
120 os.mkdir(Y_path)
121 os.mkdir(Z_path)
123 # Now, create some files and directories to put into our newly added
124 # directories
125 P_path = os.path.join(X_path, 'P')
126 Q_path = os.path.join(Y_path, 'Q')
127 R_path = os.path.join(Z_path, 'R')
129 os.mkdir(P_path)
130 os.mkdir(Q_path)
131 os.mkdir(R_path)
133 delta_path = os.path.join(X_path, 'delta')
134 epsilon_path = os.path.join(Y_path, 'epsilon')
135 upsilon_path = os.path.join(Y_path, 'upsilon')
136 zeta_path = os.path.join(Z_path, 'zeta')
138 svntest.main.file_append(delta_path, "This is the file 'delta'.")
139 svntest.main.file_append(epsilon_path, "This is the file 'epsilon'.")
140 svntest.main.file_append(upsilon_path, "This is the file 'upsilon'.")
141 svntest.main.file_append(zeta_path, "This is the file 'zeta'.")
143 # Finally, let's try adding our new files and directories
144 svntest.main.run_svn(None, 'add', X_path, Y_path, Z_path)
146 # Make sure the adds show up as such in status
147 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
148 expected_status.add({
149 'X' : Item(status='A ', wc_rev=0),
150 'A/C/Y' : Item(status='A ', wc_rev=0),
151 'A/D/H/Z' : Item(status='A ', wc_rev=0),
152 'X/P' : Item(status='A ', wc_rev=0),
153 'A/C/Y/Q' : Item(status='A ', wc_rev=0),
154 'A/D/H/Z/R' : Item(status='A ', wc_rev=0),
155 'X/delta' : Item(status='A ', wc_rev=0),
156 'A/C/Y/epsilon' : Item(status='A ', wc_rev=0),
157 'A/C/Y/upsilon' : Item(status='A ', wc_rev=0),
158 'A/D/H/Z/zeta' : Item(status='A ', wc_rev=0),
161 svntest.actions.run_and_verify_status(wc_dir, expected_status)
163 #----------------------------------------------------------------------
165 def add_executable(sbox):
166 "schedule: add some executable files"
168 sbox.build()
170 def runTest(wc_dir, fileName, perm, executable):
171 fileName = os.path.join(wc_dir, fileName)
172 if executable:
173 expected_out = ["*\n"]
174 else:
175 expected_out = []
176 f = open(fileName,"w")
177 f.close()
178 os.chmod(fileName,perm)
179 svntest.main.run_svn(None, 'add', fileName)
180 svntest.actions.run_and_verify_svn(None, expected_out, [],
181 'propget', "svn:executable", fileName)
183 test_cases = [
184 ("all_exe", 0777, 1),
185 ("none_exe", 0666, 0),
186 ("user_exe", 0766, 1),
187 ("group_exe", 0676, 0),
188 ("other_exe", 0667, 0),
190 for test_case in test_cases:
191 runTest(sbox.wc_dir, *test_case)
193 #----------------------------------------------------------------------
195 def delete_files(sbox):
196 "schedule: delete some files"
198 sbox.build()
199 wc_dir = sbox.wc_dir
201 # Schedule some files for deletion
202 iota_path = os.path.join(wc_dir, 'iota')
203 mu_path = os.path.join(wc_dir, 'A', 'mu')
204 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
205 omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
207 svntest.main.run_svn(None, 'del', iota_path, mu_path, rho_path, omega_path)
209 # Make sure the deletes show up as such in status
210 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
211 expected_status.tweak('iota', 'A/mu', 'A/D/G/rho', 'A/D/H/omega',
212 status='D ')
214 svntest.actions.run_and_verify_status(wc_dir, expected_status)
216 #----------------------------------------------------------------------
218 def delete_dirs(sbox):
219 "schedule: delete some directories"
221 sbox.build()
222 wc_dir = sbox.wc_dir
224 # Schedule some directories for deletion (this is recursive!)
225 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
226 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
227 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
228 alpha_path = os.path.join(E_path, 'alpha')
229 beta_path = os.path.join(E_path, 'beta')
230 chi_path = os.path.join(H_path, 'chi')
231 omega_path = os.path.join(H_path, 'omega')
232 psi_path = os.path.join(H_path, 'psi')
234 # Now, delete (recursively) the directories.
235 svntest.main.run_svn(None, 'del', E_path, F_path, H_path)
237 # Make sure the deletes show up as such in status
238 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
239 expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
240 'A/B/F',
241 'A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi',
242 status='D ')
244 svntest.actions.run_and_verify_status(wc_dir, expected_status)
247 #######################################################################
248 # Stage II - Reversion of changes made in Stage I
250 # Each test in Stage II calls the corresponding Stage I test
251 # and then also tests reversion of those changes.
254 def check_reversion(files, output):
255 expected_output = []
256 for file in files:
257 expected_output = expected_output + ["Reverted '" + file + "'\n"]
258 output.sort()
259 expected_output.sort()
260 if output != expected_output:
261 print "Expected output:", expected_output
262 print "Actual output: ", output
263 raise svntest.Failure
265 #----------------------------------------------------------------------
267 def revert_add_files(sbox):
268 "revert: add some files"
270 add_files(sbox)
271 wc_dir = sbox.wc_dir
273 # Revert our changes recursively from wc_dir.
274 delta_path = os.path.join(wc_dir, 'delta')
275 zeta_path = os.path.join(wc_dir, 'A', 'B', 'zeta')
276 epsilon_path = os.path.join(wc_dir, 'A', 'D', 'G', 'epsilon')
277 files = [delta_path, zeta_path, epsilon_path]
279 output, err = svntest.actions.run_and_verify_svn(None, None, [],
280 'revert',
281 '--recursive', wc_dir)
282 check_reversion(files, output)
284 #----------------------------------------------------------------------
286 def revert_add_directories(sbox):
287 "revert: add some directories"
289 add_directories(sbox)
290 wc_dir = sbox.wc_dir
292 # Revert our changes recursively from wc_dir.
293 X_path = os.path.join(wc_dir, 'X')
294 Y_path = os.path.join(wc_dir, 'A', 'C', 'Y')
295 Z_path = os.path.join(wc_dir, 'A', 'D', 'H', 'Z')
296 files = [X_path, Y_path, Z_path]
298 output, err = svntest.actions.run_and_verify_svn(None, None, [],
299 'revert',
300 '--recursive', wc_dir)
301 check_reversion(files, output)
303 #----------------------------------------------------------------------
305 def revert_nested_adds(sbox):
306 "revert: add some nested files and directories"
308 nested_adds(sbox)
309 wc_dir = sbox.wc_dir
311 # Revert our changes recursively from wc_dir.
312 X_path = os.path.join(wc_dir, 'X')
313 Y_path = os.path.join(wc_dir, 'A', 'C', 'Y')
314 Z_path = os.path.join(wc_dir, 'A', 'D', 'H', 'Z')
315 files = [X_path, Y_path, Z_path]
317 output, err = svntest.actions.run_and_verify_svn(None, None, [],
318 'revert',
319 '--recursive', wc_dir)
320 check_reversion(files, output)
322 #----------------------------------------------------------------------
324 def revert_add_executable(sbox):
325 "revert: add some executable files"
327 add_executable(sbox)
328 wc_dir = sbox.wc_dir
330 all_path = os.path.join(wc_dir, 'all_exe')
331 none_path = os.path.join(wc_dir, 'none_exe')
332 user_path = os.path.join(wc_dir, 'user_exe')
333 group_path = os.path.join(wc_dir, 'group_exe')
334 other_path = os.path.join(wc_dir, 'other_exe')
335 files = [all_path, none_path, user_path, group_path, other_path]
337 output, err = svntest.actions.run_and_verify_svn(None, None, [],
338 'revert',
339 '--recursive', wc_dir)
340 check_reversion(files, output)
342 #----------------------------------------------------------------------
344 def revert_delete_files(sbox):
345 "revert: delete some files"
347 delete_files(sbox)
348 wc_dir = sbox.wc_dir
350 # Revert our changes recursively from wc_dir.
351 iota_path = os.path.join(wc_dir, 'iota')
352 mu_path = os.path.join(wc_dir, 'A', 'mu')
353 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
354 omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
355 files = [iota_path, mu_path, omega_path, rho_path]
357 output, err = svntest.actions.run_and_verify_svn(None, None, [],
358 'revert',
359 '--recursive', wc_dir)
360 check_reversion(files, output)
362 #----------------------------------------------------------------------
364 def revert_delete_dirs(sbox):
365 "revert: delete some directories"
367 delete_dirs(sbox)
368 wc_dir = sbox.wc_dir
370 # Revert our changes recursively from wc_dir.
371 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
372 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
373 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
374 alpha_path = os.path.join(E_path, 'alpha')
375 beta_path = os.path.join(E_path, 'beta')
376 chi_path = os.path.join(H_path, 'chi')
377 omega_path = os.path.join(H_path, 'omega')
378 psi_path = os.path.join(H_path, 'psi')
379 files = [E_path, F_path, H_path,
380 alpha_path, beta_path, chi_path, omega_path, psi_path]
382 output, err = svntest.actions.run_and_verify_svn(None, None, [],
383 'revert',
384 '--recursive', wc_dir)
385 check_reversion(files, output)
388 #######################################################################
389 # Regression tests
392 #----------------------------------------------------------------------
393 # Regression test for issue #863:
395 # Suppose here is a scheduled-add file or directory which is
396 # also missing. If I want to make the working copy forget all
397 # knowledge of the item ("unschedule" the addition), then either 'svn
398 # revert' or 'svn rm' will make that happen by removing the entry from
399 # .svn/entries file. While 'svn revert' does with no error,
400 # 'svn rm' does it with error.
402 def unschedule_missing_added(sbox):
403 "unschedule addition on missing items"
405 sbox.build()
406 wc_dir = sbox.wc_dir
408 # Create some files and dirs, then schedule them for addition
409 file1_path = os.path.join(wc_dir, 'file1')
410 file2_path = os.path.join(wc_dir, 'file2')
411 dir1_path = os.path.join(wc_dir, 'dir1')
412 dir2_path = os.path.join(wc_dir, 'dir2')
414 svntest.main.file_append(file1_path, "This is the file 'file1'.")
415 svntest.main.file_append(file2_path, "This is the file 'file2'.")
416 svntest.main.run_svn(None, 'add', file1_path, file2_path)
417 svntest.main.run_svn(None, 'mkdir', dir1_path, dir2_path)
419 # Make sure the 4 adds show up as such in status
420 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
421 expected_status.add({
422 'file1' : Item(status='A ', wc_rev=0),
423 'file2' : Item(status='A ', wc_rev=0),
424 'dir1' : Item(status='A ', wc_rev=0),
425 'dir2' : Item(status='A ', wc_rev=0),
428 svntest.actions.run_and_verify_status(wc_dir, expected_status)
430 # Poof, all 4 added things are now missing in action.
431 os.remove(file1_path)
432 os.remove(file2_path)
433 svntest.main.safe_rmtree(dir1_path)
434 svntest.main.safe_rmtree(dir2_path)
436 # Unschedule the additions, using 'svn rm' and 'svn revert'.
437 svntest.main.run_svn(svntest.verify.AnyOutput, 'rm', file1_path)
438 svntest.main.run_svn(svntest.verify.AnyOutput, 'rm', dir1_path)
439 svntest.main.run_svn(None, 'revert', file2_path, dir2_path)
441 # 'svn st' should now show absolutely zero local mods.
442 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
443 svntest.actions.run_and_verify_status(wc_dir, expected_status)
445 #----------------------------------------------------------------------
446 # Regression test for issue #962:
448 # Make sure 'rm foo; svn rm foo' works on files and directories.
449 # Also make sure that the deletion is committable.
451 def delete_missing(sbox):
452 "schedule and commit deletion on missing items"
454 sbox.build()
455 wc_dir = sbox.wc_dir
457 mu_path = os.path.join(wc_dir, 'A', 'mu')
458 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
460 # Manually remove a file and a directory.
461 os.remove(mu_path)
462 svntest.main.safe_rmtree(H_path)
464 # Now schedule them for deletion anyway, and make sure no error is output.
465 svntest.actions.run_and_verify_svn(None, None, [], 'rm', mu_path, H_path)
467 # Commit the deletions.
468 expected_output = svntest.wc.State(wc_dir, {
469 'A/mu' : Item(verb='Deleting'),
470 'A/D/H' : Item(verb='Deleting'),
473 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
474 expected_status.remove('A/mu', 'A/D/H',
475 'A/D/H/psi', 'A/D/H/omega', 'A/D/H/chi')
476 expected_status.tweak(wc_rev=1)
478 svntest.actions.run_and_verify_commit(wc_dir,
479 expected_output,
480 expected_status,
481 None, None, None, None, None,
482 wc_dir)
484 #----------------------------------------------------------------------
485 # Regression test for issue #854:
486 # Revert . inside an svn added empty directory should generate an error.
488 def revert_inside_newly_added_dir(sbox):
489 "revert inside a newly added dir"
491 sbox.build()
492 wc_dir = sbox.wc_dir
494 os.chdir(wc_dir)
496 # Schedule a new directory for addition
497 os.mkdir('foo')
498 svntest.main.run_svn(None, 'add', 'foo')
500 # Now change into the newly added directory, revert and make sure
501 # an error is output.
502 os.chdir('foo')
503 svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
504 'revert', '.')
506 #----------------------------------------------------------------------
507 # Regression test for issue #1609:
508 # 'svn status' should show a schedule-add directory as 'A' not '?'
510 def status_add_deleted_directory(sbox):
511 "status after add of deleted directory"
513 sbox.build()
514 wc_dir = sbox.wc_dir
516 # The original recipe:
518 # svnadmin create repo
519 # svn mkdir file://`pwd`/repo/foo -m r1
520 # svn co file://`pwd`/repo wc
521 # svn rm wc/foo
522 # rm -rf wc/foo
523 # svn ci wc -m r2
524 # svn mkdir wc/foo
526 A_path = os.path.join(wc_dir, 'A')
527 svntest.actions.run_and_verify_svn(None, None, [], 'rm', A_path)
528 svntest.main.safe_rmtree(A_path)
529 svntest.actions.run_and_verify_svn(None, None, [],
530 'ci', '-m', 'log msg', wc_dir)
531 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', A_path)
533 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
534 expected_status = svntest.wc.State(wc_dir,
535 { '' : Item(status=' ', wc_rev=1),
536 'A' : Item(status='A ', wc_rev=0),
537 'iota' : Item(status=' ', wc_rev=1),
539 svntest.actions.run_and_verify_status(wc_dir, expected_status)
541 # Update will *not* remove the entry for A despite it being marked
542 # deleted.
543 svntest.actions.run_and_verify_svn(None, ['At revision 2.\n'], [],
544 'up', wc_dir)
545 expected_status.tweak('', 'iota', wc_rev=2)
546 svntest.actions.run_and_verify_status(wc_dir, expected_status)
549 #----------------------------------------------------------------------
550 # Regression test for issue #939:
551 # Recursive 'svn add' should still traverse already-versioned dirs.
552 def add_recursive_already_versioned(sbox):
553 "'svn add' should traverse already-versioned dirs"
555 wc_dir = sbox.wc_dir
557 if svntest.actions.make_repo_and_wc(sbox):
558 return 1
560 # Create some files, then schedule them for addition
561 delta_path = os.path.join(wc_dir, 'delta')
562 zeta_path = os.path.join(wc_dir, 'A', 'B', 'zeta')
563 epsilon_path = os.path.join(wc_dir, 'A', 'D', 'G', 'epsilon')
565 svntest.main.file_append(delta_path, "This is the file 'delta'.")
566 svntest.main.file_append(zeta_path, "This is the file 'zeta'.")
567 svntest.main.file_append(epsilon_path, "This is the file 'epsilon'.")
569 # Make sure the adds show up as such in status
570 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
571 expected_status.add({
572 'delta' : Item(status='A ', wc_rev=0),
573 'A/B/zeta' : Item(status='A ', wc_rev=0),
574 'A/D/G/epsilon' : Item(status='A ', wc_rev=0),
577 # Perform the add with the --force flag, and check the status.
578 ### TODO: This part won't work -- you have to be inside the working copy
579 ### or else Subversion will think you're trying to add the working copy
580 ### to its parent directory, and will (possibly, if the parent directory
581 ### isn't versioned) fail.
582 #svntest.main.run_svn(None, 'add', '--force', wc_dir)
583 #svntest.actions.run_and_verify_status(wc_dir, expected_status)
585 # Now revert, and do the adds again from inside the working copy.
586 svntest.main.run_svn(None, 'revert', '--recursive', wc_dir)
587 saved_wd = os.getcwd()
588 os.chdir(wc_dir)
589 svntest.main.run_svn(None, 'add', '--force', '.')
590 os.chdir(saved_wd)
591 svntest.actions.run_and_verify_status(wc_dir, expected_status)
594 #----------------------------------------------------------------------
595 # Regression test for the case where "svn mkdir" outside a working copy
596 # would create a directory, but then not clean up after itself when it
597 # couldn't add it to source control.
598 def fail_add_directory(sbox):
599 "'svn mkdir' should clean up after itself on error"
600 # This test doesn't use a working copy
601 svntest.main.safe_rmtree(sbox.wc_dir)
602 os.makedirs(sbox.wc_dir)
604 os.chdir(sbox.wc_dir)
605 svntest.actions.run_and_verify_svn('Failed mkdir',
606 None, svntest.verify.AnyOutput,
607 'mkdir', 'A')
608 if os.path.exists('A'):
609 raise svntest.Failure('svn mkdir created an unversioned directory')
612 #----------------------------------------------------------------------
613 # Regression test for #2440
614 # Ideally this test should test for the exit status of the
615 # 'svn rm non-existent' invocation.
616 # As the corresponding change to get the exit code of svn binary invoked needs
617 # a change in many testcase, for now this testcase checks the stderr.
618 def delete_non_existent(sbox):
619 "'svn rm non-existent' should exit with an error"
621 sbox.build()
622 wc_dir = sbox.wc_dir
624 os.chdir(wc_dir)
625 svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
626 'rm', '--force', 'non-existent')
628 ########################################################################
629 # Run the tests
632 # list all tests here, starting with None:
633 test_list = [ None,
634 revert_add_files,
635 revert_add_directories,
636 revert_nested_adds,
637 SkipUnless(revert_add_executable, svntest.main.is_posix_os),
638 revert_delete_files,
639 revert_delete_dirs,
640 unschedule_missing_added,
641 delete_missing,
642 revert_inside_newly_added_dir,
643 status_add_deleted_directory,
644 add_recursive_already_versioned,
645 fail_add_directory,
646 delete_non_existent,
649 if __name__ == '__main__':
650 svntest.main.run_tests(test_list)
651 # NOTREACHED
654 ### End of file.