3 # stat_tests.py: testing the svn stat command
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 ######################################################################
24 from svntest
import wc
27 Skip
= svntest
.testcase
.Skip
28 SkipUnless
= svntest
.testcase
.SkipUnless
29 XFail
= svntest
.testcase
.XFail
30 Item
= svntest
.wc
.StateItem
34 ######################################################################
37 # Each test must return on success or raise on failure.
39 #----------------------------------------------------------------------
41 def status_unversioned_file_in_current_dir(sbox
):
42 "status on unversioned file in current directory"
44 sbox
.build(read_only
= True)
49 svntest
.main
.file_append('foo', 'a new file')
51 svntest
.actions
.run_and_verify_svn(None, [ "? foo\n" ], [],
54 #----------------------------------------------------------------------
55 # Regression for issue #590
57 def status_update_with_nested_adds(sbox
):
58 "run 'status -u' when nested additions are pending"
63 # Make a backup copy of the working copy
64 wc_backup
= sbox
.add_wc_path('backup')
65 svntest
.actions
.duplicate_dir(wc_dir
, wc_backup
)
67 # Create newdir and newfile
68 newdir_path
= os
.path
.join(wc_dir
, 'newdir')
69 newfile_path
= os
.path
.join(wc_dir
, 'newdir', 'newfile')
70 os
.makedirs(newdir_path
)
71 svntest
.main
.file_append(newfile_path
, 'new text')
73 # Schedule newdir and newfile for addition (note that the add is recursive)
74 svntest
.main
.run_svn(None, 'add', newdir_path
)
76 # Created expected output tree for commit
77 expected_output
= svntest
.wc
.State(wc_dir
, {
78 'newdir' : Item(verb
='Adding'),
79 'newdir/newfile' : Item(verb
='Adding'),
82 # Create expected status tree; all local revisions should be at 1,
83 # but newdir and newfile should be at revision 2.
84 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
86 'newdir' : Item(status
=' ', wc_rev
=2),
87 'newdir/newfile' : Item(status
=' ', wc_rev
=2),
91 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
92 expected_status
, None, wc_dir
)
94 # Now we go to the backup working copy, still at revision 1.
95 # We will run 'svn st -u', and make sure that newdir/newfile is reported
96 # as a nonexistent (but pending) path.
98 # Create expected status tree; all local revisions should be at 1,
99 # but newdir and newfile should be present with 'blank' attributes.
100 expected_status
= svntest
.actions
.get_virginal_state(wc_backup
, 1)
102 # Verify status. Notice that we're running status *without* the
103 # --quiet flag, so the unversioned items will appear.
104 # Unfortunately, the regexp that we currently use to parse status
105 # output is unable to parse a line that has no working revision! If
106 # an error happens, we'll catch it here. So that's a good enough
107 # regression test for now. Someday, though, it would be nice to
108 # positively match the mostly-empty lines.
109 svntest
.actions
.run_and_verify_unquiet_status(wc_backup
,
112 #----------------------------------------------------------------------
114 # svn status -vN should include all entries in a directory
115 def status_shows_all_in_current_dir(sbox
):
116 "status -vN shows all items in current directory"
118 sbox
.build(read_only
= True)
122 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
125 if (len(output
) != len(os
.listdir("."))):
126 raise svntest
.Failure
129 #----------------------------------------------------------------------
131 def status_missing_file(sbox
):
132 "status with a versioned file missing"
134 sbox
.build(read_only
= True)
141 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
144 if not re
.match("! +iota", line
):
145 raise svntest
.Failure
147 # This invocation is for issue #2127.
148 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
153 if re
.match("! +1 +iota", line
):
156 raise svntest
.Failure
158 #----------------------------------------------------------------------
160 def status_type_change(sbox
):
161 "status on versioned items whose type has changed"
163 sbox
.build(read_only
= True)
168 # First replace a versioned dir with a file and a versioned file
169 # with a versioned dir.
170 os
.rename('iota', 'was_iota')
171 os
.rename('A', 'iota')
172 os
.rename('was_iota', 'A')
174 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
177 raise svntest
.Failure
179 if not re
.match("~ +(iota|A)", line
):
180 raise svntest
.Failure
182 # Now change the file that is obstructing the versioned dir into an
187 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
190 raise svntest
.Failure
192 if not re
.match("~ +(iota|A)", line
):
193 raise svntest
.Failure
195 # Now change the versioned dir that is obstructing the file into an
197 svntest
.main
.safe_rmtree('iota')
200 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
203 raise svntest
.Failure
205 if not re
.match("~ +(iota|A)", line
):
206 raise svntest
.Failure
208 #----------------------------------------------------------------------
210 def status_type_change_to_symlink(sbox
):
211 "status on versioned items replaced by symlinks"
213 sbox
.build(read_only
= True)
220 os
.symlink('foo', 'iota')
221 svntest
.main
.safe_rmtree('A/D')
222 os
.symlink('bar', 'A/D')
224 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
227 raise svntest
.Failure
229 if not re
.match("~ +(iota|A/D)", line
):
230 raise svntest
.Failure
235 os
.symlink('A/mu', 'iota')
236 os
.symlink('C', 'A/D')
238 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
241 raise svntest
.Failure
243 if not re
.match("~ +(iota|A/D)", line
):
244 raise svntest
.Failure
246 #----------------------------------------------------------------------
247 # Regression test for revision 3686.
249 def status_with_new_files_pending(sbox
):
250 "status -u with new files in the repository"
257 svntest
.main
.file_append('newfile', 'this is a new file')
258 svntest
.main
.run_svn(None, 'add', 'newfile')
259 svntest
.main
.run_svn(None,
260 'ci', '-m', 'logmsg')
261 svntest
.main
.run_svn(None,
264 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
267 # The bug fixed in revision 3686 was a seg fault. We don't have a
268 # reliable way to detect a seg fault here, since we haven't dealt
269 # with the popen2{Popen3,Popen4} mess in Python yet (the latter two
270 # are classes within the first, which is a module, and the Popen3
271 # class is not the same as os.popen3(). Got that?) See the Python
272 # docs for details; in the meantime, no output means there was a
275 if line
.find('newfile') != -1:
278 raise svntest
.Failure
280 #----------------------------------------------------------------------
282 def status_for_unignored_file(sbox
):
283 "status for unignored file and directory"
285 sbox
.build(read_only
= True)
290 # use a temp file to set properties with wildcards in their values
291 # otherwise Win32/VS2005 will expand them
292 svntest
.main
.file_append('proptmp', 'new*')
293 svntest
.main
.file_append('newfile', 'this is a new file')
294 os
.makedirs('newdir')
295 svntest
.main
.run_svn(None, 'propset', 'svn:ignore', '-F', 'proptmp', '.')
298 # status on the directory with --no-ignore
299 expected
= svntest
.verify
.UnorderedOutput(
303 svntest
.actions
.run_and_verify_svn(None,
306 'status', '--no-ignore', '.')
308 # status specifying the file explicitly on the command line
309 expected
= svntest
.verify
.UnorderedOutput(
312 svntest
.actions
.run_and_verify_svn(None,
315 'status', 'newdir', 'newfile')
317 #----------------------------------------------------------------------
319 def status_for_nonexistent_file(sbox
):
320 "status on missing and unversioned file"
322 sbox
.build(read_only
= True)
328 exit_code
, output
, err
= svntest
.actions
.run_and_verify_svn(
329 None, None, [], 'status', 'nonexistent-file')
331 # there should *not* be a status line printed for the nonexistent file
333 if re
.match(" +nonexistent-file", line
):
334 raise svntest
.Failure
336 #----------------------------------------------------------------------
338 def status_nonrecursive_update_different_cwd(sbox
):
339 "status -v -N -u from different current directories"
341 # check combination of status -u and -N
342 # create A/C/J in repository
343 # create A/C/K in working copy
344 # check status output with -u and -N on target C
345 # check status output with -u and -N on target . (in C)
350 J_url
= sbox
.repo_url
+ '/A/C/J'
351 K_path
= os
.path
.join(wc_dir
, 'A', 'C', 'K' )
353 svntest
.actions
.run_and_verify_svn(None, None, [],
354 'mkdir', '-m', 'rev 2', J_url
)
356 svntest
.actions
.run_and_verify_svn(None, None, [],
362 ' * %s\n' % os
.path
.join("C", "J"),
363 'A 0 ? ? %s\n' % os
.path
.join("C", "K"),
364 ' * 1 1 jrandom C\n',
365 'Status against revision: 2\n' ]
368 svntest
.actions
.run_and_verify_svn(None,
371 'status', '-v', '-N', '-u', 'C')
376 ' * 1 1 jrandom .\n',
377 'Status against revision: 2\n']
380 svntest
.actions
.run_and_verify_svn(None,
383 'status', '-v', '-N', '-u', '.')
386 #----------------------------------------------------------------------
388 def status_file_needs_update(sbox
):
389 "status -u indicates out-of-dateness"
393 # http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=27975
395 # Basically, Andreas was seeing inconsistent results depending on
396 # whether or not he accompanied 'svn status -u' with '-v':
405 # 56 6 k cron-daily.pl
406 # * 56 44 k crontab.root
411 # The first status should show the asterisk, too. There was never
412 # any issue for this bug, so this comment and the thread are your
418 other_wc
= sbox
.add_wc_path('other')
420 svntest
.actions
.duplicate_dir(wc_dir
, other_wc
)
422 was_cwd
= os
.getcwd()
425 svntest
.main
.file_append('crontab.root', 'New file crontab.root.\n')
426 svntest
.main
.run_svn(None, 'add', 'crontab.root')
427 svntest
.main
.run_svn(None,
428 'ci', '-m', 'log msg')
431 svntest
.main
.run_svn(None,
436 svntest
.main
.file_append('crontab.root', 'New line in crontab.root.\n')
437 svntest
.main
.run_svn(None,
438 'ci', '-m', 'log msg')
440 # The `svntest.actions.run_and_verify_*_status' routines all pass
441 # the -v flag, which we don't want, as this bug never appeared when
442 # -v was passed. So we run status by hand:
444 exit_code
, out
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
449 if re
.match("\\s+\\*.*crontab\\.root$", line
):
452 raise svntest
.Failure
454 #----------------------------------------------------------------------
456 def status_uninvited_parent_directory(sbox
):
457 "status -u on outdated, added file shows only that"
459 # To reproduce, check out working copies wc1 and wc2, then do:
462 # $ echo "new file" >> newfile
464 # $ svn ci -m 'log msg'
467 # $ echo "new file" >> newfile
471 # $ svn st wc2/newfile
473 # You *should* get one line of status output, for newfile. The bug
474 # is that you get two instead, one for newfile, and one for its
475 # parent directory, wc2/.
477 # This bug was originally discovered during investigations into
478 # issue #1042, "fixed" in revision 4181, then later the fix was
479 # reverted because it caused other status problems (see the test
480 # status_file_needs_update(), which fails when 4181 is present).
485 other_wc
= sbox
.add_wc_path('other')
487 svntest
.actions
.duplicate_dir(wc_dir
, other_wc
)
489 was_cwd
= os
.getcwd()
492 svntest
.main
.file_append('newfile', 'New file.\n')
493 svntest
.main
.run_svn(None, 'add', 'newfile')
494 svntest
.main
.run_svn(None,
495 'ci', '-m', 'log msg')
499 svntest
.main
.file_append('newfile', 'New file.\n')
500 svntest
.main
.run_svn(None, 'add', 'newfile')
504 # We don't want a full status tree here, just one line (or two, if
505 # the bug is present). So run status by hand:
507 exit_code
, out
, err
= svntest
.actions
.run_and_verify_svn(
509 'status', '-u', os
.path
.join(other_wc
, 'newfile'))
512 # The "/?" is just to allow for an optional trailing slash.
513 if re
.match("\\s+\\*.*\.other/?$", line
):
514 raise svntest
.Failure
516 def status_on_forward_deletion(sbox
):
517 "status -u on working copy deleted in HEAD"
519 sbox
.build(create_wc
= False)
522 top_url
= sbox
.repo_url
523 A_url
= top_url
+ '/A'
525 svntest
.main
.run_svn(None,
526 'rm', '-m', 'Remove A.', A_url
)
528 svntest
.main
.safe_rmtree(wc_dir
)
533 svntest
.main
.run_svn(None,
534 'co', '-r1', top_url
+ "@1", 'wc')
535 # If the bug is present, this will error with
537 # subversion/libsvn_wc/lock.c:513: (apr_err=155005)
538 # svn: Working copy not locked
539 # svn: directory '' not locked
541 svntest
.actions
.run_and_verify_svn(None, None, [], 'st', '-u', 'wc')
543 # Try again another way; the error would look like this:
545 # subversion/libsvn_repos/delta.c:207: (apr_err=160005)
546 # svn: Invalid filesystem path syntax
547 # svn: svn_repos_dir_delta: invalid editor anchoring; at least \
548 # one of the input paths is not a directory and there was \
551 # (Dang! Hope a user never has to see that :-) ).
553 svntest
.main
.safe_rmtree('wc')
554 svntest
.main
.run_svn(None,
555 'co', '-r1', A_url
+ "@1", 'wc')
556 svntest
.actions
.run_and_verify_svn(None, None, [], 'st', '-u', 'wc')
558 #----------------------------------------------------------------------
560 def get_last_changed_date(path
):
561 "get the Last Changed Date for path using svn info"
562 exit_code
, out
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
565 if re
.match("^Last Changed Date", line
):
567 print "Didn't find Last Changed Date for " + path
568 raise svntest
.Failure
570 # Helper for timestamp_behaviour test
571 def get_text_timestamp(path
):
572 "get the text-time for path using svn info"
573 exit_code
, out
, err
= svntest
.actions
.run_and_verify_svn(None, None, [],
576 if re
.match("^Text Last Updated", line
):
578 print "Didn't find text-time for " + path
579 raise svntest
.Failure
581 # Helper for timestamp_behaviour test
582 def text_time_behaviour(wc_dir
, wc_path
, status_path
, expected_status
, cmd
):
583 "text-time behaviour"
585 # Pristine text and text-time
586 fp
= open(wc_path
, 'rb')
587 pre_text
= fp
.readlines()
588 pre_text_time
= get_text_timestamp(wc_path
)
590 # Modifying the text does not affect text-time
591 svntest
.main
.file_append(wc_path
, "some mod")
592 expected_status
.tweak(status_path
, status
='M ')
593 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
594 text_time
= get_text_timestamp(wc_path
)
595 if text_time
!= pre_text_time
:
596 raise svntest
.Failure
598 # Manually reverting the text does not affect the text-time
599 fp
= open(wc_path
, 'wb')
600 fp
.writelines(pre_text
)
602 expected_status
.tweak(status_path
, status
=' ')
603 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
604 text_time
= get_text_timestamp(wc_path
)
605 if text_time
!= pre_text_time
:
606 raise svntest
.Failure
608 # revert/cleanup change the text-time even though the text doesn't change
610 svntest
.actions
.run_and_verify_svn(None, None, [], cmd
, wc_dir
)
612 svntest
.actions
.run_and_verify_svn(None, None, [], cmd
, wc_path
)
613 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
614 text_time
= get_text_timestamp(wc_path
)
615 if text_time
== pre_text_time
:
616 raise svntest
.Failure
619 # Is this really a status test? I'm not sure, but I don't know where
621 def timestamp_behaviour(sbox
):
622 "timestamp behaviour"
627 A_path
= os
.path
.join(wc_dir
, 'A')
628 iota_path
= os
.path
.join(wc_dir
, 'iota')
630 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
631 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
633 # Sleep to ensure timestamps change
636 # Check behaviour of revert on text-time
637 text_time_behaviour(wc_dir
, iota_path
, 'iota', expected_status
, 'revert')
639 # Sleep to ensure timestamps change
642 # Check behaviour of cleanup on text-time
643 text_time_behaviour(wc_dir
, iota_path
, 'iota', expected_status
, 'cleanup')
645 # Create a config to enable use-commit-times
646 config_dir
= os
.path
.join(os
.path
.abspath(svntest
.main
.temp_dir
),
648 config_contents
= '''\
650 use-commit-times = yes
652 svntest
.main
.create_config_dir(config_dir
, config_contents
)
654 other_wc
= sbox
.add_wc_path('other')
655 svntest
.actions
.run_and_verify_svn("checkout failed", None, [],
658 '--config-dir', config_dir
)
660 other_iota_path
= os
.path
.join(other_wc
, 'iota')
661 iota_text_timestamp
= get_text_timestamp(other_iota_path
)
662 iota_last_changed
= get_last_changed_date(other_iota_path
)
663 if (iota_text_timestamp
[17] != ':' or
664 iota_text_timestamp
[17:] != iota_last_changed
[17:]):
665 raise svntest
.Failure
667 ### FIXME: check the working file's timestamp as well
669 #----------------------------------------------------------------------
671 def status_on_unversioned_dotdot(sbox
):
672 "status on '..' where '..' is not versioned"
673 # See issue #1617 (and #2030).
674 sbox
.build(read_only
= True)
677 new_dir
= os
.path
.join(wc_dir
, 'new_dir')
678 new_subdir
= os
.path
.join(new_dir
, 'new_subdir')
684 exit_code
, out
, err
= svntest
.main
.run_svn(1, 'st', '..')
686 if line
.find('svn: warning: \'..\' is not a working copy') != -1:
689 raise svntest
.Failure
691 #----------------------------------------------------------------------
693 def status_on_partially_nonrecursive_wc(sbox
):
694 "status -u in partially non-recursive wc"
695 # Based on issue #2122.
697 # $ svn co -N -r 213 svn://svn.debian.org/pkg-kde .
699 # Checked out revision 213.
701 # $ svn up -r 213 scripts www
702 # [ List of scripts/* files.]
703 # Updated to revision 213.
704 # [ List of www/* files.]
705 # Updated to revision 213.
708 # * 213 www/IGNORE-ME
710 # svn: subversion/libsvn_wc/status.c:910: tweak_statushash: \
711 # Assertion `repos_text_status == svn_wc_status_added' failed. \
712 # Aborted (core dumped)
714 # You might think that the intermediate "svn up -r 213 scripts www"
715 # step is unnecessary, but when I tried eliminating it, I got
718 # subversion/libsvn_wc/lock.c:642: (apr_err=155005)
719 # svn: Working copy 'www' not locked
722 # instead of the assertion error.
727 top_url
= sbox
.repo_url
728 A_url
= top_url
+ '/A'
729 D_url
= top_url
+ '/A/D'
730 G_url
= top_url
+ '/A/D/G'
731 H_url
= top_url
+ '/A/D/H'
732 rho
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'rho')
734 # Commit a change to A/D/G/rho. This will be our equivalent of
735 # whatever change it was that happened between r213 and HEAD in the
736 # reproduction recipe. For us, it's r2.
737 svntest
.main
.file_append(rho
, 'Whan that Aprille with his shoores soote\n')
738 svntest
.main
.run_svn(None,
739 'ci', '-m', 'log msg', rho
)
741 # Make the working copy weird in the right way, then try status -u.
742 D_wc
= sbox
.add_wc_path('D')
743 svntest
.main
.run_svn(None,
744 'co', '-r1', '-N', D_url
, D_wc
)
747 svntest
.main
.run_svn(None,
749 svntest
.main
.run_svn(None,
753 def missing_dir_in_anchor(sbox
):
754 "a missing dir in the anchor"
756 sbox
.build(read_only
= True)
759 foo_path
= os
.path
.join(wc_dir
, 'foo')
760 svntest
.actions
.run_and_verify_svn(None, None, [], 'mkdir', foo_path
)
761 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
762 expected_status
.add({
763 'foo' : Item(status
='A ', wc_rev
=0),
765 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
767 # At one point this caused a "foo not locked" error
768 svntest
.main
.safe_rmtree(foo_path
)
769 expected_status
.tweak('foo', status
='! ', wc_rev
='?')
770 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
773 def status_in_xml(sbox
):
774 "status output in XML format"
776 sbox
.build(read_only
= True)
780 file_path
= os
.path
.join(wc_dir
, file_name
)
781 svntest
.main
.file_append(file_path
, "test status --xml\n")
783 # Retrieve last changed date from svn log
784 exit_code
, output
, error
= svntest
.actions
.run_and_verify_svn(
785 None, None, [], 'log', file_path
, '--xml', '-rHEAD')
789 if line
.find(info_msg
) >= 0:
790 time_str
= line
[:len(line
)]
793 raise svntest
.Failure
795 template
= ["<?xml version=\"1.0\"?>\n",
798 " path=\"%s\">\n" % (file_path
),
800 " path=\"%s\">\n" % (file_path
),
803 " item=\"modified\"\n",
804 " revision=\"1\">\n",
806 " revision=\"1\">\n",
807 "<author>%s</author>\n" % svntest
.main
.wc_author
,
813 " revision=\"1\"/>\n",
818 exit_code
, output
, error
= svntest
.actions
.run_and_verify_svn(None, None, [],
823 for i
in range(0, len(output
)):
824 if output
[i
] != template
[i
]:
825 print "ERROR: expected:", template
[i
], "actual:", output
[i
]
826 raise svntest
.Failure
828 #----------------------------------------------------------------------
830 def status_ignored_dir(sbox
):
831 "status on ignored directory"
834 new_dir
= os
.path
.join(wc_dir
, "dir.o")
835 new_dir_url
= sbox
.repo_url
+ "/dir.o"
837 svntest
.actions
.run_and_verify_svn("Create dir", "\n|Committed revision 2.", [],
838 'mkdir', new_dir_url
, '-m', 'msg')
840 # Make a dir that is ignored by the default ignore patterns.
843 # run_and_verify_status doesn't handle this weird kind of entry.
844 svntest
.actions
.run_and_verify_svn(None,
845 ['I * ' + new_dir
+ "\n",
846 ' * 1 ' + wc_dir
+ "\n",
847 'Status against revision: 2\n'], [],
848 "status", "-u", wc_dir
)
850 #----------------------------------------------------------------------
852 def status_unversioned_dir(sbox
):
853 "status on unversioned dir (issue 2030)"
854 sbox
.build(read_only
= True)
856 expected_err
= ["svn: warning: '" + dir + "' is not a working copy\n",
857 "svn: warning: '" + dir + "' is not a working copy\n"]
858 svntest
.actions
.run_and_verify_svn2(None, [], expected_err
, 0,
861 #----------------------------------------------------------------------
863 def status_missing_dir(sbox
):
864 "status with a versioned directory missing"
865 sbox
.build(read_only
= True)
867 a_d_g
= os
.path
.join(wc_dir
, "A", "D", "G")
869 # ok, blow away the A/D/G directory
870 svntest
.main
.safe_rmtree(a_d_g
)
872 expected
= svntest
.verify
.UnorderedOutput(["! " + a_d_g
+ "\n"])
873 svntest
.actions
.run_and_verify_svn(None, expected
, [], "status", wc_dir
)
875 expected
= svntest
.verify
.UnorderedOutput(
876 [" * " + os
.path
.join(a_d_g
, "pi") + "\n",
877 " * " + os
.path
.join(a_d_g
, "rho") + "\n",
878 " * " + os
.path
.join(a_d_g
, "tau") + "\n",
879 "! * ? " + a_d_g
+ "\n",
880 " * 1 " + os
.path
.join(wc_dir
, "A", "D") + "\n",
881 "Status against revision: 1\n" ])
883 # now run status -u, we should be able to do this without crashing
884 svntest
.actions
.run_and_verify_svn(None, expected
, [],
885 "status", "-u", wc_dir
)
887 def status_add_plus_conflict(sbox
):
888 "status on conflicted added file"
890 svntest
.actions
.do_sleep_for_timestamps()
894 branch_url
= sbox
.repo_url
+ '/branch'
895 trunk_url
= sbox
.repo_url
+ '/trunk'
897 svntest
.actions
.run_and_verify_svn(None, None, [],
898 'mkdir', '-m', 'rev 2',
899 branch_url
, trunk_url
)
901 svntest
.actions
.run_and_verify_svn(None, None, [],
904 branch_file
= os
.path
.join(wc_dir
, 'branch', 'file')
906 svntest
.main
.file_write(branch_file
, "line 1\nline2\nline3\n", 'wb+')
908 svntest
.actions
.run_and_verify_svn(None, None, [], 'add', branch_file
)
910 svntest
.actions
.run_and_verify_svn(None, None, [],
912 branch_file
, '-m', 'rev 3')
914 svntest
.main
.file_write(branch_file
, "line 1\nline3\n", 'wb')
916 svntest
.actions
.run_and_verify_svn(None, None, [],
918 branch_file
, '-m', 'rev 4')
920 svntest
.main
.file_write(branch_file
, "line 1\nline2\n", 'wb')
922 svntest
.actions
.run_and_verify_svn(None, None, [],
924 branch_file
, '-m', 'rev 5')
926 trunk_dir
= os
.path
.join(wc_dir
, 'trunk')
928 svntest
.actions
.run_and_verify_svn(None, None, [],
930 branch_url
, '-r', '2:3', trunk_dir
)
932 svntest
.actions
.run_and_verify_svn(None, None, [],
934 branch_url
, '-r', '4:5', trunk_dir
)
937 "? " + os
.path
.join(wc_dir
, "trunk", "file.merge-left.r4") + "\n",
938 "? " + os
.path
.join(wc_dir
, "trunk", "file.merge-right.r5") + "\n",
939 "? " + os
.path
.join(wc_dir
, "trunk", "file.working") + "\n",
940 "C + " + os
.path
.join(wc_dir
, "trunk", "file") + "\n",
942 if svntest
.main
.server_has_mergeinfo():
943 lines
.append(" M " + os
.path
.join(wc_dir
, "trunk") + "\n")
945 expected_output
= svntest
.verify
.UnorderedOutput(lines
)
947 svntest
.actions
.run_and_verify_svn(None, expected_output
, [],
950 #----------------------------------------------------------------------
952 def inconsistent_eol(sbox
):
953 "status with inconsistent eol style"
957 iota_path
= os
.path
.join(wc_dir
, "iota")
959 svntest
.main
.file_write(iota_path
, "line 1\nline 2\n", "wb")
961 svntest
.actions
.run_and_verify_svn(None,
962 "property 'svn:eol-style' set on.*iota",
964 'propset', 'svn:eol-style', 'native',
965 os
.path
.join(wc_dir
, 'iota'))
967 expected_output
= svntest
.wc
.State(wc_dir
, {
968 'iota' : Item(verb
='Sending'),
971 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
972 expected_status
.tweak('iota', wc_rev
=2)
974 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
975 expected_status
, None, wc_dir
)
977 # Make the eol style inconsistent and verify that status says nothing.
978 svntest
.main
.file_write(iota_path
, "line 1\nline 2\r\n", "wb")
979 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
981 #----------------------------------------------------------------------
982 # Test for issue #2533
983 def status_update_with_incoming_props(sbox
):
984 "run 'status -u' variations w/ incoming propchanges"
988 A_path
= os
.path
.join(wc_dir
, 'A')
990 # Add a property to the root folder and a subdir
991 svntest
.main
.run_svn(None, 'propset', 'red', 'rojo', wc_dir
)
992 svntest
.main
.run_svn(None, 'propset', 'black', 'bobo', A_path
)
994 # Create expected output tree.
995 expected_output
= svntest
.wc
.State(wc_dir
, {
996 '' : Item(verb
='Sending'),
997 'A' : Item(verb
='Sending'),
1000 # Created expected status tree.
1001 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1002 expected_status
.tweak('', wc_rev
=2, status
=' ')
1003 expected_status
.tweak('A', wc_rev
=2, status
=' ')
1005 # Commit the working copy
1006 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
1010 # Create expected trees for an update to revision 1.
1011 expected_output
= svntest
.wc
.State(wc_dir
, {
1012 '' : Item(status
=' U'),
1013 'A' : Item(status
=' U'),
1015 expected_disk
= svntest
.main
.greek_state
.copy()
1016 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1018 # Do the update and check the results in three ways... INCLUDING PROPS
1019 svntest
.actions
.run_and_verify_update(wc_dir
,
1023 None, None, None, None, None, 1,
1026 # Can't use run_and_verify_status here because the out-of-date
1027 # information in the status output isn't copied in the status tree.
1028 expected
= svntest
.verify
.UnorderedOutput(
1029 [" * 1 " + A_path
+ "\n",
1030 " * 1 " + wc_dir
+ "\n",
1031 "Status against revision: 2\n" ])
1033 svntest
.actions
.run_and_verify_svn(None,
1039 expected
= svntest
.verify
.UnorderedOutput(
1041 os
.path
.join(wc_dir
, "iota") + "\n",
1042 " * 1 1 jrandom " + A_path
+ "\n",
1043 " * 1 1 jrandom " + wc_dir
+ "\n",
1044 "Status against revision: 2\n" ])
1046 svntest
.actions
.run_and_verify_svn(None, expected
, [],
1050 # Retrieve last changed date from svn log
1051 exit_code
, output
, error
= svntest
.actions
.run_and_verify_svn(None, None, [],
1057 if line
.find(info_msg
) >= 0:
1058 time_str
= line
[:len(line
)]
1061 raise svntest
.Failure
1063 xout
= ["<?xml version=\"1.0\"?>\n",
1066 " path=\"%s\">\n" % (wc_dir
),
1068 " path=\"%s\">\n" % (A_path
),
1070 " props=\"none\"\n",
1071 " item=\"normal\"\n",
1072 " revision=\"1\">\n",
1074 " revision=\"1\">\n",
1075 "<author>%s</author>\n" % svntest
.main
.wc_author
,
1080 " props=\"modified\"\n",
1081 " item=\"none\">\n",
1082 "</repos-status>\n",
1085 " path=\"%s\">\n" % (wc_dir
),
1087 " props=\"none\"\n",
1088 " item=\"normal\"\n",
1089 " revision=\"1\">\n",
1091 " revision=\"1\">\n",
1092 "<author>%s</author>\n" % svntest
.main
.wc_author
,
1097 " props=\"modified\"\n",
1098 " item=\"none\">\n",
1099 "</repos-status>\n",
1102 " revision=\"2\"/>\n",
1106 exit_code
, output
, error
= svntest
.actions
.run_and_verify_svn(None, xout
, [],
1111 # more incoming prop updates.
1112 def status_update_verbose_with_incoming_props(sbox
):
1113 "run 'status -uv' w/ incoming propchanges"
1116 wc_dir
= sbox
.wc_dir
1117 A_path
= os
.path
.join(wc_dir
, 'A')
1118 D_path
= os
.path
.join(A_path
, 'D')
1119 B_path
= os
.path
.join(A_path
, 'B')
1120 E_path
= os
.path
.join(A_path
, 'B', 'E')
1121 G_path
= os
.path
.join(A_path
, 'D', 'G')
1122 H_path
= os
.path
.join(A_path
, 'D', 'H')
1123 # Add a property to the root folder and a subdir
1124 svntest
.main
.run_svn(None, 'propset', 'red', 'rojo', D_path
)
1125 svntest
.main
.run_svn(None, 'propset', 'black', 'bobo', E_path
)
1126 svntest
.main
.run_svn(None, 'propset', 'black', 'bobo', wc_dir
)
1128 # Create expected output tree.
1129 expected_output
= svntest
.wc
.State(wc_dir
, {
1130 'A/D' : Item(verb
='Sending'),
1131 'A/B/E' : Item(verb
='Sending'),
1132 '' : Item(verb
='Sending'),
1134 # Created expected status tree.
1135 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1136 expected_status
.tweak('A/D', wc_rev
=2, status
=' ')
1137 expected_status
.tweak('A/B/E', wc_rev
=2, status
=' ')
1138 expected_status
.tweak('', wc_rev
=2, status
=' ')
1140 # Commit the working copy
1141 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
1145 # Create expected trees for an update to revision 1.
1146 expected_output
= svntest
.wc
.State(wc_dir
, {
1147 'A/D' : Item(status
=' U'),
1148 'A/B/E' : Item(status
=' U'),
1149 '' : Item(status
=' U'),
1151 expected_disk
= svntest
.main
.greek_state
.copy()
1152 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1154 # Do the update and check the results in three ways... INCLUDING PROPS
1155 svntest
.actions
.run_and_verify_update(wc_dir
,
1159 None, None, None, None, None, 1,
1162 # Can't use run_and_verify_status here because the out-of-date
1163 # information in the status output isn't copied in the status tree.
1164 common
= " 1 1 jrandom "
1165 expected
= svntest
.verify
.UnorderedOutput(
1166 [" " + common
+ os
.path
.join(E_path
, 'alpha') + "\n",
1167 " " + common
+ os
.path
.join(E_path
, 'beta') + "\n",
1168 " *" + common
+ os
.path
.join(E_path
) + "\n",
1169 " " + common
+ os
.path
.join(B_path
, 'lambda') + "\n",
1170 " " + common
+ os
.path
.join(B_path
, 'F') + "\n",
1171 " " + common
+ B_path
+ "\n",
1172 " " + common
+ os
.path
.join(G_path
, 'pi') + "\n",
1173 " " + common
+ os
.path
.join(G_path
, 'rho') + "\n",
1174 " " + common
+ os
.path
.join(G_path
, 'tau') + "\n",
1175 " " + common
+ G_path
+ "\n",
1176 " " + common
+ os
.path
.join(H_path
, 'chi') + "\n",
1177 " " + common
+ os
.path
.join(H_path
, 'omega') + "\n",
1178 " " + common
+ os
.path
.join(H_path
, 'psi') + "\n",
1179 " " + common
+ H_path
+ "\n",
1180 " " + common
+ os
.path
.join(D_path
, 'gamma') + "\n",
1181 " *" + common
+ D_path
+ "\n",
1182 " " + common
+ os
.path
.join(A_path
, 'mu') + "\n",
1183 " " + common
+ os
.path
.join(A_path
, 'C') + "\n",
1184 " " + common
+ A_path
+ "\n",
1185 " " + common
+ os
.path
.join(wc_dir
, 'iota') + "\n",
1186 " *" + common
+ wc_dir
+ "\n",
1187 "Status against revision: 2\n" ])
1189 svntest
.actions
.run_and_verify_svn(None,
1192 "status", "-uv", wc_dir
)
1194 #----------------------------------------------------------------------
1195 # Test for issue #2468
1196 def status_nonrecursive_update(sbox
):
1197 "run 'status -uN' with incoming changes"
1200 wc_dir
= sbox
.wc_dir
1201 A_path
= os
.path
.join(wc_dir
, 'A')
1202 D_path
= os
.path
.join(A_path
, 'D')
1203 mu_path
= os
.path
.join(A_path
, 'mu')
1204 gamma_path
= os
.path
.join(D_path
, 'gamma')
1206 # Change files in A and D and commit
1207 svntest
.main
.file_append(mu_path
, "new line of text")
1208 svntest
.main
.file_append(gamma_path
, "new line of text")
1210 # Create expected trees for commit
1211 expected_output
= svntest
.wc
.State(wc_dir
, {
1212 'A/mu' : Item(verb
='Sending'),
1213 'A/D/gamma' : Item(verb
='Sending')
1215 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1216 expected_status
.tweak('A/mu', wc_rev
=2, status
=' ')
1217 expected_status
.tweak('A/D/gamma', wc_rev
=2, status
=' ')
1219 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
1223 # Create expected trees for an update to revision 1.
1224 expected_output
= svntest
.wc
.State(wc_dir
, {
1225 'A/mu' : Item(status
='U '),
1226 'A/D/gamma' : Item(status
='U '),
1228 expected_disk
= svntest
.main
.greek_state
.copy()
1229 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1231 # Do the update and check the results in three ways
1232 svntest
.actions
.run_and_verify_update(wc_dir
,
1236 None, None, None, None, None, 0,
1239 # Check the remote status of folder A (non-recursively)
1240 xout
= [" * 1 " + os
.path
.join(wc_dir
, "A", "mu") + "\n",
1241 "Status against revision: 2\n" ]
1243 svntest
.actions
.run_and_verify_svn(None,
1246 "status", "-uN", A_path
)
1248 def change_files(wc_dir
, files
):
1249 """Make a basic change to the files.
1250 files = a list of paths relative to the wc root directory
1254 filepath
= os
.path
.join(wc_dir
, file)
1255 svntest
.main
.file_append(filepath
, "new line of text")
1257 def change_files_and_commit(wc_dir
, files
, baserev
=1):
1258 """Make a basic change to the files and commit them.
1259 files = a list of paths relative to the wc root directory
1262 change_files(wc_dir
, files
)
1264 # Prepare expected trees for commit
1265 expected_output
= svntest
.wc
.State(wc_dir
, {
1266 'A/mu' : Item(verb
='Sending'),
1267 'A/D/gamma' : Item(verb
='Sending')
1269 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
1271 commitrev
= baserev
+ 1
1273 expected_output
.add({file : Item(verb
='Sending')})
1274 expected_status
.tweak(file, wc_rev
=commitrev
, status
=' ')
1276 svntest
.actions
.run_and_verify_commit(wc_dir
, expected_output
,
1280 def status_depth_local(sbox
):
1281 "run 'status --depth=X' with local changes"
1283 sbox
.build(read_only
= True)
1284 wc_dir
= sbox
.wc_dir
1285 A_path
= os
.path
.join(wc_dir
, 'A')
1286 D_path
= os
.path
.join(A_path
, 'D')
1288 mu_path
= os
.path
.join(A_path
, 'mu')
1289 gamma_path
= os
.path
.join(D_path
, 'gamma')
1291 # make some changes to the greek tree
1292 change_files(wc_dir
, ['A/mu', 'A/D/gamma'])
1293 svntest
.main
.run_svn(None, 'propset', 'svn:test', 'value', A_path
)
1294 svntest
.main
.run_svn(None, 'propset', 'svn:test', 'value', D_path
)
1296 # for all the possible types of depth, check the status
1299 expected
= svntest
.verify
.UnorderedOutput(
1300 [" M %s\n" % A_path
])
1301 svntest
.actions
.run_and_verify_svn(None,
1304 "status", "--depth=empty", A_path
)
1307 expected
= svntest
.verify
.UnorderedOutput(
1308 [" M %s\n" % A_path
,
1309 "M %s\n" % mu_path
])
1311 svntest
.actions
.run_and_verify_svn(None,
1314 "status", "--depth=files", A_path
)
1317 expected
= svntest
.verify
.UnorderedOutput(
1318 [" M %s\n" % A_path
,
1320 "M %s\n" % mu_path
])
1322 svntest
.actions
.run_and_verify_svn(None,
1325 "status", "--depth=immediates", A_path
)
1327 # depth=infinity (the default)
1328 expected
= svntest
.verify
.UnorderedOutput(
1329 [" M %s\n" % A_path
,
1332 "M %s\n" % gamma_path
])
1334 svntest
.actions
.run_and_verify_svn(None,
1337 "status", "--depth=infinity", A_path
)
1339 def status_depth_update(sbox
):
1340 "run 'status --depth=X -u' with incoming changes"
1343 wc_dir
= sbox
.wc_dir
1344 A_path
= os
.path
.join(wc_dir
, 'A')
1345 D_path
= os
.path
.join(A_path
, 'D')
1347 mu_path
= os
.path
.join(A_path
, 'mu')
1348 gamma_path
= os
.path
.join(D_path
, 'gamma')
1350 # add some files, change directory properties
1351 change_files_and_commit(wc_dir
, ['A/mu', 'A/D/gamma'])
1352 svntest
.main
.run_svn(None, 'up', wc_dir
)
1353 svntest
.main
.run_svn(None, 'propset', 'svn:test', 'value', A_path
)
1354 svntest
.main
.run_svn(None, 'propset', 'svn:test', 'value', D_path
)
1355 svntest
.main
.run_svn(None, 'ci', '-m', 'log message', wc_dir
)
1358 svntest
.main
.run_svn(None, 'up', '-r', '1', wc_dir
)
1360 # for all the possible types of depth, check the status
1363 expected
= svntest
.verify
.UnorderedOutput(
1364 [" * 1 %s\n" % A_path
,
1365 "Status against revision: 3\n"])
1367 svntest
.actions
.run_and_verify_svn(None,
1370 "status", "-u", "--depth=empty", A_path
)
1373 expected
= svntest
.verify
.UnorderedOutput(
1374 [" * 1 %s\n" % mu_path
,
1375 " * 1 %s\n" % A_path
,
1376 "Status against revision: 3\n"])
1378 svntest
.actions
.run_and_verify_svn(None,
1381 "status", "-u", "--depth=files",
1385 expected
= svntest
.verify
.UnorderedOutput(
1386 [" * 1 %s\n" % A_path
,
1387 " * 1 %s\n" % D_path
,
1388 " * 1 %s\n" % mu_path
,
1389 "Status against revision: 3\n"])
1391 svntest
.actions
.run_and_verify_svn(None,
1394 "status", "-u", "--depth=immediates",
1397 # depth=infinity (the default)
1398 expected
= svntest
.verify
.UnorderedOutput(
1399 [" * 1 %s\n" % A_path
,
1400 " * 1 %s\n" % D_path
,
1401 " * 1 %s\n" % mu_path
,
1402 " * 1 %s\n" % gamma_path
,
1403 "Status against revision: 3\n"])
1405 svntest
.actions
.run_and_verify_svn(None,
1408 "status", "-u", "--depth=infinity",
1412 #----------------------------------------------------------------------
1413 # Test for issue #2420
1414 def status_dash_u_deleted_directories(sbox
):
1415 "run 'status -u' with locally deleted directories"
1418 wc_dir
= sbox
.wc_dir
1419 A_path
= os
.path
.join(wc_dir
, 'A')
1420 B_path
= os
.path
.join(A_path
, 'B')
1422 # delete the B directory
1423 svntest
.actions
.run_and_verify_svn(None, None, [],
1426 # now run status -u on B and its children
1427 was_cwd
= os
.getcwd()
1431 # check status -u of B
1432 expected
= svntest
.verify
.UnorderedOutput(
1434 "D 1 %s\n" % os
.path
.join("B", "lambda"),
1435 "D 1 %s\n" % os
.path
.join("B", "E"),
1436 "D 1 %s\n" % os
.path
.join("B", "E", "alpha"),
1437 "D 1 %s\n" % os
.path
.join("B", "E", "beta"),
1438 "D 1 %s\n" % os
.path
.join("B", "F"),
1439 "Status against revision: 1\n" ])
1440 svntest
.actions
.run_and_verify_svn(None,
1443 "status", "-u", "B")
1445 # again, but now from inside B, should give the same output
1447 expected
= svntest
.verify
.UnorderedOutput(
1449 "D 1 %s\n" % "lambda",
1451 "D 1 %s\n" % os
.path
.join("E", "alpha"),
1452 "D 1 %s\n" % os
.path
.join("E", "beta"),
1454 "Status against revision: 1\n" ])
1455 svntest
.actions
.run_and_verify_svn(None,
1458 "status", "-u", ".")
1460 # check status -u of B/E
1461 expected
= svntest
.verify
.UnorderedOutput(
1462 ["D 1 %s\n" % os
.path
.join("B", "E"),
1463 "D 1 %s\n" % os
.path
.join("B", "E", "alpha"),
1464 "D 1 %s\n" % os
.path
.join("B", "E", "beta"),
1465 "Status against revision: 1\n" ])
1469 svntest
.actions
.run_and_verify_svn(None,
1473 os
.path
.join("B", "E"))
1475 #----------------------------------------------------------------------
1477 # Test for issue #2737: show obstructed status for versioned directories
1478 # replaced by local directories.
1479 def status_dash_u_type_change(sbox
):
1480 "status -u on versioned items whose type changed"
1482 sbox
.build(read_only
= True)
1483 wc_dir
= sbox
.wc_dir
1487 # Change the versioned file iota into an unversioned dir.
1491 xout
= ["~ 1 iota\n",
1492 "Status against revision: 1\n" ]
1494 svntest
.actions
.run_and_verify_svn(None,
1499 # Change the versioned directory A into an unversioned dir.
1500 svntest
.main
.safe_rmtree('A')
1503 expected
= svntest
.verify
.UnorderedOutput(
1506 "Status against revision: 1\n" ])
1508 svntest
.actions
.run_and_verify_svn(None,
1513 ########################################################################
1517 # list all tests here, starting with None:
1519 status_unversioned_file_in_current_dir
,
1520 status_update_with_nested_adds
,
1521 status_shows_all_in_current_dir
,
1522 status_missing_file
,
1524 SkipUnless(status_type_change_to_symlink
,
1525 svntest
.main
.is_posix_os
),
1526 status_with_new_files_pending
,
1527 status_for_unignored_file
,
1528 status_for_nonexistent_file
,
1529 status_file_needs_update
,
1530 status_uninvited_parent_directory
,
1531 status_on_forward_deletion
,
1532 timestamp_behaviour
,
1533 status_on_unversioned_dotdot
,
1534 status_on_partially_nonrecursive_wc
,
1535 missing_dir_in_anchor
,
1538 status_unversioned_dir
,
1540 status_nonrecursive_update_different_cwd
,
1541 status_add_plus_conflict
,
1543 status_update_with_incoming_props
,
1544 status_update_verbose_with_incoming_props
,
1545 status_nonrecursive_update
,
1546 status_dash_u_deleted_directories
,
1548 status_depth_update
,
1549 status_dash_u_type_change
,
1552 if __name__
== '__main__':
1553 svntest
.main
.run_tests(test_list
)