3 # actions.py: routines that actually run the svn client.
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2003 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 import os
.path
, shutil
, string
, re
, sys
21 import main
, tree
, wc
# general svntest routines in this module.
22 from svntest
import Failure
, SVNAnyOutput
24 class SVNUnexpectedOutput(Failure
):
25 """Exception raised if an invocation of svn results in unexpected
26 output of any kind."""
29 class SVNUnexpectedStdout(SVNUnexpectedOutput
):
30 """Exception raised if an invocation of svn results in unexpected
34 class SVNUnexpectedStderr(SVNUnexpectedOutput
):
35 """Exception raised if an invocation of svn results in unexpected
39 class SVNExpectedStdout(SVNUnexpectedOutput
):
40 """Exception raised if an invocation of svn results in no output on
41 STDOUT when output was expected."""
44 class SVNExpectedStderr(SVNUnexpectedOutput
):
45 """Exception raised if an invocation of svn results in no output on
46 STDERR when output was expected."""
49 class SVNIncorrectDatatype(SVNUnexpectedOutput
):
50 """Exception raised if invalid input is passed to the
51 run_and_verify_* API"""
55 ######################################################################
56 # Used by every test, so that they can run independently of
57 # one another. The first time it's run, it executes 'svnadmin' to
58 # create a repository and then 'svn imports' the greek tree.
59 # Thereafter, every time this routine is called, it recursively copies
60 # the `pristine repos' to a new location.
62 def guarantee_greek_repository(path
):
63 """Guarantee that a local svn repository exists at PATH, containing
64 nothing but the greek-tree at revision 1."""
66 if path
== main
.pristine_dir
:
67 print "ERROR: attempt to overwrite the pristine repos! Aborting."
70 # If there's no pristine repos, create one.
71 if not os
.path
.exists(main
.pristine_dir
):
72 main
.create_repos(main
.pristine_dir
)
74 # dump the greek tree to disk.
75 main
.greek_state
.write_to_disk(main
.greek_dump_dir
)
77 # build a URL for doing an import.
78 url
= main
.test_area_url
+ '/' + main
.pristine_dir
80 url
= string
.replace(url
, '\\', '/')
82 # import the greek tree, using l:foo/p:bar
83 ### todo: svn should not be prompting for auth info when using
84 ### repositories with no auth/auth requirements
85 output
, errput
= main
.run_svn(None, 'import',
86 '--username', main
.wc_author
,
87 '--password', main
.wc_passwd
,
88 '-m', 'Log message for revision 1.',
89 main
.greek_dump_dir
, url
)
91 # check for any errors from the import
93 display_lines("Errors during initial 'svn import':",
94 'STDERR', None, errput
)
97 # verify the printed output of 'svn import'.
98 lastline
= string
.strip(output
.pop())
99 cm
= re
.compile ("(Committed|Imported) revision [0-9]+.")
100 match
= cm
.search (lastline
)
102 print "ERROR: import did not succeed, while creating greek repos."
103 print "The final line from 'svn import' was:"
106 output_tree
= tree
.build_tree_from_commit(output
)
108 ### due to path normalization in the .old_tree() method, we cannot
109 ### prepend the necessary '.' directory. thus, let's construct an old
110 ### tree manually from the greek_state.
112 for greek_path
in main
.greek_state
.desc
.keys():
113 output_list
.append([ os
.path
.join(main
.greek_dump_dir
, greek_path
),
114 None, {}, {'verb' : 'Adding'}])
115 expected_output_tree
= tree
.build_generic_tree(output_list
)
118 tree
.compare_trees(output_tree
, expected_output_tree
)
119 except tree
.SVNTreeUnequal
:
120 display_trees("ERROR: output of import command is unexpected.",
121 'OUTPUT TREE', expected_output_tree
, output_tree
)
124 # Now that the pristine repos exists, copy it to PATH.
125 if os
.path
.exists(path
):
127 if main
.copy_repos(main
.pristine_dir
, path
, 1):
128 print "ERROR: copying repository failed."
131 # make the repos world-writeable, for mod_dav_svn's sake.
132 main
.chmod_tree(path
, 0666, 0666)
135 def run_and_verify_svn(message
, expected_stdout
, expected_stderr
, *varargs
):
136 """Invokes main.run_svn with *VARARGS. If EXPECTED_STDOUT or
137 EXPECTED_STDERR is not 'None', invokes compare_and_display_lines
138 with MESSAGE and the expected output. If the comparison fails,
139 compare_and_display_lines will raise."""
140 ### TODO catch and throw particular exceptions from above
142 if expected_stderr
is not None and expected_stderr
is not []:
145 out
, err
= main
.run_svn(want_err
, *varargs
)
147 if type(expected_stdout
) is type([]):
148 compare_and_display_lines(message
, 'STDOUT', expected_stdout
, out
)
149 elif expected_stdout
== SVNAnyOutput
:
151 if message
is not None: print message
152 raise SVNExpectedStdout
153 elif expected_stdout
is not None:
154 raise SVNIncorrectDatatype("Unexpected specification for stdout data")
156 if type(expected_stderr
) is type([]):
157 compare_and_display_lines(message
, 'STDERR', expected_stderr
, err
)
158 elif expected_stderr
== SVNAnyOutput
:
160 if message
is not None: print message
161 raise SVNExpectedStderr
162 elif expected_stderr
is not None:
163 raise SVNIncorrectDatatype("Unexpected specification for stderr data")
167 ######################################################################
170 # These are all routines that invoke 'svn' in particular ways, and
171 # then verify the results by comparing expected trees with actual
174 # For all the functions below, the OUTPUT_TREE and DISK_TREE args need
175 # to be created by feeding carefully constructed lists to
176 # tree.build_generic_tree(). A STATUS_TREE can be built by
177 # hand, or by editing the tree returned by get_virginal_state().
180 def run_and_verify_checkout(URL
, wc_dir_name
, output_tree
, disk_tree
,
181 singleton_handler_a
= None,
183 singleton_handler_b
= None,
185 """Checkout the URL into a new directory WC_DIR_NAME.
187 The subcommand output will be verified against OUTPUT_TREE,
188 and the working copy itself will be verified against DISK_TREE.
189 SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to
190 tree.compare_trees - see that function's doc string for more details.
191 Returns if successful and raise on failure."""
193 if isinstance(output_tree
, wc
.State
):
194 output_tree
= output_tree
.old_tree()
195 if isinstance(disk_tree
, wc
.State
):
196 disk_tree
= disk_tree
.old_tree()
198 # Remove dir if it's already there.
199 main
.safe_rmtree(wc_dir_name
)
201 # Checkout and make a tree of the output, using l:foo/p:bar
202 ### todo: svn should not be prompting for auth info when using
203 ### repositories with no auth/auth requirements
204 output
, errput
= main
.run_svn (None, 'co',
205 '--username', main
.wc_author
,
206 '--password', main
.wc_passwd
,
208 mytree
= tree
.build_tree_from_checkout (output
)
210 # Verify actual output against expected output.
211 tree
.compare_trees (mytree
, output_tree
)
213 # Create a tree by scanning the working copy
214 mytree
= tree
.build_tree_from_wc (wc_dir_name
)
216 # Verify expected disk against actual disk.
217 tree
.compare_trees (mytree
, disk_tree
,
218 singleton_handler_a
, a_baton
,
219 singleton_handler_b
, b_baton
)
222 def run_and_verify_export(URL
, export_dir_name
, output_tree
, disk_tree
,
223 singleton_handler_a
= None,
225 singleton_handler_b
= None,
227 """Export the URL into a new directory WC_DIR_NAME.
229 The subcommand output will be verified against OUTPUT_TREE,
230 and the exported copy itself will be verified against DISK_TREE.
231 SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to
232 tree.compare_trees - see that function's doc string for more details.
233 Returns if successful and raise on failure."""
235 if isinstance(output_tree
, wc
.State
):
236 output_tree
= output_tree
.old_tree()
237 if isinstance(disk_tree
, wc
.State
):
238 disk_tree
= disk_tree
.old_tree()
240 # Export and make a tree of the output, using l:foo/p:bar
241 ### todo: svn should not be prompting for auth info when using
242 ### repositories with no auth/auth requirements
243 output
, errput
= main
.run_svn (None, 'export',
244 '--username', main
.wc_author
,
245 '--password', main
.wc_passwd
,
246 URL
, export_dir_name
)
247 mytree
= tree
.build_tree_from_checkout (output
)
249 # Verify actual output against expected output.
250 tree
.compare_trees (mytree
, output_tree
)
252 # Create a tree by scanning the working copy. Don't ignore
253 # the .svn directories so that we generate an error if they
255 mytree
= tree
.build_tree_from_wc (export_dir_name
, ignore_svn
=0)
257 # Verify expected disk against actual disk.
258 tree
.compare_trees (mytree
, disk_tree
,
259 singleton_handler_a
, a_baton
,
260 singleton_handler_b
, b_baton
)
263 def verify_update(actual_output
, wc_dir_name
,
264 output_tree
, disk_tree
, status_tree
,
265 singleton_handler_a
, a_baton
,
266 singleton_handler_b
, b_baton
,
268 """Verify update of WC_DIR_NAME.
270 The subcommand output (found in ACTUAL_OUTPUT) will be verified
271 against OUTPUT_TREE, and the working copy itself will be verified
272 against DISK_TREE. If optional STATUS_OUTPUT_TREE is given, then
273 'svn status' output will be compared. (This is a good way to check
274 that revision numbers were bumped.) SINGLETON_HANDLER_A and
275 SINGLETON_HANDLER_B will be passed to tree.compare_trees - see that
276 function's doc string for more details. If CHECK_PROPS is set, then
277 disk comparison will examine props. Returns if successful, raises
280 # Verify actual output against expected output.
281 tree
.compare_trees (actual_output
, output_tree
)
283 # Create a tree by scanning the working copy
284 mytree
= tree
.build_tree_from_wc (wc_dir_name
, check_props
)
286 # Verify expected disk against actual disk.
287 tree
.compare_trees (mytree
, disk_tree
,
288 singleton_handler_a
, a_baton
,
289 singleton_handler_b
, b_baton
)
291 # Verify via 'status' command too, if possible.
293 run_and_verify_status(wc_dir_name
, status_tree
)
296 def run_and_verify_update(wc_dir_name
,
297 output_tree
, disk_tree
, status_tree
,
298 error_re_string
= None,
299 singleton_handler_a
= None,
301 singleton_handler_b
= None,
306 """Update WC_DIR_NAME. *ARGS are any extra optional args to the
307 update subcommand. NOTE: If *ARGS is specified at all, explicit
308 target paths must be passed in *ARGS as well (or a default `.' will
309 be chosen by the 'svn' binary). This allows the caller to update
310 many items in a single working copy dir, but still verify the entire
313 If ERROR_RE_STRING, the update must exit with error, and the error
314 message must match regular expression ERROR_RE_STRING.
316 Else if ERROR_RE_STRING is None, then:
318 The subcommand output will be verified against OUTPUT_TREE, and the
319 working copy itself will be verified against DISK_TREE. If optional
320 STATUS_TREE is given, then 'svn status' output will be compared.
321 (This is a good way to check that revision numbers were bumped.)
322 SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to
323 tree.compare_trees - see that function's doc string for more
326 If CHECK_PROPS is set, then disk comparison will examine props.
327 Returns if successful, raises on failure."""
329 if isinstance(output_tree
, wc
.State
):
330 output_tree
= output_tree
.old_tree()
331 if isinstance(disk_tree
, wc
.State
):
332 disk_tree
= disk_tree
.old_tree()
333 if isinstance(status_tree
, wc
.State
):
334 status_tree
= status_tree
.old_tree()
336 # Update and make a tree of the output.
338 output
, errput
= main
.run_svn (error_re_string
, 'up', *args
)
340 output
, errput
= main
.run_svn (error_re_string
, 'up', wc_dir_name
, *args
)
342 if (error_re_string
):
343 rm
= re
.compile(error_re_string
)
345 match
= rm
.search(line
)
348 raise main
.SVNUnmatchedError
350 mytree
= tree
.build_tree_from_checkout (output
)
351 verify_update (mytree
, wc_dir_name
,
352 output_tree
, disk_tree
, status_tree
,
353 singleton_handler_a
, a_baton
,
354 singleton_handler_b
, b_baton
,
358 def run_and_verify_merge(dir, rev1
, rev2
, url
,
359 output_tree
, disk_tree
, status_tree
, skip_tree
,
360 error_re_string
= None,
361 singleton_handler_a
= None,
363 singleton_handler_b
= None,
369 """Run 'svn merge -rREV1:REV2 URL DIR'
371 If ERROR_RE_STRING, the merge must exit with error, and the error
372 message must match regular expression ERROR_RE_STRING.
374 Else if ERROR_RE_STRING is None, then:
376 The subcommand output will be verified against OUTPUT_TREE, and the
377 working copy itself will be verified against DISK_TREE. If optional
378 STATUS_TREE is given, then 'svn status' output will be compared.
379 The 'skipped' merge output will be compared to SKIP_TREE.
380 SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to
381 tree.compare_trees - see that function's doc string for more
384 If CHECK_PROPS is set, then disk comparison will examine props.
386 If DRY_RUN is set then a --dry-run merge will be carried out first and
387 the output compared with that of the full merge.
389 Returns if successful, raises on failure."""
391 if isinstance(output_tree
, wc
.State
):
392 output_tree
= output_tree
.old_tree()
393 if isinstance(disk_tree
, wc
.State
):
394 disk_tree
= disk_tree
.old_tree()
395 if isinstance(status_tree
, wc
.State
):
396 status_tree
= status_tree
.old_tree()
397 if isinstance(skip_tree
, wc
.State
):
398 skip_tree
= skip_tree
.old_tree()
400 merge_command
= ('merge', '-r', rev1
+ ':' + rev2
, url
, dir)
403 pre_disk
= tree
.build_tree_from_wc(dir)
404 dry_run_command
= merge_command
+ ('--dry-run',)
405 dry_run_command
= dry_run_command
+ args
406 out_dry
, err_dry
= main
.run_svn(error_re_string
, *dry_run_command
)
407 post_disk
= tree
.build_tree_from_wc(dir)
409 tree
.compare_trees(post_disk
, pre_disk
)
410 except tree
.SVNTreeError
:
411 print "============================================================="
412 print "Dry-run merge altered working copy"
413 print "============================================================="
417 # Update and make a tree of the output.
418 merge_command
= merge_command
+ args
419 out
, err
= main
.run_svn (error_re_string
, *merge_command
)
421 if (error_re_string
):
422 rm
= re
.compile(error_re_string
)
424 match
= rm
.search(line
)
427 raise main
.SVNUnmatchedError
429 ### we should raise a less generic error here. which?
432 if dry_run
and out
!= out_dry
:
433 print "============================================================="
434 print "Merge outputs differ"
435 print "The dry-run merge output:"
436 map(sys
.stdout
.write
, out_dry
)
437 print "The full merge output:"
438 map(sys
.stdout
.write
, out
)
439 print "============================================================="
440 raise main
.SVNUnmatchedError
442 def missing_skip(a
, b
):
443 print "============================================================="
444 print "Merge failed to skip: " + a
.path
445 print "============================================================="
447 def extra_skip(a
, b
):
448 print "============================================================="
449 print "Merge unexpectedly skipped: " + a
.path
450 print "============================================================="
453 myskiptree
= tree
.build_tree_from_skipped(out
)
454 tree
.compare_trees(myskiptree
, skip_tree
,
455 extra_skip
, None, missing_skip
, None)
457 mytree
= tree
.build_tree_from_checkout(out
)
458 verify_update (mytree
, dir,
459 output_tree
, disk_tree
, status_tree
,
460 singleton_handler_a
, a_baton
,
461 singleton_handler_b
, b_baton
,
465 def run_and_verify_switch(wc_dir_name
,
468 output_tree
, disk_tree
, status_tree
,
469 singleton_handler_a
= None,
471 singleton_handler_b
= None,
475 """Switch WC_TARGET (in working copy dir WC_DIR_NAME) to SWITCH_URL.
477 The subcommand output will be verified against OUTPUT_TREE, and the
478 working copy itself will be verified against DISK_TREE. If optional
479 STATUS_OUTPUT_TREE is given, then 'svn status' output will be
480 compared. (This is a good way to check that revision numbers were
481 bumped.) SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to
482 tree.compare_trees - see that function's doc string for more details.
483 If CHECK_PROPS is set, then disk comparison will examine props.
484 Returns if successful, raises on failure."""
486 if isinstance(output_tree
, wc
.State
):
487 output_tree
= output_tree
.old_tree()
488 if isinstance(disk_tree
, wc
.State
):
489 disk_tree
= disk_tree
.old_tree()
490 if isinstance(status_tree
, wc
.State
):
491 status_tree
= status_tree
.old_tree()
493 # Update and make a tree of the output.
494 output
, errput
= main
.run_svn (None, 'switch',
495 '--username', main
.wc_author
,
496 '--password', main
.wc_passwd
,
497 switch_url
, wc_target
)
498 mytree
= tree
.build_tree_from_checkout (output
)
500 verify_update (mytree
, wc_dir_name
,
501 output_tree
, disk_tree
, status_tree
,
502 singleton_handler_a
, a_baton
,
503 singleton_handler_b
, b_baton
,
507 def run_and_verify_commit(wc_dir_name
, output_tree
, status_output_tree
,
508 error_re_string
= None,
509 singleton_handler_a
= None,
511 singleton_handler_b
= None,
514 """Commit and verify results within working copy WC_DIR_NAME,
515 sending ARGS to the commit subcommand.
517 The subcommand output will be verified against OUTPUT_TREE. If
518 optional STATUS_OUTPUT_TREE is given, then 'svn status' output will
519 be compared. (This is a good way to check that revision numbers
522 If ERROR_RE_STRING is None, the commit must not exit with error. If
523 ERROR_RE_STRING is a string, the commit must exit with error, and
524 the error message must match regular expression ERROR_RE_STRING.
526 SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to
527 tree.compare_trees - see that function's doc string for more
528 details. Returns if successful, raises on failure."""
530 if isinstance(output_tree
, wc
.State
):
531 output_tree
= output_tree
.old_tree()
532 if isinstance(status_output_tree
, wc
.State
):
533 status_output_tree
= status_output_tree
.old_tree()
536 output
, errput
= main
.run_svn(error_re_string
, 'ci', '-m', 'log msg', *args
)
538 if (error_re_string
):
539 rm
= re
.compile(error_re_string
)
541 match
= rm
.search(line
)
544 raise main
.SVNUnmatchedError
546 # Else not expecting error:
548 # Remove the final output line, and verify that the commit succeeded.
551 lastline
= string
.strip(output
.pop())
553 cm
= re
.compile("(Committed|Imported) revision [0-9]+.")
554 match
= cm
.search(lastline
)
556 print "ERROR: commit did not succeed."
557 print "The final line from 'svn ci' was:"
559 raise main
.SVNCommitFailure
561 # The new 'final' line in the output is either a regular line that
562 # mentions {Adding, Deleting, Sending, ...}, or it could be a line
563 # that says "Transmitting file data ...". If the latter case, we
564 # want to remove the line from the output; it should be ignored when
567 lastline
= output
.pop()
569 tm
= re
.compile("Transmitting file data.+")
570 match
= tm
.search(lastline
)
572 # whoops, it was important output, put it back.
573 output
.append(lastline
)
575 # Convert the output into a tree.
576 mytree
= tree
.build_tree_from_commit (output
)
578 # Verify actual output against expected output.
580 tree
.compare_trees (mytree
, output_tree
)
581 except tree
.SVNTreeError
:
582 display_trees("Output of commit is unexpected.",
583 "OUTPUT TREE", output_tree
, mytree
)
586 # Verify via 'status' command too, if possible.
587 if status_output_tree
:
588 run_and_verify_status(wc_dir_name
, status_output_tree
)
591 # This function always passes '-q' to the status command, which
592 # suppresses the printing of any unversioned or nonexistent items.
593 def run_and_verify_status(wc_dir_name
, output_tree
,
594 singleton_handler_a
= None,
596 singleton_handler_b
= None,
598 """Run 'status' on WC_DIR_NAME and compare it with the
599 expected OUTPUT_TREE. SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will
600 be passed to tree.compare_trees - see that function's doc string for
602 Returns on success, raises on failure."""
604 if isinstance(output_tree
, wc
.State
):
605 output_tree
= output_tree
.old_tree()
607 output
, errput
= main
.run_svn (None, 'status', '-v', '-u', '-q', wc_dir_name
)
609 mytree
= tree
.build_tree_from_status (output
)
611 # Verify actual output against expected output.
612 if (singleton_handler_a
or singleton_handler_b
):
614 tree
.compare_trees (mytree
, output_tree
,
615 singleton_handler_a
, a_baton
,
616 singleton_handler_b
, b_baton
)
617 except tree
.SVNTreeError
:
618 display_trees(None, 'OUTPUT TREE', output_tree
, mytree
)
622 tree
.compare_trees (mytree
, output_tree
)
623 except tree
.SVNTreeError
:
624 display_trees(None, 'OUTPUT TREE', output_tree
, mytree
)
628 # A variant of previous func, but doesn't pass '-q'. This allows us
629 # to verify unversioned or nonexistent items in the list.
630 def run_and_verify_unquiet_status(wc_dir_name
, output_tree
,
631 singleton_handler_a
= None,
633 singleton_handler_b
= None,
635 """Run 'status' on WC_DIR_NAME and compare it with the
636 expected OUTPUT_TREE. SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will
637 be passed to tree.compare_trees - see that function's doc string for
639 Returns on success, raises on failure."""
641 if isinstance(output_tree
, wc
.State
):
642 output_tree
= output_tree
.old_tree()
644 output
, errput
= main
.run_svn (None, 'status', '-v', '-u', wc_dir_name
)
646 mytree
= tree
.build_tree_from_status (output
)
648 # Verify actual output against expected output.
649 if (singleton_handler_a
or singleton_handler_b
):
650 tree
.compare_trees (mytree
, output_tree
,
651 singleton_handler_a
, a_baton
,
652 singleton_handler_b
, b_baton
)
654 tree
.compare_trees (mytree
, output_tree
)
657 ######################################################################
658 # Displaying expected and actual output
660 def display_trees(message
, label
, expected
, actual
):
661 'Print two trees, expected and actual.'
662 if message
is not None:
664 if expected
is not None:
665 print 'EXPECTED', label
+ ':'
666 tree
.dump_tree(expected
)
667 if actual
is not None:
668 print 'ACTUAL', label
+ ':'
669 tree
.dump_tree(actual
)
672 def display_lines(message
, label
, expected
, actual
):
673 'Print two sets of output lines, expected and actual.'
674 if message
is not None:
676 if expected
is not None:
677 print 'EXPECTED', label
+ ':'
678 map(sys
.stdout
.write
, expected
)
679 if actual
is not None:
680 print 'ACTUAL', label
+ ':'
681 map(sys
.stdout
.write
, actual
)
683 def compare_and_display_lines(message
, label
, expected
, actual
):
684 'Compare two sets of output lines, and print them if they differ.'
685 # This catches the None vs. [] cases
686 if expected
is None: exp
= []
688 if actual
is None: act
= []
692 display_lines(message
, label
, expected
, actual
)
693 raise main
.SVNLineUnequal
696 ######################################################################
697 # Other general utilities
700 # This allows a test to *quickly* bootstrap itself.
701 def make_repo_and_wc(sbox
):
702 """Create a fresh repository and checkout a wc from it.
704 The repo and wc directories will both be named TEST_NAME, and
705 repsectively live within the global dirs 'general_repo_dir' and
706 'general_wc_dir' (variables defined at the top of this test
707 suite.) Returns on success, raises on failure."""
709 # Store the path of the current repository.
710 main
.set_repos_paths(sbox
.repo_dir
)
712 # Create (or copy afresh) a new repos with a greek tree in it.
713 guarantee_greek_repository(sbox
.repo_dir
)
715 # Generate the expected output tree.
716 expected_output
= main
.greek_state
.copy()
717 expected_output
.wc_dir
= sbox
.wc_dir
718 expected_output
.tweak(status
='A ', contents
=None)
720 # Generate an expected wc tree.
721 expected_wc
= main
.greek_state
723 # Do a checkout, and verify the resulting output and disk contents.
724 run_and_verify_checkout(main
.current_repo_url
,
730 # Duplicate a working copy or other dir.
731 def duplicate_dir(wc_name
, wc_copy_name
):
732 """Copy the working copy WC_NAME to WC_COPY_NAME. Overwrite any
733 existing tree at that location."""
735 if os
.path
.exists(wc_copy_name
):
736 main
.safe_rmtree(wc_copy_name
)
737 shutil
.copytree(wc_name
, wc_copy_name
)
741 def get_virginal_state(wc_dir
, rev
):
742 "Return a virginal greek tree state for a WC and repos at revision REV."
744 rev
= str(rev
) ### maybe switch rev to an integer?
746 # copy the greek tree, shift it to the new wc_dir, insert a root elem,
747 # then tweak all values
748 state
= main
.greek_state
.copy()
749 state
.wc_dir
= wc_dir
750 state
.desc
[''] = wc
.StateItem()
751 state
.tweak(contents
=None, status
=' ', wc_rev
=rev
, repos_rev
=rev
)
756 # Cheap administrative directory locking
757 def lock_admin_dir(wc_dir
):
758 "Lock a SVN administrative directory"
760 path
= os
.path
.join(wc_dir
, main
.get_admin_name(), 'lock')
761 main
.file_append(path
, "stop looking!")