2 # main.py: a shared, automated test suite for Subversion
4 # Subversion is a tool for revision control.
5 # See http://subversion.tigris.org for more information.
7 # ====================================================================
8 # Licensed to the Apache Software Foundation (ASF) under one
9 # or more contributor license agreements. See the NOTICE file
10 # distributed with this work for additional information
11 # regarding copyright ownership. The ASF licenses this file
12 # to you under the Apache License, Version 2.0 (the
13 # "License"); you may not use this file except in compliance
14 # with the License. You may obtain a copy of the License at
16 # http://www.apache.org/licenses/LICENSE-2.0
18 # Unless required by applicable law or agreed to in writing,
19 # software distributed under the License is distributed on an
20 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 # KIND, either express or implied. See the License for the
22 # specific language governing permissions and limitations
24 ######################################################################
26 import sys
# for argv[]
28 import shutil
# for rmtree()
30 import stat
# for ST_MODE
32 import copy
# for deepcopy()
33 import time
# for time()
34 import traceback
# for print_exc()
36 import optparse
# for argument parsing
41 from urllib
.parse
import quote
as urllib_parse_quote
42 from urllib
.parse
import unquote
as urllib_parse_unquote
46 from urllib
import quote
as urllib_parse_quote
47 from urllib
import unquote
as urllib_parse_unquote
50 from svntest
import Failure
51 from svntest
import Skip
54 ######################################################################
56 # HOW TO USE THIS MODULE:
58 # Write a new python script that
60 # 1) imports this 'svntest' package
62 # 2) contains a number of related 'test' routines. (Each test
63 # routine should take no arguments, and return None on success
64 # or throw a Failure exception on failure. Each test should
65 # also contain a short docstring.)
67 # 3) places all the tests into a list that begins with None.
69 # 4) calls svntest.main.client_test() on the list.
71 # Also, your tests will probably want to use some of the common
72 # routines in the 'Utilities' section below.
74 #####################################################################
77 class SVNProcessTerminatedBySignal(Failure
):
78 "Exception raised if a spawned process segfaulted, aborted, etc."
81 class SVNLineUnequal(Failure
):
82 "Exception raised if two lines are unequal"
85 class SVNUnmatchedError(Failure
):
86 "Exception raised if an expected error is not found"
89 class SVNCommitFailure(Failure
):
90 "Exception raised if a commit failed"
93 class SVNRepositoryCopyFailure(Failure
):
94 "Exception raised if unable to copy a repository"
97 class SVNRepositoryCreateFailure(Failure
):
98 "Exception raised if unable to create a repository"
102 if sys
.platform
== 'win32':
104 file_scheme_prefix
= 'file:///'
107 os
.environ
['SVN_DBG_STACKTRACES_TO_STDERR'] = 'y'
110 file_scheme_prefix
= 'file://'
114 # The location of our mock svneditor script.
116 svneditor_script
= os
.path
.join(sys
.path
[0], 'svneditor.bat')
118 svneditor_script
= os
.path
.join(sys
.path
[0], 'svneditor.py')
120 # Username and password used by the working copies
121 wc_author
= 'jrandom'
122 wc_passwd
= 'rayjandom'
124 # Username and password used by the working copies for "second user"
126 wc_author2
= 'jconstant' # use the same password as wc_author
128 # Set C locale for command line programs
129 os
.environ
['LC_ALL'] = 'C'
131 # This function mimics the Python 2.3 urllib function of the same name.
132 def pathname2url(path
):
133 """Convert the pathname PATH from the local syntax for a path to the form
134 used in the path component of a URL. This does not produce a complete URL.
135 The return value will already be quoted using the quote() function."""
137 # Don't leave ':' in file://C%3A/ escaped as our canonicalization
138 # rules will replace this with a ':' on input.
139 return urllib_parse_quote(path
.replace('\\', '/')).replace('%3A', ':')
141 # This function mimics the Python 2.3 urllib function of the same name.
142 def url2pathname(path
):
143 """Convert the path component PATH from an encoded URL to the local syntax
144 for a path. This does not accept a complete URL. This function uses
145 unquote() to decode PATH."""
146 return os
.path
.normpath(urllib_parse_unquote(path
))
148 ######################################################################
149 # The locations of the svn, svnadmin and svnlook binaries, relative to
150 # the only scripts that import this file right now (they live in ../).
151 # Use --bin to override these defaults.
152 svn_binary
= os
.path
.abspath('../../svn/svn' + _exe
)
153 svnadmin_binary
= os
.path
.abspath('../../svnadmin/svnadmin' + _exe
)
154 svnlook_binary
= os
.path
.abspath('../../svnlook/svnlook' + _exe
)
155 svnrdump_binary
= os
.path
.abspath('../../svnrdump/svnrdump' + _exe
)
156 svnsync_binary
= os
.path
.abspath('../../svnsync/svnsync' + _exe
)
157 svnversion_binary
= os
.path
.abspath('../../svnversion/svnversion' + _exe
)
158 svndumpfilter_binary
= os
.path
.abspath('../../svndumpfilter/svndumpfilter' + \
160 entriesdump_binary
= os
.path
.abspath('entries-dump' + _exe
)
161 atomic_ra_revprop_change_binary
= os
.path
.abspath('atomic-ra-revprop-change' + \
164 # Location to the pristine repository, will be calculated from test_area_url
165 # when we know what the user specified for --url.
166 pristine_greek_repos_url
= None
168 # Global variable to track all of our options
171 # End of command-line-set global variables.
172 ######################################################################
174 # All temporary repositories and working copies are created underneath
175 # this dir, so there's one point at which to mount, e.g., a ramdisk.
176 work_dir
= "svn-test-work"
178 # Constant for the merge info property.
179 SVN_PROP_MERGEINFO
= "svn:mergeinfo"
181 # Where we want all the repositories and working copies to live.
182 # Each test will have its own!
183 general_repo_dir
= os
.path
.join(work_dir
, "repositories")
184 general_wc_dir
= os
.path
.join(work_dir
, "working_copies")
186 # temp directory in which we will create our 'pristine' local
187 # repository and other scratch data. This should be removed when we
188 # quit and when we startup.
189 temp_dir
= os
.path
.join(work_dir
, 'local_tmp')
191 # (derivatives of the tmp dir.)
192 pristine_greek_repos_dir
= os
.path
.join(temp_dir
, "repos")
193 greek_dump_dir
= os
.path
.join(temp_dir
, "greekfiles")
194 default_config_dir
= os
.path
.abspath(os
.path
.join(temp_dir
, "config"))
197 # Our pristine greek-tree state.
199 # If a test wishes to create an "expected" working-copy tree, it should
200 # call main.greek_state.copy(). That method will return a copy of this
201 # State object which can then be edited.
203 _item
= svntest
.wc
.StateItem
204 greek_state
= svntest
.wc
.State('', {
205 'iota' : _item("This is the file 'iota'.\n"),
207 'A/mu' : _item("This is the file 'mu'.\n"),
209 'A/B/lambda' : _item("This is the file 'lambda'.\n"),
211 'A/B/E/alpha' : _item("This is the file 'alpha'.\n"),
212 'A/B/E/beta' : _item("This is the file 'beta'.\n"),
216 'A/D/gamma' : _item("This is the file 'gamma'.\n"),
218 'A/D/G/pi' : _item("This is the file 'pi'.\n"),
219 'A/D/G/rho' : _item("This is the file 'rho'.\n"),
220 'A/D/G/tau' : _item("This is the file 'tau'.\n"),
222 'A/D/H/chi' : _item("This is the file 'chi'.\n"),
223 'A/D/H/psi' : _item("This is the file 'psi'.\n"),
224 'A/D/H/omega' : _item("This is the file 'omega'.\n"),
228 ######################################################################
229 # Utilities shared by the tests
231 "Wrap a function, catch, print and ignore exceptions"
232 def w(*args
, **kwds
):
234 return func(*args
, **kwds
)
236 if ex
.__class
__ != Failure
or ex
.args
:
239 print('EXCEPTION: %s: %s' % (ex
.__class
__.__name
__, ex_args
))
241 print('EXCEPTION: %s' % ex
.__class
__.__name
__)
244 def setup_development_mode():
245 "Wraps functions in module actions"
246 l
= [ 'run_and_verify_svn',
247 'run_and_verify_svnversion',
248 'run_and_verify_load',
249 'run_and_verify_dump',
250 'run_and_verify_checkout',
251 'run_and_verify_export',
252 'run_and_verify_update',
253 'run_and_verify_merge',
254 'run_and_verify_switch',
255 'run_and_verify_commit',
256 'run_and_verify_unquiet_status',
257 'run_and_verify_status',
258 'run_and_verify_diff_summarize',
259 'run_and_verify_diff_summarize_xml',
260 'run_and_validate_lock']
263 setattr(svntest
.actions
, func
, wrap_ex(getattr(svntest
.actions
, func
)))
265 def get_admin_name():
266 "Return name of SVN administrative subdirectory."
268 if (windows
or sys
.platform
== 'cygwin') \
269 and 'SVN_ASP_DOT_NET_HACK' in os
.environ
:
274 def wc_is_singledb(wcpath
):
275 """Temporary function that checks whether a working copy directory looks
276 like it is part of a single-db working copy."""
278 pristine
= os
.path
.join(wcpath
, get_admin_name(), 'pristine')
279 if not os
.path
.exists(pristine
):
282 # Now we must be looking at a multi-db WC dir or the root dir of a
283 # single-DB WC. Sharded 'pristine' dir => single-db, else => multi-db.
284 for name
in os
.listdir(pristine
):
287 elif len(name
) == 40:
292 def get_start_commit_hook_path(repo_dir
):
293 "Return the path of the start-commit-hook conf file in REPO_DIR."
295 return os
.path
.join(repo_dir
, "hooks", "start-commit")
298 def get_pre_commit_hook_path(repo_dir
):
299 "Return the path of the pre-commit-hook conf file in REPO_DIR."
301 return os
.path
.join(repo_dir
, "hooks", "pre-commit")
304 def get_post_commit_hook_path(repo_dir
):
305 "Return the path of the post-commit-hook conf file in REPO_DIR."
307 return os
.path
.join(repo_dir
, "hooks", "post-commit")
309 def get_pre_revprop_change_hook_path(repo_dir
):
310 "Return the path of the pre-revprop-change hook script in REPO_DIR."
312 return os
.path
.join(repo_dir
, "hooks", "pre-revprop-change")
314 def get_svnserve_conf_file_path(repo_dir
):
315 "Return the path of the svnserve.conf file in REPO_DIR."
317 return os
.path
.join(repo_dir
, "conf", "svnserve.conf")
319 def get_fsfs_conf_file_path(repo_dir
):
320 "Return the path of the fsfs.conf file in REPO_DIR."
322 return os
.path
.join(repo_dir
, "db", "fsfs.conf")
324 def get_fsfs_format_file_path(repo_dir
):
325 "Return the path of the format file in REPO_DIR."
327 return os
.path
.join(repo_dir
, "db", "format")
329 # Run any binary, logging the command line and return code
330 def run_command(command
, error_expected
, binary_mode
=0, *varargs
):
331 """Run COMMAND with VARARGS. Return exit code as int; stdout, stderr
332 as lists of lines (including line terminators). See run_command_stdin()
333 for details. If ERROR_EXPECTED is None, any stderr also will be printed."""
335 return run_command_stdin(command
, error_expected
, 0, binary_mode
,
338 # A regular expression that matches arguments that are trivially safe
339 # to pass on a command line without quoting on any supported operating
341 _safe_arg_re
= re
.compile(r
'^[A-Za-z\d\.\_\/\-\:\@]+$')
344 """Quote ARG for a command line.
346 Simply surround every argument in double-quotes unless it contains
347 only universally harmless characters.
349 WARNING: This function cannot handle arbitrary command-line
350 arguments. It can easily be confused by shell metacharacters. A
351 perfect job would be difficult and OS-dependent (see, for example,
352 http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp).
353 In other words, this function is just good enough for what we need
357 if _safe_arg_re
.match(arg
):
361 arg
= arg
.replace('$', '\$')
362 return '"%s"' % (arg
,)
364 def open_pipe(command
, bufsize
=0, stdin
=None, stdout
=None, stderr
=None):
365 """Opens a subprocess.Popen pipe to COMMAND using STDIN,
366 STDOUT, and STDERR. BUFSIZE is passed to subprocess.Popen's
367 argument of the same name.
369 Returns (infile, outfile, errfile, waiter); waiter
370 should be passed to wait_on_pipe."""
371 command
= [str(x
) for x
in command
]
373 # On Windows subprocess.Popen() won't accept a Python script as
374 # a valid program to execute, rather it wants the Python executable.
375 if (sys
.platform
== 'win32') and (command
[0].endswith('.py')):
376 command
.insert(0, sys
.executable
)
378 # Quote only the arguments on Windows. Later versions of subprocess,
379 # 2.5.2+ confirmed, don't require this quoting, but versions < 2.4.3 do.
380 if sys
.platform
== 'win32':
382 args
= ' '.join([_quote_arg(x
) for x
in args
])
383 command
= command
[0] + ' ' + args
384 command_string
= command
386 command_string
= ' '.join(command
)
389 stdin
= subprocess
.PIPE
391 stdout
= subprocess
.PIPE
393 stderr
= subprocess
.PIPE
395 p
= subprocess
.Popen(command
,
400 close_fds
=not windows
)
401 return p
.stdin
, p
.stdout
, p
.stderr
, (p
, command_string
)
403 def wait_on_pipe(waiter
, binary_mode
, stdin
=None):
404 """WAITER is (KID, COMMAND_STRING). Wait for KID (opened with open_pipe)
405 to finish, dying if it does. If KID fails, create an error message
406 containing any stdout and stderr from the kid. Show COMMAND_STRING in
407 diagnostic messages. Normalize Windows line endings of stdout and stderr
408 if not BINARY_MODE. Return KID's exit code as int; stdout, stderr as
409 lists of lines (including line terminators)."""
413 kid
, command_string
= waiter
414 stdout
, stderr
= kid
.communicate(stdin
)
415 exit_code
= kid
.returncode
417 # Normalize Windows line endings if in text mode.
418 if windows
and not binary_mode
:
419 stdout
= stdout
.replace('\r\n', '\n')
420 stderr
= stderr
.replace('\r\n', '\n')
422 # Convert output strings to lists.
423 stdout_lines
= stdout
.splitlines(True)
424 stderr_lines
= stderr
.splitlines(True)
428 exit_signal
= os
.WTERMSIG(-exit_code
)
430 exit_signal
= exit_code
432 if stdout_lines
is not None:
433 sys
.stdout
.write("".join(stdout_lines
))
435 if stderr_lines
is not None:
436 sys
.stderr
.write("".join(stderr_lines
))
439 # show the whole path to make it easier to start a debugger
440 sys
.stderr
.write("CMD: %s terminated by signal %d\n"
441 % (command_string
, exit_signal
))
443 raise SVNProcessTerminatedBySignal
445 if exit_code
and options
.verbose
:
446 sys
.stderr
.write("CMD: %s exited with %d\n"
447 % (command_string
, exit_code
))
448 return stdout_lines
, stderr_lines
, exit_code
450 def spawn_process(command
, bufsize
=0, binary_mode
=0, stdin_lines
=None,
452 """Run any binary, supplying input text, logging the command line.
453 BUFSIZE dictates the pipe buffer size used in communication with the
454 subprocess: 0 means unbuffered, 1 means line buffered, any other
455 positive value means use a buffer of (approximately) that size.
456 A negative bufsize means to use the system default, which usually
457 means fully buffered. The default value for bufsize is 0 (unbuffered).
458 Normalize Windows line endings of stdout and stderr if not BINARY_MODE.
459 Return exit code as int; stdout, stderr as lists of lines (including
460 line terminators)."""
461 if stdin_lines
and not isinstance(stdin_lines
, list):
462 raise TypeError("stdin_lines should have list type")
464 # Log the command line
465 if options
.verbose
and not command
.endswith('.py'):
466 sys
.stdout
.write('CMD: %s %s\n' % (os
.path
.basename(command
),
467 ' '.join([_quote_arg(x
) for x
in varargs
])))
470 infile
, outfile
, errfile
, kid
= open_pipe([command
] + list(varargs
), bufsize
)
473 for x
in stdin_lines
:
476 stdout_lines
, stderr_lines
, exit_code
= wait_on_pipe(kid
, binary_mode
)
482 return exit_code
, stdout_lines
, stderr_lines
484 def run_command_stdin(command
, error_expected
, bufsize
=0, binary_mode
=0,
485 stdin_lines
=None, *varargs
):
486 """Run COMMAND with VARARGS; input STDIN_LINES (a list of strings
487 which should include newline characters) to program via stdin - this
488 should not be very large, as if the program outputs more than the OS
489 is willing to buffer, this will deadlock, with both Python and
490 COMMAND waiting to write to each other for ever. For tests where this
491 is a problem, setting BUFSIZE to a sufficiently large value will prevent
492 the deadlock, see spawn_process().
493 Normalize Windows line endings of stdout and stderr if not BINARY_MODE.
494 Return exit code as int; stdout, stderr as lists of lines (including
496 If ERROR_EXPECTED is None, any stderr also will be printed."""
501 exit_code
, stdout_lines
, stderr_lines
= spawn_process(command
,
509 print('<TIME = %.6f>' % (stop
- start
))
510 for x
in stdout_lines
:
512 for x
in stderr_lines
:
515 if (not error_expected
) and (stderr_lines
):
516 if not options
.verbose
:
517 for x
in stderr_lines
:
522 [line
for line
in stdout_lines
if not line
.startswith("DBG:")], \
525 def create_config_dir(cfgdir
, config_contents
=None, server_contents
=None):
526 "Create config directories and files"
529 cfgfile_cfg
= os
.path
.join(cfgdir
, 'config')
530 cfgfile_srv
= os
.path
.join(cfgdir
, 'servers')
532 # create the directory
533 if not os
.path
.isdir(cfgdir
):
536 # define default config file contents if none provided
537 if config_contents
is None:
538 config_contents
= """
544 interactive-conflicts = false
547 # define default server file contents if none provided
548 if server_contents
is None:
549 http_library_str
= ""
550 if options
.http_library
:
551 http_library_str
= "http-library=%s" % (options
.http_library
)
552 server_contents
= """
556 store-plaintext-passwords=yes
558 """ % (http_library_str
)
560 file_write(cfgfile_cfg
, config_contents
)
561 file_write(cfgfile_srv
, server_contents
)
563 def _with_config_dir(args
):
564 if '--config-dir' in args
:
567 return args
+ ('--config-dir', default_config_dir
)
569 def _with_auth(args
):
570 assert '--password' not in args
571 args
= args
+ ('--password', wc_passwd
,
573 if '--username' in args
:
576 return args
+ ('--username', wc_author
)
578 # For running subversion and returning the output
579 def run_svn(error_expected
, *varargs
):
580 """Run svn with VARARGS; return exit code as int; stdout, stderr as
581 lists of lines (including line terminators).
582 If ERROR_EXPECTED is None, any stderr also will be printed. If
583 you're just checking that something does/doesn't come out of
584 stdout/stderr, you might want to use actions.run_and_verify_svn()."""
585 return run_command(svn_binary
, error_expected
, 0,
586 *(_with_auth(_with_config_dir(varargs
))))
588 # For running svnadmin. Ignores the output.
589 def run_svnadmin(*varargs
):
590 """Run svnadmin with VARARGS, returns exit code as int; stdout, stderr as
591 list of lines (including line terminators)."""
592 return run_command(svnadmin_binary
, 1, 0, *varargs
)
594 # For running svnlook. Ignores the output.
595 def run_svnlook(*varargs
):
596 """Run svnlook with VARARGS, returns exit code as int; stdout, stderr as
597 list of lines (including line terminators)."""
598 return run_command(svnlook_binary
, 1, 0, *varargs
)
600 def run_svnrdump(stdin_input
, *varargs
):
601 """Run svnrdump with VARARGS, returns exit code as int; stdout, stderr as
602 list of lines (including line terminators)."""
604 return run_command_stdin(svnrdump_binary
, 0, 1, 0, stdin_input
,
605 *(_with_auth(_with_config_dir(varargs
))))
607 return run_command(svnrdump_binary
, 1, 0,
608 *(_with_auth(_with_config_dir(varargs
))))
610 def run_svnsync(*varargs
):
611 """Run svnsync with VARARGS, returns exit code as int; stdout, stderr as
612 list of lines (including line terminators)."""
613 return run_command(svnsync_binary
, 1, 0, *(_with_config_dir(varargs
)))
615 def run_svnversion(*varargs
):
616 """Run svnversion with VARARGS, returns exit code as int; stdout, stderr
617 as list of lines (including line terminators)."""
618 return run_command(svnversion_binary
, 1, 0, *varargs
)
620 def run_entriesdump(path
):
621 """Run the entries-dump helper, returning a dict of Entry objects."""
622 # use spawn_process rather than run_command to avoid copying all the data
623 # to stdout in verbose mode.
624 exit_code
, stdout_lines
, stderr_lines
= spawn_process(entriesdump_binary
,
626 if exit_code
or stderr_lines
:
627 ### report on this? or continue to just skip it?
633 exec(''.join([line
for line
in stdout_lines
if not line
.startswith("DBG:")]))
636 def run_entriesdump_subdirs(path
):
637 """Run the entries-dump helper, returning a list of directory names."""
638 # use spawn_process rather than run_command to avoid copying all the data
639 # to stdout in verbose mode.
640 exit_code
, stdout_lines
, stderr_lines
= spawn_process(entriesdump_binary
,
641 0, 0, None, '--subdirs', path
)
642 return [line
.strip() for line
in stdout_lines
if not line
.startswith("DBG:")]
644 def run_atomic_ra_revprop_change(url
, revision
, propname
, skel
, want_error
):
645 """Run the atomic-ra-revprop-change helper, returning its exit code, stdout,
646 and stderr. For HTTP, default HTTP library is used."""
647 # use spawn_process rather than run_command to avoid copying all the data
648 # to stdout in verbose mode.
649 #exit_code, stdout_lines, stderr_lines = spawn_process(entriesdump_binary,
652 # This passes HTTP_LIBRARY in addition to our params.
653 return run_command(atomic_ra_revprop_change_binary
, True, False,
654 url
, revision
, propname
, skel
,
655 options
.http_library
, want_error
and 1 or 0)
658 # Chmod recursively on a whole subtree
659 def chmod_tree(path
, mode
, mask
):
660 for dirpath
, dirs
, files
in os
.walk(path
):
661 for name
in dirs
+ files
:
662 fullname
= os
.path
.join(dirpath
, name
)
663 if not os
.path
.islink(fullname
):
664 new_mode
= (os
.stat(fullname
)[stat
.ST_MODE
] & ~mask
) | mode
665 os
.chmod(fullname
, new_mode
)
667 # For clearing away working copies
668 def safe_rmtree(dirname
, retry
=0):
669 "Remove the tree at DIRNAME, making it writable first"
671 chmod_tree(dirname
, 0666, 0666)
672 shutil
.rmtree(dirname
)
674 if not os
.path
.exists(dirname
):
678 for delay
in (0.5, 1, 2, 4):
689 # For making local mods to files
690 def file_append(path
, new_text
):
691 "Append NEW_TEXT to file at PATH"
692 open(path
, 'a').write(new_text
)
694 # Append in binary mode
695 def file_append_binary(path
, new_text
):
696 "Append NEW_TEXT to file at PATH in binary mode"
697 open(path
, 'ab').write(new_text
)
699 # For creating new files, and making local mods to existing files.
700 def file_write(path
, contents
, mode
='w'):
701 """Write the CONTENTS to the file at PATH, opening file using MODE,
702 which is (w)rite by default."""
703 open(path
, mode
).write(contents
)
705 # For replacing parts of contents in an existing file, with new content.
706 def file_substitute(path
, contents
, new_contents
):
707 """Replace the CONTENTS in the file at PATH using the NEW_CONTENTS"""
708 fcontent
= open(path
, 'r').read().replace(contents
, new_contents
)
709 open(path
, 'w').write(fcontent
)
711 # For creating blank new repositories
712 def create_repos(path
):
713 """Create a brand-new SVN repository at PATH. If PATH does not yet
716 if not os
.path
.exists(path
):
717 os
.makedirs(path
) # this creates all the intermediate dirs, if neccessary
719 opts
= ("--bdb-txn-nosync",)
720 if options
.server_minor_version
< 5:
721 opts
+= ("--pre-1.5-compatible",)
722 elif options
.server_minor_version
< 6:
723 opts
+= ("--pre-1.6-compatible",)
724 elif options
.server_minor_version
< 7:
725 opts
+= ("--pre-1.7-compatible",)
726 if options
.fs_type
is not None:
727 opts
+= ("--fs-type=" + options
.fs_type
,)
728 exit_code
, stdout
, stderr
= run_command(svnadmin_binary
, 1, 0, "create",
731 # Skip tests if we can't create the repository.
734 if line
.find('Unknown FS type') != -1:
736 # If the FS type is known, assume the repos couldn't be created
737 # (e.g. due to a missing 'svnadmin' binary).
738 raise SVNRepositoryCreateFailure("".join(stderr
).rstrip())
740 # Allow unauthenticated users to write to the repos, for ra_svn testing.
741 file_write(get_svnserve_conf_file_path(path
),
742 "[general]\nauth-access = write\n");
743 if options
.enable_sasl
:
744 file_append(get_svnserve_conf_file_path(path
),
745 "realm = svntest\n[sasl]\nuse-sasl = true\n")
747 file_append(get_svnserve_conf_file_path(path
), "password-db = passwd\n")
748 file_append(os
.path
.join(path
, "conf", "passwd"),
749 "[users]\njrandom = rayjandom\njconstant = rayjandom\n");
751 if options
.fs_type
is None or options
.fs_type
== 'fsfs':
753 if options
.config_file
is not None:
754 shutil
.copy(options
.config_file
, get_fsfs_conf_file_path(path
))
757 if options
.fsfs_sharding
is not None:
758 def transform_line(line
):
759 if line
.startswith('layout '):
760 if options
.fsfs_sharding
> 0:
761 line
= 'layout sharded %d' % options
.fsfs_sharding
763 line
= 'layout linear'
767 format_file_path
= get_fsfs_format_file_path(path
)
768 contents
= open(format_file_path
, 'rb').read()
771 new_contents
= "".join([transform_line(line
) + "\n"
772 for line
in contents
.split("\n")])
773 if new_contents
[-1] == "\n":
774 # we don't currently allow empty lines (\n\n) in the format file.
775 new_contents
= new_contents
[:-1]
778 os
.chmod(format_file_path
, 0666)
779 file_write(format_file_path
, new_contents
, 'wb')
782 # Note that some tests (currently only commit_tests) create their own
783 # post-commit hooks, which would override this one. :-(
784 if options
.fsfs_packing
:
786 abs_path
= os
.path
.abspath(path
)
787 create_python_hook_script(get_post_commit_hook_path(abs_path
),
788 "import subprocess\n"
791 "sys.exit(subprocess.Popen(command).wait())\n"
792 % repr([svnadmin_binary
, 'pack', abs_path
]))
794 # make the repos world-writeable, for mod_dav_svn's sake.
795 chmod_tree(path
, 0666, 0666)
797 # For copying a repository
798 def copy_repos(src_path
, dst_path
, head_revision
, ignore_uuid
= 1):
799 "Copy the repository SRC_PATH, with head revision HEAD_REVISION, to DST_PATH"
801 # Save any previous value of SVN_DBG_QUIET
802 saved_quiet
= os
.environ
.get('SVN_DBG_QUIET')
803 os
.environ
['SVN_DBG_QUIET'] = 'y'
805 # Do an svnadmin dump|svnadmin load cycle. Print a fake pipe command so that
806 # the displayed CMDs can be run by hand
807 create_repos(dst_path
)
808 dump_args
= ['dump', src_path
]
809 load_args
= ['load', dst_path
]
812 load_args
= load_args
+ ['--ignore-uuid']
814 sys
.stdout
.write('CMD: %s %s | %s %s\n' %
815 (os
.path
.basename(svnadmin_binary
), ' '.join(dump_args
),
816 os
.path
.basename(svnadmin_binary
), ' '.join(load_args
)))
820 dump_in
, dump_out
, dump_err
, dump_kid
= open_pipe(
821 [svnadmin_binary
] + dump_args
)
822 load_in
, load_out
, load_err
, load_kid
= open_pipe(
823 [svnadmin_binary
] + load_args
,
824 stdin
=dump_out
) # Attached to dump_kid
828 print('<TIME = %.6f>' % (stop
- start
))
830 load_stdout
, load_stderr
, load_exit_code
= wait_on_pipe(load_kid
, True)
831 dump_stdout
, dump_stderr
, dump_exit_code
= wait_on_pipe(dump_kid
, True)
836 #load_in is dump_out so it's already closed.
840 if saved_quiet
is None:
841 del os
.environ
['SVN_DBG_QUIET']
843 os
.environ
['SVN_DBG_QUIET'] = saved_quiet
845 dump_re
= re
.compile(r
'^\* Dumped revision (\d+)\.\r?$')
847 for dump_line
in dump_stderr
:
848 match
= dump_re
.match(dump_line
)
849 if not match
or match
.group(1) != str(expect_revision
):
850 print('ERROR: dump failed: %s' % dump_line
.strip())
851 raise SVNRepositoryCopyFailure
853 if expect_revision
!= head_revision
+ 1:
854 print('ERROR: dump failed; did not see revision %s' % head_revision
)
855 raise SVNRepositoryCopyFailure
857 load_re
= re
.compile(r
'^------- Committed revision (\d+) >>>\r?$')
859 for load_line
in load_stdout
:
860 match
= load_re
.match(load_line
)
862 if match
.group(1) != str(expect_revision
):
863 print('ERROR: load failed: %s' % load_line
.strip())
864 raise SVNRepositoryCopyFailure
866 if expect_revision
!= head_revision
+ 1:
867 print('ERROR: load failed; did not see revision %s' % head_revision
)
868 raise SVNRepositoryCopyFailure
871 def canonicalize_url(input):
872 "Canonicalize the url, if the scheme is unknown, returns intact input"
874 m
= re
.match(r
"^((file://)|((svn|svn\+ssh|http|https)(://)))", input)
877 return scheme
+ re
.sub(r
'//*', '/', input[len(scheme
):])
882 def create_python_hook_script(hook_path
, hook_script_code
):
883 """Create a Python hook script at HOOK_PATH with the specified
887 # Use an absolute path since the working directory is not guaranteed
888 hook_path
= os
.path
.abspath(hook_path
)
889 # Fill the python file.
890 file_write("%s.py" % hook_path
, hook_script_code
)
891 # Fill the batch wrapper file.
892 file_append("%s.bat" % hook_path
,
893 "@\"%s\" %s.py %%*\n" % (sys
.executable
, hook_path
))
895 # For all other platforms
896 file_write(hook_path
, "#!%s\n%s" % (sys
.executable
, hook_script_code
))
897 os
.chmod(hook_path
, 0755)
899 def write_restrictive_svnserve_conf(repo_dir
, anon_access
="none"):
900 "Create a restrictive authz file ( no anynomous access )."
902 fp
= open(get_svnserve_conf_file_path(repo_dir
), 'w')
903 fp
.write("[general]\nanon-access = %s\nauth-access = write\n"
904 "authz-db = authz\n" % anon_access
)
905 if options
.enable_sasl
:
906 fp
.write("realm = svntest\n[sasl]\nuse-sasl = true\n");
908 fp
.write("password-db = passwd\n")
911 # Warning: because mod_dav_svn uses one shared authz file for all
912 # repositories, you *cannot* use write_authz_file in any test that
913 # might be run in parallel.
915 # write_authz_file can *only* be used in test suites which disable
916 # parallel execution at the bottom like so
917 # if __name__ == '__main__':
918 # svntest.main.run_tests(test_list, serial_only = True)
919 def write_authz_file(sbox
, rules
, sections
=None):
920 """Write an authz file to SBOX, appropriate for the RA method used,
921 with authorizations rules RULES mapping paths to strings containing
922 the rules. You can add sections SECTIONS (ex. groups, aliases...) with
923 an appropriate list of mappings.
925 fp
= open(sbox
.authz_file
, 'w')
927 # When the sandbox repository is read only it's name will be different from
928 # the repository name.
929 repo_name
= sbox
.repo_dir
930 while repo_name
[-1] == '/':
931 repo_name
= repo_name
[:-1]
932 repo_name
= os
.path
.basename(repo_name
)
934 if sbox
.repo_url
.startswith("http"):
935 prefix
= repo_name
+ ":"
939 for p
, r
in sections
.items():
940 fp
.write("[%s]\n%s\n" % (p
, r
))
942 for p
, r
in rules
.items():
943 fp
.write("[%s%s]\n%s\n" % (prefix
, p
, r
))
946 def use_editor(func
):
947 os
.environ
['SVN_EDITOR'] = svneditor_script
948 os
.environ
['SVN_MERGE'] = svneditor_script
949 os
.environ
['SVNTEST_EDITOR_FUNC'] = func
950 os
.environ
['SVN_TEST_PYTHON'] = sys
.executable
952 def mergeinfo_notify_line(revstart
, revend
):
953 """Return an expected output line that describes the beginning of a
954 mergeinfo recording notification on revisions REVSTART through REVEND."""
957 revstart
= abs(revstart
)
958 return "--- Recording mergeinfo for reverse merge of r%ld .*:\n" \
961 return "--- Recording mergeinfo for merge of r%ld .*:\n" % (revstart
)
962 elif (revstart
< revend
):
963 return "--- Recording mergeinfo for merge of r%ld through r%ld .*:\n" \
966 return "--- Recording mergeinfo for reverse merge of r%ld through " \
967 "r%ld .*:\n" % (revstart
, revend
)
969 def merge_notify_line(revstart
=None, revend
=None, same_URL
=True,
971 """Return an expected output line that describes the beginning of a
972 merge operation on revisions REVSTART through REVEND. Omit both
973 REVSTART and REVEND for the case where the left and right sides of
974 the merge are from different URLs."""
975 from_foreign_phrase
= foreign
and "\(from foreign repository\) " or ""
977 return "--- Merging differences between %srepository URLs into '.+':\n" \
978 % (foreign
and "foreign " or "")
981 # The left and right sides of the merge are from different URLs.
982 return "--- Merging differences between %srepository URLs into '.+':\n" \
983 % (foreign
and "foreign " or "")
985 return "--- Reverse-merging %sr%ld into '.+':\n" \
986 % (from_foreign_phrase
, abs(revstart
))
988 return "--- Merging %sr%ld into '.+':\n" \
989 % (from_foreign_phrase
, revstart
)
991 if revstart
> revend
:
992 return "--- Reverse-merging %sr%ld through r%ld into '.+':\n" \
993 % (from_foreign_phrase
, revstart
, revend
)
995 return "--- Merging %sr%ld through r%ld into '.+':\n" \
996 % (from_foreign_phrase
, revstart
, revend
)
1000 "Conjure up a log message based on the calling test."
1002 for idx
in range(1, 100):
1003 frame
= sys
._getframe
(idx
)
1005 # If this frame isn't from a function in *_tests.py, then skip it.
1006 filename
= frame
.f_code
.co_filename
1007 if not filename
.endswith('_tests.py'):
1010 # There should be a test_list in this module.
1011 test_list
= frame
.f_globals
.get('test_list')
1012 if test_list
is None:
1015 # If the function is not in the test_list, then skip it.
1016 func_name
= frame
.f_code
.co_name
1017 func_ob
= frame
.f_globals
.get(func_name
)
1018 if func_ob
not in test_list
:
1021 # Make the log message look like a line from a traceback.
1022 # Well...close. We use single quotes to avoid interfering with the
1023 # double-quote quoting performed on Windows
1024 return "File '%s', line %d, in %s" % (filename
, frame
.f_lineno
, func_name
)
1027 ######################################################################
1028 # Functions which check the test configuration
1029 # (useful for conditional XFails)
1031 def is_ra_type_dav():
1032 return options
.test_area_url
.startswith('http')
1034 def is_ra_type_dav_neon():
1035 """Return True iff running tests over RA-Neon.
1036 CAUTION: Result is only valid if svn was built to support both."""
1037 return options
.test_area_url
.startswith('http') and \
1038 (options
.http_library
== "neon")
1040 def is_ra_type_dav_serf():
1041 """Return True iff running tests over RA-Serf.
1042 CAUTION: Result is only valid if svn was built to support both."""
1043 return options
.test_area_url
.startswith('http') and \
1044 (options
.http_library
== "serf")
1046 def is_ra_type_svn():
1047 """Return True iff running tests over RA-svn."""
1048 return options
.test_area_url
.startswith('svn')
1050 def is_ra_type_file():
1051 """Return True iff running tests over RA-local."""
1052 return options
.test_area_url
.startswith('file')
1054 def is_fs_type_fsfs():
1055 # This assumes that fsfs is the default fs implementation.
1056 return options
.fs_type
== 'fsfs' or options
.fs_type
is None
1058 def is_os_windows():
1059 return os
.name
== 'nt'
1061 def is_windows_type_dav():
1062 return is_os_windows() and is_ra_type_dav()
1065 return os
.name
== 'posix'
1068 return sys
.platform
== 'darwin'
1070 def is_fs_case_insensitive():
1071 return (is_os_darwin() or is_os_windows())
1073 def server_has_mergeinfo():
1074 return options
.server_minor_version
>= 5
1076 def server_has_revprop_commit():
1077 return options
.server_minor_version
>= 5
1079 def server_authz_has_aliases():
1080 return options
.server_minor_version
>= 5
1082 def server_gets_client_capabilities():
1083 return options
.server_minor_version
>= 5
1085 def server_has_partial_replay():
1086 return options
.server_minor_version
>= 5
1088 def server_enforces_date_syntax():
1089 return options
.server_minor_version
>= 5
1091 def server_has_atomic_revprop():
1092 return options
.server_minor_version
>= 7
1094 ######################################################################
1097 class TestSpawningThread(threading
.Thread
):
1098 """A thread that runs test cases in their own processes.
1099 Receives test numbers to run from the queue, and saves results into
1100 the results field."""
1101 def __init__(self
, queue
):
1102 threading
.Thread
.__init
__(self
)
1109 next_index
= self
.queue
.get_nowait()
1113 self
.run_one(next_index
)
1115 def run_one(self
, index
):
1116 command
= sys
.argv
[0]
1119 args
.append(str(index
))
1121 # add some startup arguments from this process
1123 args
.append('--fs-type=' + options
.fs_type
)
1124 if options
.test_area_url
:
1125 args
.append('--url=' + options
.test_area_url
)
1129 args
.append('--cleanup')
1130 if options
.enable_sasl
:
1131 args
.append('--enable-sasl')
1132 if options
.http_library
:
1133 args
.append('--http-library=' + options
.http_library
)
1134 if options
.server_minor_version
:
1135 args
.append('--server-minor-version=' + str(options
.server_minor_version
))
1137 result
, stdout_lines
, stderr_lines
= spawn_process(command
, 0, 0, None,
1139 self
.results
.append((index
, result
, stdout_lines
, stderr_lines
))
1142 sys
.stdout
.write('.')
1144 sys
.stdout
.write('F')
1149 """Encapsulate a single test case (predicate), including logic for
1150 runing the test and test list output."""
1152 def __init__(self
, func
, index
):
1153 self
.pred
= svntest
.testcase
.create_test_case(func
)
1157 if options
.verbose
and self
.pred
.inprogress
:
1158 print(" %2d %-5s %s [[%s]]" % (self
.index
,
1159 self
.pred
.list_mode(),
1160 self
.pred
.description
,
1161 self
.pred
.inprogress
))
1163 print(" %2d %-5s %s" % (self
.index
,
1164 self
.pred
.list_mode(),
1165 self
.pred
.description
))
1168 def get_function_name(self
):
1169 return self
.pred
.get_function_name()
1171 def _print_name(self
, prefix
):
1172 if self
.pred
.inprogress
:
1173 print("%s %s %s: %s [[WIMP: %s]]" % (prefix
,
1174 os
.path
.basename(sys
.argv
[0]),
1176 self
.pred
.description
,
1177 self
.pred
.inprogress
))
1179 print("%s %s %s: %s" % (prefix
,
1180 os
.path
.basename(sys
.argv
[0]),
1182 self
.pred
.description
))
1186 """Run self.pred and return the result. The return value is
1187 - 0 if the test was successful
1188 - 1 if it errored in a way that indicates test failure
1189 - 2 if the test skipped
1191 sbox_name
= self
.pred
.get_sandbox_name()
1193 sandbox
= svntest
.sandbox
.Sandbox(sbox_name
, self
.index
)
1197 # Explicitly set this so that commands that commit but don't supply a
1198 # log message will fail rather than invoke an editor.
1199 # Tests that want to use an editor should invoke svntest.main.use_editor.
1200 os
.environ
['SVN_EDITOR'] = ''
1201 os
.environ
['SVNTEST_EDITOR_FUNC'] = ''
1203 if options
.use_jsvn
:
1204 # Set this SVNKit specific variable to the current test (test name plus
1205 # its index) being run so that SVNKit daemon could use this test name
1206 # for its separate log file
1207 os
.environ
['SVN_CURRENT_TEST'] = os
.path
.basename(sys
.argv
[0]) + "_" + \
1210 svntest
.actions
.no_sleep_for_timestamps()
1211 svntest
.actions
.do_relocate_validation()
1213 saved_dir
= os
.getcwd()
1215 rc
= self
.pred
.run(sandbox
)
1217 self
._print
_name
('STYLE ERROR in')
1218 print('Test driver returned a status code.')
1220 result
= svntest
.testcase
.RESULT_OK
1222 result
= svntest
.testcase
.RESULT_SKIP
1224 result
= svntest
.testcase
.RESULT_FAIL
1225 # We captured Failure and its subclasses. We don't want to print
1226 # anything for plain old Failure since that just indicates test
1227 # failure, rather than relevant information. However, if there
1228 # *is* information in the exception's arguments, then print it.
1229 if ex
.__class
__ != Failure
or ex
.args
:
1232 print('EXCEPTION: %s: %s' % (ex
.__class
__.__name
__, ex_args
))
1234 print('EXCEPTION: %s' % ex
.__class
__.__name
__)
1235 traceback
.print_exc(file=sys
.stdout
)
1237 except KeyboardInterrupt:
1238 print('Interrupted')
1240 except SystemExit, ex
:
1241 print('EXCEPTION: SystemExit(%d), skipping cleanup' % ex
.code
)
1242 self
._print
_name
(ex
.code
and 'FAIL: ' or 'PASS: ')
1245 result
= svntest
.testcase
.RESULT_FAIL
1246 print('UNEXPECTED EXCEPTION:')
1247 traceback
.print_exc(file=sys
.stdout
)
1251 exit_code
, result_text
, result_benignity
= self
.pred
.results(result
)
1252 if not (options
.quiet
and result_benignity
):
1253 self
._print
_name
(result_text
)
1254 if sandbox
is not None and exit_code
!= 1 and options
.cleanup
:
1255 sandbox
.cleanup_test_paths()
1258 ######################################################################
1259 # Main testing functions
1261 # These two functions each take a TEST_LIST as input. The TEST_LIST
1262 # should be a list of test functions; each test function should take
1263 # no arguments and return a 0 on success, non-zero on failure.
1264 # Ideally, each test should also have a short, one-line docstring (so
1265 # it can be displayed by the 'list' command.)
1267 # Func to run one test in the list.
1268 def run_one_test(n
, test_list
, finished_tests
= None):
1269 """Run the Nth client test in TEST_LIST, return the result.
1271 If we're running the tests in parallel spawn the test in a new process.
1274 # allow N to be negative, so './basic_tests.py -- -1' works
1275 num_tests
= len(test_list
) - 1
1276 if (n
== 0) or (abs(n
) > num_tests
):
1277 print("There is no test %s.\n" % n
)
1283 exit_code
= TestRunner(test_list
[n
], n
).run()
1286 def _internal_run_tests(test_list
, testnums
, parallel
):
1287 """Run the tests from TEST_LIST whose indices are listed in TESTNUMS.
1289 If we're running the tests in parallel spawn as much parallel processes
1290 as requested and gather the results in a temp. buffer when a child
1291 process is finished.
1299 for testnum
in testnums
:
1300 if run_one_test(testnum
, test_list
) == 1:
1303 number_queue
= queue
.Queue()
1304 for num
in testnums
:
1305 number_queue
.put(num
)
1307 threads
= [ TestSpawningThread(number_queue
) for i
in range(parallel
) ]
1314 # list of (index, result, stdout, stderr)
1317 results
+= t
.results
1320 # terminate the line of dots
1323 # all tests are finished, find out the result and print the logs.
1324 for (index
, result
, stdout_lines
, stderr_lines
) in results
:
1326 for line
in stdout_lines
:
1327 sys
.stdout
.write(line
)
1329 for line
in stderr_lines
:
1330 sys
.stdout
.write(line
)
1334 svntest
.sandbox
.cleanup_deferred_test_paths()
1338 def create_default_options():
1339 """Set the global options to the defaults, as provided by the argument
1344 def _create_parser():
1345 """Return a parser for our test suite."""
1347 usage
= 'usage: %prog [options] [<test> ...]'
1348 parser
= optparse
.OptionParser(usage
=usage
)
1349 parser
.add_option('-l', '--list', action
='store_true', dest
='list_tests',
1350 help='Print test doc strings instead of running them')
1351 parser
.add_option('-v', '--verbose', action
='store_true', dest
='verbose',
1352 help='Print binary command-lines (not with --quiet)')
1353 parser
.add_option('-q', '--quiet', action
='store_true',
1354 help='Print only unexpected results (not with --verbose)')
1355 parser
.add_option('-p', '--parallel', action
='store_const', const
=5,
1357 help='Run the tests in parallel')
1358 parser
.add_option('-c', action
='store_true', dest
='is_child_process',
1359 help='Flag if we are running this python test as a ' +
1361 parser
.add_option('--url', action
='store',
1362 help='Base url to the repos (e.g. svn://localhost)')
1363 parser
.add_option('--fs-type', action
='store',
1364 help='Subversion file system type (fsfs or bdb)')
1365 parser
.add_option('--cleanup', action
='store_true',
1366 help='Whether to clean up')
1367 parser
.add_option('--enable-sasl', action
='store_true',
1368 help='Whether to enable SASL authentication')
1369 parser
.add_option('--bin', action
='store', dest
='svn_bin',
1370 help='Use the svn binaries installed in this path')
1371 parser
.add_option('--use-jsvn', action
='store_true',
1372 help="Use the jsvn (SVNKit based) binaries. Can be " +
1373 "combined with --bin to point to a specific path")
1374 parser
.add_option('--http-library', action
='store',
1375 help="Make svn use this DAV library (neon or serf) if " +
1376 "it supports both, else assume it's using this " +
1377 "one; the default is neon")
1378 parser
.add_option('--server-minor-version', type='int', action
='store',
1379 help="Set the minor version for the server ('4', " +
1381 parser
.add_option('--fsfs-packing', action
='store_true',
1382 help="Run 'svnadmin pack' automatically")
1383 parser
.add_option('--fsfs-sharding', action
='store', type='int',
1384 help='Default shard size (for fsfs)')
1385 parser
.add_option('--config-file', action
='store',
1386 help="Configuration file for tests.")
1387 parser
.add_option('--keep-local-tmp', action
='store_true',
1388 help="Don't remove svn-test-work/local_tmp after test " +
1389 "run is complete. Useful for debugging failures.")
1390 parser
.add_option('--development', action
='store_true',
1391 help='Test development mode: provides more detailed ' +
1392 'test output and ignores all exceptions in the ' +
1393 'run_and_verify* functions. This option is only ' +
1394 'useful during test development!')
1396 # most of the defaults are None, but some are other values, set them here
1397 parser
.set_defaults(
1398 server_minor_version
=7,
1399 url
=file_scheme_prefix
+ pathname2url(os
.path
.abspath(os
.getcwd())),
1400 http_library
='serf')
1405 def _parse_options(arglist
=sys
.argv
[1:]):
1406 """Parse the arguments in arg_list, and set the global options object with
1411 parser
= _create_parser()
1412 (options
, args
) = parser
.parse_args(arglist
)
1414 # some sanity checking
1415 if options
.verbose
and options
.quiet
:
1416 parser
.error("'verbose' and 'quiet' are incompatible")
1417 if options
.fsfs_packing
and not options
.fsfs_sharding
:
1418 parser
.error("--fsfs-packing requires --fsfs-sharding")
1419 if options
.server_minor_version
< 4 or options
.server_minor_version
> 7:
1420 parser
.error("test harness only supports server minor versions 4-7")
1423 if options
.url
[-1:] == '/': # Normalize url to have no trailing slash
1424 options
.test_area_url
= options
.url
[:-1]
1426 options
.test_area_url
= options
.url
1428 return (parser
, args
)
1431 # Main func. This is the "entry point" that all the test scripts call
1432 # to run their list of tests.
1434 # This routine parses sys.argv to decide what to do.
1435 def run_tests(test_list
, serial_only
= False):
1436 """Main routine to run all tests in TEST_LIST.
1438 NOTE: this function does not return. It does a sys.exit() with the
1439 appropriate exit code.
1442 global pristine_greek_repos_url
1444 global svnadmin_binary
1445 global svnlook_binary
1446 global svnsync_binary
1447 global svndumpfilter_binary
1448 global svnversion_binary
1454 (parser
, args
) = _parse_options()
1457 parser
= _create_parser()
1459 # parse the positional arguments (test nums, names)
1463 testnums
.append(int(arg
))
1466 # Do nothing for now.
1471 # Check if the argument is a range
1472 numberstrings
= arg
.split(':');
1473 if len(numberstrings
) != 2:
1474 numberstrings
= arg
.split('-');
1475 if len(numberstrings
) != 2:
1477 left
= int(numberstrings
[0])
1478 right
= int(numberstrings
[1])
1482 for nr
in range(left
,right
+1):
1491 # Check if the argument is a function name, and translate
1492 # it to a number if possible
1493 for testnum
in list(range(1, len(test_list
))):
1494 test_case
= TestRunner(test_list
[testnum
], testnum
)
1495 if test_case
.get_function_name() == str(arg
):
1496 testnums
.append(testnum
)
1503 parser
.error("invalid test number, range of numbers, " +
1504 "or function '%s'\n" % arg
)
1506 # Calculate pristine_greek_repos_url from test_area_url.
1507 pristine_greek_repos_url
= options
.test_area_url
+ '/' + pathname2url(pristine_greek_repos_dir
)
1509 if options
.use_jsvn
:
1510 if options
.svn_bin
is None:
1511 options
.svn_bin
= ''
1512 svn_binary
= os
.path
.join(options
.svn_bin
, 'jsvn' + _bat
)
1513 svnadmin_binary
= os
.path
.join(options
.svn_bin
, 'jsvnadmin' + _bat
)
1514 svnlook_binary
= os
.path
.join(options
.svn_bin
, 'jsvnlook' + _bat
)
1515 svnsync_binary
= os
.path
.join(options
.svn_bin
, 'jsvnsync' + _bat
)
1516 svndumpfilter_binary
= os
.path
.join(options
.svn_bin
,
1517 'jsvndumpfilter' + _bat
)
1518 svnversion_binary
= os
.path
.join(options
.svn_bin
,
1519 'jsvnversion' + _bat
)
1522 svn_binary
= os
.path
.join(options
.svn_bin
, 'svn' + _exe
)
1523 svnadmin_binary
= os
.path
.join(options
.svn_bin
, 'svnadmin' + _exe
)
1524 svnlook_binary
= os
.path
.join(options
.svn_bin
, 'svnlook' + _exe
)
1525 svnsync_binary
= os
.path
.join(options
.svn_bin
, 'svnsync' + _exe
)
1526 svndumpfilter_binary
= os
.path
.join(options
.svn_bin
,
1527 'svndumpfilter' + _exe
)
1528 svnversion_binary
= os
.path
.join(options
.svn_bin
, 'svnversion' + _exe
)
1530 ######################################################################
1532 # Cleanup: if a previous run crashed or interrupted the python
1533 # interpreter, then `temp_dir' was never removed. This can cause wonkiness.
1534 if not options
.is_child_process
:
1535 safe_rmtree(temp_dir
, 1)
1538 # If no test numbers were listed explicitly, include all of them:
1539 testnums
= list(range(1, len(test_list
)))
1541 if options
.list_tests
:
1542 print("Test # Mode Test Description")
1543 print("------ ----- ----------------")
1544 for testnum
in testnums
:
1545 TestRunner(test_list
[testnum
], testnum
).list()
1547 # done. just exit with success.
1550 # don't run tests in parallel when the tests don't support it or there
1551 # are only a few tests to run.
1552 if serial_only
or len(testnums
) < 2:
1553 options
.parallel
= 0
1555 if not options
.is_child_process
:
1556 # Build out the default configuration directory
1557 create_config_dir(default_config_dir
)
1559 # Setup the pristine repository
1560 svntest
.actions
.setup_pristine_greek_repository()
1563 exit_code
= _internal_run_tests(test_list
, testnums
, options
.parallel
)
1565 # Remove all scratchwork: the 'pristine' repository, greek tree, etc.
1566 # This ensures that an 'import' will happen the next time we run.
1567 if not options
.is_child_process
and not options
.keep_local_tmp
:
1568 safe_rmtree(temp_dir
, 1)
1570 # Cleanup after ourselves.
1571 svntest
.sandbox
.cleanup_deferred_test_paths()
1573 # Return the appropriate exit code from the tests.