Fix compiler warning due to missing function prototype.
[svn.git] / subversion / tests / cmdline / schedule_tests.py
blob78b06adf2f6c0f8a33c402489ca1f3ba9bd6b45b
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(read_only = True)
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(read_only = True)
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(read_only = True)
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(read_only = True)
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(read_only = True)
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(read_only = True)
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 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
280 'revert',
281 '--recursive',
282 wc_dir)
283 check_reversion(files, output)
285 #----------------------------------------------------------------------
287 def revert_add_directories(sbox):
288 "revert: add some directories"
290 add_directories(sbox)
291 wc_dir = sbox.wc_dir
293 # Revert our changes recursively from wc_dir.
294 X_path = os.path.join(wc_dir, 'X')
295 Y_path = os.path.join(wc_dir, 'A', 'C', 'Y')
296 Z_path = os.path.join(wc_dir, 'A', 'D', 'H', 'Z')
297 files = [X_path, Y_path, Z_path]
299 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
300 'revert',
301 '--recursive',
302 wc_dir)
303 check_reversion(files, output)
305 #----------------------------------------------------------------------
307 def revert_nested_adds(sbox):
308 "revert: add some nested files and directories"
310 nested_adds(sbox)
311 wc_dir = sbox.wc_dir
313 # Revert our changes recursively from wc_dir.
314 X_path = os.path.join(wc_dir, 'X')
315 Y_path = os.path.join(wc_dir, 'A', 'C', 'Y')
316 Z_path = os.path.join(wc_dir, 'A', 'D', 'H', 'Z')
317 files = [X_path, Y_path, Z_path]
319 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
320 'revert',
321 '--recursive',
322 wc_dir)
323 check_reversion(files, output)
325 #----------------------------------------------------------------------
327 def revert_add_executable(sbox):
328 "revert: add some executable files"
330 add_executable(sbox)
331 wc_dir = sbox.wc_dir
333 all_path = os.path.join(wc_dir, 'all_exe')
334 none_path = os.path.join(wc_dir, 'none_exe')
335 user_path = os.path.join(wc_dir, 'user_exe')
336 group_path = os.path.join(wc_dir, 'group_exe')
337 other_path = os.path.join(wc_dir, 'other_exe')
338 files = [all_path, none_path, user_path, group_path, other_path]
340 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
341 'revert',
342 '--recursive',
343 wc_dir)
344 check_reversion(files, output)
346 #----------------------------------------------------------------------
348 def revert_delete_files(sbox):
349 "revert: delete some files"
351 delete_files(sbox)
352 wc_dir = sbox.wc_dir
354 # Revert our changes recursively from wc_dir.
355 iota_path = os.path.join(wc_dir, 'iota')
356 mu_path = os.path.join(wc_dir, 'A', 'mu')
357 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
358 omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
359 files = [iota_path, mu_path, omega_path, rho_path]
361 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
362 'revert',
363 '--recursive',
364 wc_dir)
365 check_reversion(files, output)
367 #----------------------------------------------------------------------
369 def revert_delete_dirs(sbox):
370 "revert: delete some directories"
372 delete_dirs(sbox)
373 wc_dir = sbox.wc_dir
375 # Revert our changes recursively from wc_dir.
376 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
377 F_path = os.path.join(wc_dir, 'A', 'B', 'F')
378 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
379 alpha_path = os.path.join(E_path, 'alpha')
380 beta_path = os.path.join(E_path, 'beta')
381 chi_path = os.path.join(H_path, 'chi')
382 omega_path = os.path.join(H_path, 'omega')
383 psi_path = os.path.join(H_path, 'psi')
384 files = [E_path, F_path, H_path,
385 alpha_path, beta_path, chi_path, omega_path, psi_path]
387 exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
388 'revert',
389 '--recursive',
390 wc_dir)
391 check_reversion(files, output)
394 #######################################################################
395 # Regression tests
398 #----------------------------------------------------------------------
399 # Regression test for issue #863:
401 # Suppose here is a scheduled-add file or directory which is
402 # also missing. If I want to make the working copy forget all
403 # knowledge of the item ("unschedule" the addition), then either 'svn
404 # revert' or 'svn rm' will make that happen by removing the entry from
405 # .svn/entries file. While 'svn revert' does with no error,
406 # 'svn rm' does it with error.
408 def unschedule_missing_added(sbox):
409 "unschedule addition on missing items"
411 sbox.build(read_only = True)
412 wc_dir = sbox.wc_dir
414 # Create some files and dirs, then schedule them for addition
415 file1_path = os.path.join(wc_dir, 'file1')
416 file2_path = os.path.join(wc_dir, 'file2')
417 dir1_path = os.path.join(wc_dir, 'dir1')
418 dir2_path = os.path.join(wc_dir, 'dir2')
420 svntest.main.file_append(file1_path, "This is the file 'file1'.")
421 svntest.main.file_append(file2_path, "This is the file 'file2'.")
422 svntest.main.run_svn(None, 'add', file1_path, file2_path)
423 svntest.main.run_svn(None, 'mkdir', dir1_path, dir2_path)
425 # Make sure the 4 adds show up as such in status
426 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
427 expected_status.add({
428 'file1' : Item(status='A ', wc_rev=0),
429 'file2' : Item(status='A ', wc_rev=0),
430 'dir1' : Item(status='A ', wc_rev=0),
431 'dir2' : Item(status='A ', wc_rev=0),
434 svntest.actions.run_and_verify_status(wc_dir, expected_status)
436 # Poof, all 4 added things are now missing in action.
437 os.remove(file1_path)
438 os.remove(file2_path)
439 svntest.main.safe_rmtree(dir1_path)
440 svntest.main.safe_rmtree(dir2_path)
442 # Unschedule the additions, using 'svn rm' and 'svn revert'.
443 svntest.main.run_svn(svntest.verify.AnyOutput, 'rm', file1_path)
444 svntest.main.run_svn(svntest.verify.AnyOutput, 'rm', dir1_path)
445 svntest.main.run_svn(None, 'revert', file2_path, dir2_path)
447 # 'svn st' should now show absolutely zero local mods.
448 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
449 svntest.actions.run_and_verify_status(wc_dir, expected_status)
451 #----------------------------------------------------------------------
452 # Regression test for issue #962:
454 # Make sure 'rm foo; svn rm foo' works on files and directories.
455 # Also make sure that the deletion is committable.
457 def delete_missing(sbox):
458 "schedule and commit deletion on missing items"
460 sbox.build()
461 wc_dir = sbox.wc_dir
463 mu_path = os.path.join(wc_dir, 'A', 'mu')
464 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
466 # Manually remove a file and a directory.
467 os.remove(mu_path)
468 svntest.main.safe_rmtree(H_path)
470 # Now schedule them for deletion anyway, and make sure no error is output.
471 svntest.actions.run_and_verify_svn(None, None, [], 'rm', mu_path, H_path)
473 # Commit the deletions.
474 expected_output = svntest.wc.State(wc_dir, {
475 'A/mu' : Item(verb='Deleting'),
476 'A/D/H' : Item(verb='Deleting'),
479 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
480 expected_status.remove('A/mu', 'A/D/H',
481 'A/D/H/psi', 'A/D/H/omega', 'A/D/H/chi')
482 expected_status.tweak(wc_rev=1)
484 svntest.actions.run_and_verify_commit(wc_dir,
485 expected_output,
486 expected_status,
487 None, wc_dir)
489 #----------------------------------------------------------------------
490 # Regression test for issue #854:
491 # Revert . inside an svn added empty directory should generate an error.
493 def revert_inside_newly_added_dir(sbox):
494 "revert inside a newly added dir"
496 sbox.build(read_only = True)
497 wc_dir = sbox.wc_dir
499 os.chdir(wc_dir)
501 # Schedule a new directory for addition
502 os.mkdir('foo')
503 svntest.main.run_svn(None, 'add', 'foo')
505 # Now change into the newly added directory, revert and make sure
506 # an error is output.
507 os.chdir('foo')
508 svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
509 'revert', '.')
511 #----------------------------------------------------------------------
512 # Regression test for issue #1609:
513 # 'svn status' should show a schedule-add directory as 'A' not '?'
515 def status_add_deleted_directory(sbox):
516 "status after add of deleted directory"
518 sbox.build()
519 wc_dir = sbox.wc_dir
521 # The original recipe:
523 # svnadmin create repo
524 # svn mkdir file://`pwd`/repo/foo -m r1
525 # svn co file://`pwd`/repo wc
526 # svn rm wc/foo
527 # rm -rf wc/foo
528 # svn ci wc -m r2
529 # svn mkdir wc/foo
531 A_path = os.path.join(wc_dir, 'A')
532 svntest.actions.run_and_verify_svn(None, None, [], 'rm', A_path)
533 svntest.main.safe_rmtree(A_path)
534 svntest.actions.run_and_verify_svn(None, None, [],
535 'ci', '-m', 'log msg', wc_dir)
536 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', A_path)
538 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
539 expected_status = svntest.wc.State(wc_dir,
540 { '' : Item(status=' ', wc_rev=1),
541 'A' : Item(status='A ', wc_rev=0),
542 'iota' : Item(status=' ', wc_rev=1),
544 svntest.actions.run_and_verify_status(wc_dir, expected_status)
546 # Update will *not* remove the entry for A despite it being marked
547 # deleted.
548 svntest.actions.run_and_verify_svn(None, ['At revision 2.\n'], [],
549 'up', wc_dir)
550 expected_status.tweak('', 'iota', wc_rev=2)
551 svntest.actions.run_and_verify_status(wc_dir, expected_status)
554 #----------------------------------------------------------------------
555 # Regression test for issue #939:
556 # Recursive 'svn add' should still traverse already-versioned dirs.
557 def add_recursive_already_versioned(sbox):
558 "'svn add' should traverse already-versioned dirs"
560 wc_dir = sbox.wc_dir
562 if svntest.actions.make_repo_and_wc(sbox):
563 return 1
565 # Create some files, then schedule them for addition
566 delta_path = os.path.join(wc_dir, 'delta')
567 zeta_path = os.path.join(wc_dir, 'A', 'B', 'zeta')
568 epsilon_path = os.path.join(wc_dir, 'A', 'D', 'G', 'epsilon')
570 svntest.main.file_append(delta_path, "This is the file 'delta'.")
571 svntest.main.file_append(zeta_path, "This is the file 'zeta'.")
572 svntest.main.file_append(epsilon_path, "This is the file 'epsilon'.")
574 # Make sure the adds show up as such in status
575 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
576 expected_status.add({
577 'delta' : Item(status='A ', wc_rev=0),
578 'A/B/zeta' : Item(status='A ', wc_rev=0),
579 'A/D/G/epsilon' : Item(status='A ', wc_rev=0),
582 # Perform the add with the --force flag, and check the status.
583 ### TODO: This part won't work -- you have to be inside the working copy
584 ### or else Subversion will think you're trying to add the working copy
585 ### to its parent directory, and will (possibly, if the parent directory
586 ### isn't versioned) fail.
587 #svntest.main.run_svn(None, 'add', '--force', wc_dir)
588 #svntest.actions.run_and_verify_status(wc_dir, expected_status)
590 # Now revert, and do the adds again from inside the working copy.
591 svntest.main.run_svn(None, 'revert', '--recursive', wc_dir)
592 saved_wd = os.getcwd()
593 os.chdir(wc_dir)
594 svntest.main.run_svn(None, 'add', '--force', '.')
595 os.chdir(saved_wd)
596 svntest.actions.run_and_verify_status(wc_dir, expected_status)
599 #----------------------------------------------------------------------
600 # Regression test for the case where "svn mkdir" outside a working copy
601 # would create a directory, but then not clean up after itself when it
602 # couldn't add it to source control.
603 def fail_add_directory(sbox):
604 "'svn mkdir' should clean up after itself on error"
605 # This test doesn't use a working copy
606 svntest.main.safe_rmtree(sbox.wc_dir)
607 os.makedirs(sbox.wc_dir)
609 os.chdir(sbox.wc_dir)
610 svntest.actions.run_and_verify_svn('Failed mkdir',
611 None, svntest.verify.AnyOutput,
612 'mkdir', 'A')
613 if os.path.exists('A'):
614 raise svntest.Failure('svn mkdir created an unversioned directory')
617 #----------------------------------------------------------------------
618 # Regression test for #2440
619 # Ideally this test should test for the exit status of the
620 # 'svn rm non-existent' invocation.
621 # As the corresponding change to get the exit code of svn binary invoked needs
622 # a change in many testcase, for now this testcase checks the stderr.
623 def delete_non_existent(sbox):
624 "'svn rm non-existent' should exit with an error"
626 sbox.build(read_only = True)
627 wc_dir = sbox.wc_dir
629 os.chdir(wc_dir)
630 svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
631 'rm', '--force', 'non-existent')
633 ########################################################################
634 # Run the tests
637 # list all tests here, starting with None:
638 test_list = [ None,
639 revert_add_files,
640 revert_add_directories,
641 revert_nested_adds,
642 SkipUnless(revert_add_executable, svntest.main.is_posix_os),
643 revert_delete_files,
644 revert_delete_dirs,
645 unschedule_missing_added,
646 delete_missing,
647 revert_inside_newly_added_dir,
648 status_add_deleted_directory,
649 add_recursive_already_versioned,
650 fail_add_directory,
651 delete_non_existent,
654 if __name__ == '__main__':
655 svntest.main.run_tests(test_list)
656 # NOTREACHED
659 ### End of file.