summaryrefslogtreecommitdiff
path: root/subversion/tests/cmdline/svntest/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/cmdline/svntest/main.py')
-rw-r--r--subversion/tests/cmdline/svntest/main.py492
1 files changed, 368 insertions, 124 deletions
diff --git a/subversion/tests/cmdline/svntest/main.py b/subversion/tests/cmdline/svntest/main.py
index 0d1ca91..de59c06 100644
--- a/subversion/tests/cmdline/svntest/main.py
+++ b/subversion/tests/cmdline/svntest/main.py
@@ -36,6 +36,7 @@ import xml
import urllib
import logging
import hashlib
+import zipfile
from urlparse import urlparse
try:
@@ -53,7 +54,7 @@ import svntest
from svntest import Failure
from svntest import Skip
-SVN_VER_MINOR = 8
+SVN_VER_MINOR = 9
######################################################################
#
@@ -111,7 +112,7 @@ class SVNRepositoryCreateFailure(Failure):
# Windows specifics
if sys.platform == 'win32':
windows = True
- file_scheme_prefix = 'file:'
+ file_scheme_prefix = 'file:///'
_exe = '.exe'
_bat = '.bat'
os.environ['SVN_DBG_STACKTRACES_TO_STDERR'] = 'y'
@@ -131,6 +132,10 @@ else:
wc_author = 'jrandom'
wc_passwd = 'rayjandom'
+# Username and password used by svnrdump in dump/load cross-checks
+crosscheck_username = '__dumpster__'
+crosscheck_password = '__loadster__'
+
# Username and password used by the working copies for "second user"
# scenarios
wc_author2 = 'jconstant' # use the same password as wc_author
@@ -141,23 +146,30 @@ stack_trace_regexp = r'(?:.*subversion[\\//].*\.c:[0-9]*,$|.*apr_err=.*)'
os.environ['LC_ALL'] = 'C'
######################################################################
-# The locations of the svn, svnadmin and svnlook binaries, relative to
-# the only scripts that import this file right now (they live in ../).
+# The locations of the svn binaries.
# Use --bin to override these defaults.
-svn_binary = os.path.abspath('../../svn/svn' + _exe)
-svnadmin_binary = os.path.abspath('../../svnadmin/svnadmin' + _exe)
-svnlook_binary = os.path.abspath('../../svnlook/svnlook' + _exe)
-svnrdump_binary = os.path.abspath('../../svnrdump/svnrdump' + _exe)
-svnsync_binary = os.path.abspath('../../svnsync/svnsync' + _exe)
-svnversion_binary = os.path.abspath('../../svnversion/svnversion' + _exe)
-svndumpfilter_binary = os.path.abspath('../../svndumpfilter/svndumpfilter' + \
- _exe)
-svnmucc_binary=os.path.abspath('../../svnmucc/svnmucc' + _exe)
-entriesdump_binary = os.path.abspath('entries-dump' + _exe)
-atomic_ra_revprop_change_binary = os.path.abspath('atomic-ra-revprop-change' + \
- _exe)
-wc_lock_tester_binary = os.path.abspath('../libsvn_wc/wc-lock-tester' + _exe)
-wc_incomplete_tester_binary = os.path.abspath('../libsvn_wc/wc-incomplete-tester' + _exe)
+def P(relpath,
+ head=os.path.dirname(os.path.dirname(os.path.abspath('.')))
+ ):
+ if sys.platform=='win32':
+ return os.path.join(head, relpath + '.exe')
+ else:
+ return os.path.join(head, relpath)
+svn_binary = P('svn/svn')
+svnadmin_binary = P('svnadmin/svnadmin')
+svnlook_binary = P('svnlook/svnlook')
+svnrdump_binary = P('svnrdump/svnrdump')
+svnsync_binary = P('svnsync/svnsync')
+svnversion_binary = P('svnversion/svnversion')
+svndumpfilter_binary = P('svndumpfilter/svndumpfilter')
+svnmucc_binary = P('svnmucc/svnmucc')
+svnfsfs_binary = P('svnfsfs/svnfsfs')
+entriesdump_binary = P('tests/cmdline/entries-dump')
+lock_helper_binary = P('tests/cmdline/lock-helper')
+atomic_ra_revprop_change_binary = P('tests/cmdline/atomic-ra-revprop-change')
+wc_lock_tester_binary = P('tests/libsvn_wc/wc-lock-tester')
+wc_incomplete_tester_binary = P('tests/libsvn_wc/wc-incomplete-tester')
+del P
######################################################################
# The location of svnauthz binary, relative to the only scripts that
@@ -539,6 +551,15 @@ def run_command_stdin(command, error_expected, bufsize=-1, binary_mode=False,
and not any(map(lambda arg: 'prop_tests-12' in arg, varargs)):
raise Failure("Repository diskpath in %s: %r" % (name, lines))
+ valgrind_diagnostic = False
+ # A valgrind diagnostic will raise a failure if the command is
+ # expected to run without error. When an error is expected any
+ # subsequent error pattern matching is usually lenient and will not
+ # detect the diagnostic so make sure a failure is raised here.
+ if error_expected and stderr_lines:
+ if any(map(lambda arg: re.match('==[0-9]+==', arg), stderr_lines)):
+ valgrind_diagnostic = True
+
stop = time.time()
logger.info('<TIME = %.6f>' % (stop - start))
for x in stdout_lines:
@@ -546,7 +567,8 @@ def run_command_stdin(command, error_expected, bufsize=-1, binary_mode=False,
for x in stderr_lines:
logger.info(x.rstrip())
- if (not error_expected) and ((stderr_lines) or (exit_code != 0)):
+ if (((not error_expected) and ((stderr_lines) or (exit_code != 0)))
+ or valgrind_diagnostic):
for x in stderr_lines:
logger.warning(x.rstrip())
if len(varargs) <= 5:
@@ -561,7 +583,8 @@ def run_command_stdin(command, error_expected, bufsize=-1, binary_mode=False,
stderr_lines
def create_config_dir(cfgdir, config_contents=None, server_contents=None,
- ssl_cert=None, ssl_url=None, http_proxy=None):
+ ssl_cert=None, ssl_url=None, http_proxy=None,
+ exclusive_wc_locks=None):
"Create config directories and files"
# config file names
@@ -582,25 +605,41 @@ password-stores =
[miscellany]
interactive-conflicts = false
"""
-
+ if exclusive_wc_locks:
+ config_contents += """
+[working-copy]
+exclusive-locking = true
+"""
# define default server file contents if none provided
if server_contents is None:
http_library_str = ""
if options.http_library:
http_library_str = "http-library=%s" % (options.http_library)
http_proxy_str = ""
+ http_proxy_username_str = ""
+ http_proxy_password_str = ""
if options.http_proxy:
http_proxy_parsed = urlparse("//" + options.http_proxy)
http_proxy_str = "http-proxy-host=%s\n" % (http_proxy_parsed.hostname) + \
"http-proxy-port=%d" % (http_proxy_parsed.port or 80)
+ if options.http_proxy_username:
+ http_proxy_username_str = "http-proxy-username=%s" % \
+ (options.http_proxy_username)
+ if options.http_proxy_password:
+ http_proxy_password_str = "http-proxy-password=%s" % \
+ (options.http_proxy_password)
+
server_contents = """
#
[global]
%s
%s
+%s
+%s
store-plaintext-passwords=yes
store-passwords=yes
-""" % (http_library_str, http_proxy_str)
+""" % (http_library_str, http_proxy_str, http_proxy_username_str,
+ http_proxy_password_str)
file_write(cfgfile_cfg, config_contents)
file_write(cfgfile_srv, server_contents)
@@ -661,14 +700,25 @@ def _with_config_dir(args):
else:
return args + ('--config-dir', default_config_dir)
+class svnrdump_crosscheck_authentication:
+ pass
+
def _with_auth(args):
assert '--password' not in args
- args = args + ('--password', wc_passwd,
+ if svnrdump_crosscheck_authentication in args:
+ args = filter(lambda x: x is not svnrdump_crosscheck_authentication, args)
+ auth_username = crosscheck_username
+ auth_password = crosscheck_password
+ else:
+ auth_username = wc_author
+ auth_password = wc_passwd
+
+ args = args + ('--password', auth_password,
'--no-auth-cache' )
if '--username' in args:
return args
else:
- return args + ('--username', wc_author )
+ return args + ('--username', auth_username )
# For running subversion and returning the output
def run_svn(error_expected, *varargs):
@@ -716,7 +766,8 @@ def run_svnrdump(stdin_input, *varargs):
def run_svnsync(*varargs):
"""Run svnsync with VARARGS, returns exit code as int; stdout, stderr as
list of lines (including line terminators)."""
- return run_command(svnsync_binary, 1, False, *(_with_config_dir(varargs)))
+ return run_command(svnsync_binary, 1, False,
+ *(_with_auth(_with_config_dir(varargs))))
def run_svnversion(*varargs):
"""Run svnversion with VARARGS, returns exit code as int; stdout, stderr
@@ -739,6 +790,16 @@ def run_svnauthz_validate(*varargs):
stderr as list of lines (including line terminators)."""
return run_command(svnauthz_validate_binary, 1, False, *varargs)
+def run_svnfsfs(*varargs):
+ """Run svnfsfs with VARARGS, returns exit code as int; stdout, stderr
+ as list of lines (including line terminators)."""
+ return run_command(svnfsfs_binary, 1, False, *varargs)
+
+def run_lock_helper(repo, path, user, seconds):
+ """Run lock-helper to lock path in repo by username for seconds"""
+
+ return run_command(lock_helper_binary, 1, False, repo, path, user, seconds)
+
def run_entriesdump(path):
"""Run the entries-dump helper, returning a dict of Entry objects."""
# use spawn_process rather than run_command to avoid copying all the data
@@ -793,9 +854,11 @@ def run_atomic_ra_revprop_change(url, revision, propname, skel, want_error):
url, revision, propname, skel,
want_error and 1 or 0, default_config_dir)
-def run_wc_lock_tester(recursive, path):
+def run_wc_lock_tester(recursive, path, work_queue=False):
"Run the wc-lock obtainer tool, returning its exit code, stdout and stderr"
- if recursive:
+ if work_queue:
+ option = "-w"
+ elif recursive:
option = "-r"
else:
option = "-1"
@@ -874,47 +937,10 @@ def file_substitute(path, contents, new_contents):
fcontent = open(path, 'r').read().replace(contents, new_contents)
open(path, 'w').write(fcontent)
-# For creating blank new repositories
-def create_repos(path, minor_version = None):
- """Create a brand-new SVN repository at PATH. If PATH does not yet
- exist, create it."""
-
- if not os.path.exists(path):
- os.makedirs(path) # this creates all the intermediate dirs, if neccessary
-
- opts = ("--bdb-txn-nosync",)
- if not minor_version or minor_version > options.server_minor_version:
- minor_version = options.server_minor_version
- opts += ("--compatible-version=1.%d" % (minor_version),)
- if options.fs_type is not None:
- opts += ("--fs-type=" + options.fs_type,)
- exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False, "create",
- path, *opts)
-
- # Skip tests if we can't create the repository.
- if stderr:
- stderr_lines = 0
- not_using_fsfs_backend = (options.fs_type != "fsfs")
- backend_deprecation_warning = False
- for line in stderr:
- stderr_lines += 1
- if line.find('Unknown FS type') != -1:
- raise Skip
- if not_using_fsfs_backend:
- if 0 < line.find('repository back-end is deprecated, consider using'):
- backend_deprecation_warning = True
-
- # Creating BDB repositories will cause svnadmin to print a warning
- # which should be ignored.
- if (stderr_lines == 1
- and not_using_fsfs_backend
- and backend_deprecation_warning):
- pass
- else:
- # If the FS type is known and we noticed more than just the
- # BDB-specific warning, assume the repos couldn't be created
- # (e.g. due to a missing 'svnadmin' binary).
- raise SVNRepositoryCreateFailure("".join(stderr).rstrip())
+# For setting up authz and hooks in existing repos
+def _post_create_repos(path, minor_version = None):
+ """Set default access right configurations for svnserve and mod_dav
+ as well as hooks etc. in the SVN repository at PATH."""
# Require authentication to write to the repos, for ra_svn testing.
file_write(get_svnserve_conf_file_path(path),
@@ -927,14 +953,26 @@ def create_repos(path, minor_version = None):
# This actually creates TWO [users] sections in the file (one of them is
# uncommented in `svnadmin create`'s template), so we exercise the .ini
# files reading code's handling of duplicates, too. :-)
- file_append(os.path.join(path, "conf", "passwd"),
- "[users]\njrandom = rayjandom\njconstant = rayjandom\n");
+ users = ("[users]\n"
+ "jrandom = rayjandom\n"
+ "jconstant = rayjandom\n")
+ if tests_verify_dump_load_cross_check():
+ # Insert a user for the dump/load cross-check.
+ users += (crosscheck_username + " = " + crosscheck_password + "\n")
+ file_append(os.path.join(path, "conf", "passwd"), users)
if options.fs_type is None or options.fs_type == 'fsfs':
# fsfs.conf file
if options.config_file is not None and \
(not minor_version or minor_version >= 6):
- shutil.copy(options.config_file, get_fsfs_conf_file_path(path))
+ config_file = open(options.config_file, 'r')
+ fsfsconf = open(get_fsfs_conf_file_path(path), 'w')
+ for line in config_file.readlines():
+ fsfsconf.write(line)
+ if options.memcached_server and line == '[memcached-servers]\n':
+ fsfsconf.write('key = %s\n' % options.memcached_server)
+ config_file.close()
+ fsfsconf.close()
# format file
if options.fsfs_sharding is not None:
@@ -964,7 +1002,7 @@ def create_repos(path, minor_version = None):
# post-commit
# Note that some tests (currently only commit_tests) create their own
# post-commit hooks, which would override this one. :-(
- if options.fsfs_packing:
+ if options.fsfs_packing and minor_version >=6:
# some tests chdir.
abs_path = os.path.abspath(path)
create_python_hook_script(get_post_commit_hook_path(abs_path),
@@ -977,6 +1015,73 @@ def create_repos(path, minor_version = None):
# make the repos world-writeable, for mod_dav_svn's sake.
chmod_tree(path, 0666, 0666)
+def _unpack_precooked_repos(path, template):
+ testdir = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
+ repozip = os.path.join(os.path.dirname(testdir), "templates", template)
+ zipfile.ZipFile(repozip, 'r').extractall(path)
+
+# For creating new, pre-cooked greek repositories
+def unpack_greek_repos(path):
+ template = "greek-fsfs-v%d.zip" % options.fsfs_version
+ _unpack_precooked_repos(path, template)
+ _post_create_repos(path, options.server_minor_version)
+
+# For creating blank new repositories
+def create_repos(path, minor_version = None):
+ """Create a brand-new SVN repository at PATH. If PATH does not yet
+ exist, create it."""
+
+ if not os.path.exists(path):
+ os.makedirs(path) # this creates all the intermediate dirs, if necessary
+
+ if options.fsfs_version is None:
+ if options.fs_type == "bdb":
+ opts = ("--bdb-txn-nosync",)
+ else:
+ opts = ()
+ if minor_version is None or minor_version > options.server_minor_version:
+ minor_version = options.server_minor_version
+ opts += ("--compatible-version=1.%d" % (minor_version),)
+ if options.fs_type is not None:
+ opts += ("--fs-type=" + options.fs_type,)
+ exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False,
+ "create", path, *opts)
+ else:
+ # Copy a pre-cooked FSFS repository
+ assert options.fs_type == "fsfs"
+ template = "empty-fsfs-v%d.zip" % options.fsfs_version
+ _unpack_precooked_repos(path, template)
+ exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False,
+ "setuuid", path)
+
+ # Skip tests if we can't create the repository.
+ if stderr:
+ stderr_lines = 0
+ not_using_fsfs_backend = (options.fs_type != "fsfs")
+ backend_deprecation_warning = False
+ for line in stderr:
+ stderr_lines += 1
+ if line.find('Unknown FS type') != -1:
+ raise Skip
+ if not_using_fsfs_backend:
+ if 0 < line.find('repository back-end is deprecated, consider using'):
+ backend_deprecation_warning = True
+
+ # Creating BDB repositories will cause svnadmin to print a warning
+ # which should be ignored.
+ if (stderr_lines == 1
+ and not_using_fsfs_backend
+ and backend_deprecation_warning):
+ pass
+ else:
+ # If the FS type is known and we noticed more than just the
+ # BDB-specific warning, assume the repos couldn't be created
+ # (e.g. due to a missing 'svnadmin' binary).
+ raise SVNRepositoryCreateFailure("".join(stderr).rstrip())
+
+ # Configure the new repository.
+ _post_create_repos(path, minor_version)
+
# For copying a repository
def copy_repos(src_path, dst_path, head_revision, ignore_uuid = 1,
minor_version = None):
@@ -1120,7 +1225,7 @@ def write_restrictive_svnserve_conf_with_groups(repo_dir,
# parallel execution at the bottom like so
# if __name__ == '__main__':
# svntest.main.run_tests(test_list, serial_only = True)
-def write_authz_file(sbox, rules, sections=None):
+def write_authz_file(sbox, rules, sections=None, prefixed_rules=None):
"""Write an authz file to SBOX, appropriate for the RA method used,
with authorizations rules RULES mapping paths to strings containing
the rules. You can add sections SECTIONS (ex. groups, aliases...) with
@@ -1128,23 +1233,37 @@ an appropriate list of mappings.
"""
fp = open(sbox.authz_file, 'w')
- # When the sandbox repository is read only it's name will be different from
+ # When the sandbox repository is read only its name will be different from
# the repository name.
- repo_name = sbox.repo_dir
- while repo_name[-1] == '/':
- repo_name = repo_name[:-1]
- repo_name = os.path.basename(repo_name)
+ repo_name = os.path.basename(sbox.repo_dir.rstrip('/'))
if sbox.repo_url.startswith("http"):
- prefix = repo_name + ":"
+ default_prefix = repo_name + ":"
else:
- prefix = ""
+ default_prefix = ""
+
if sections:
for p, r in sections.items():
fp.write("[%s]\n%s\n" % (p, r))
- for p, r in rules.items():
- fp.write("[%s%s]\n%s\n" % (prefix, p, r))
+ if not prefixed_rules:
+ prefixed_rules = dict()
+
+ if rules:
+ for p, r in rules.items():
+ prefixed_rules[default_prefix + p] = r
+
+ for p, r in prefixed_rules.items():
+ fp.write("[%s]\n%s\n" % (p, r))
+ if tests_verify_dump_load_cross_check():
+ # Insert an ACE that lets the dump/load cross-check bypass
+ # authz restrictions.
+ fp.write(crosscheck_username + " = rw\n")
+
+ if tests_verify_dump_load_cross_check() and '/' not in prefixed_rules:
+ # We need a repository-root ACE for the dump/load cross-check
+ fp.write("[/]\n" + crosscheck_username + " = rw\n")
+
fp.close()
# See the warning about parallel test execution in write_authz_file
@@ -1295,6 +1414,12 @@ def make_log_msg():
# Functions which check the test configuration
# (useful for conditional XFails)
+def tests_use_prepacakaged_repository():
+ return options.fsfs_version is not None
+
+def tests_verify_dump_load_cross_check():
+ return options.dump_load_cross_check
+
def is_ra_type_dav():
return options.test_area_url.startswith('http')
@@ -1322,9 +1447,28 @@ def is_fs_type_fsfs():
# This assumes that fsfs is the default fs implementation.
return options.fs_type == 'fsfs' or options.fs_type is None
+def is_fs_type_fsx():
+ return options.fs_type == 'fsx'
+
def is_fs_type_bdb():
return options.fs_type == 'bdb'
+def is_fs_log_addressing():
+ return is_fs_type_fsx() or \
+ (is_fs_type_fsfs() and options.server_minor_version >= 9)
+
+def fs_has_rep_sharing():
+ return is_fs_type_fsx() or \
+ (is_fs_type_fsfs() and options.server_minor_version >= 6)
+
+def fs_has_pack():
+ return is_fs_type_fsx() or \
+ (is_fs_type_fsfs() and options.server_minor_version >= 6)
+
+def fs_has_unique_freeze():
+ return (is_fs_type_fsfs() and options.server_minor_version >= 9
+ or is_fs_type_bdb())
+
def is_os_windows():
return os.name == 'nt'
@@ -1367,6 +1511,9 @@ def server_enforces_date_syntax():
def server_has_atomic_revprop():
return options.server_minor_version >= 7
+def server_has_reverse_get_file_revs():
+ return options.server_minor_version >= 8
+
def is_plaintext_password_storage_disabled():
try:
predicate = re.compile("^WARNING: Plaintext password storage is enabled!")
@@ -1378,6 +1525,30 @@ def is_plaintext_password_storage_disabled():
return False
return True
+
+# https://issues.apache.org/bugzilla/show_bug.cgi?id=56480
+# https://issues.apache.org/bugzilla/show_bug.cgi?id=55397
+__mod_dav_url_quoting_broken_versions = frozenset([
+ '2.2.27',
+ '2.2.26',
+ '2.2.25',
+ '2.4.9',
+ '2.4.8',
+ '2.4.7',
+ '2.4.6',
+ '2.4.5',
+])
+def is_mod_dav_url_quoting_broken():
+ if is_ra_type_dav():
+ return (options.httpd_version in __mod_dav_url_quoting_broken_versions)
+ return None
+
+def is_httpd_authz_provider_enabled():
+ if is_ra_type_dav():
+ v = options.httpd_version.split('.')
+ return (v[0] == '2' and int(v[1]) >= 3) or int(v[0]) > 2
+ return None
+
######################################################################
@@ -1412,13 +1583,12 @@ class TestSpawningThread(threading.Thread):
args = []
args.append(str(index))
args.append('-c')
+ args.append('--set-log-level=%s' % logger.getEffectiveLevel())
# add some startup arguments from this process
if options.fs_type:
args.append('--fs-type=' + options.fs_type)
if options.test_area_url:
args.append('--url=' + options.test_area_url)
- if logger.getEffectiveLevel() <= logging.DEBUG:
- args.append('-v')
if options.cleanup:
args.append('--cleanup')
if options.enable_sasl:
@@ -1435,6 +1605,24 @@ class TestSpawningThread(threading.Thread):
args.append('--ssl-cert=' + options.ssl_cert)
if options.http_proxy:
args.append('--http-proxy=' + options.http_proxy)
+ if options.http_proxy_username:
+ args.append('--http-proxy-username=' + options.http_proxy_username)
+ if options.http_proxy_password:
+ args.append('--http-proxy-password=' + options.http_proxy_password)
+ if options.httpd_version:
+ args.append('--httpd-version=' + options.httpd_version)
+ if options.exclusive_wc_locks:
+ args.append('--exclusive-wc-locks')
+ if options.memcached_server:
+ args.append('--memcached-server=' + options.memcached_server)
+ if options.fsfs_sharding:
+ args.append('--fsfs-sharding=' + str(options.fsfs_sharding))
+ if options.fsfs_packing:
+ args.append('--fsfs-packing')
+ if options.fsfs_version:
+ args.append('--fsfs-version=' + str(options.fsfs_version))
+ if options.dump_load_cross_check:
+ args.append('--dump-load-cross-check')
result, stdout_lines, stderr_lines = spawn_process(command, 0, False, None,
*args)
@@ -1694,13 +1882,7 @@ def _internal_run_tests(test_list, testnums, parallel, srcdir, progress_func):
return exit_code
-def create_default_options():
- """Set the global options to the defaults, as provided by the argument
- parser."""
- _parse_options([])
-
-
-def _create_parser():
+def _create_parser(usage=None):
"""Return a parser for our test suite."""
def set_log_level(option, opt, value, parser, level=None):
if level:
@@ -1710,9 +1892,18 @@ def _create_parser():
# called from --set-log-level
logger.setLevel(getattr(logging, value, None) or int(value))
- # set up the parser
+ # Set up the parser.
+ # If you add new options, consider adding them in
+ #
+ # .../build/run_tests.py:main()
+ #
+ # and handling them in
+ #
+ # .../build/run_tests.py:TestHarness._init_py_tests()
+ #
_default_http_library = 'serf'
- usage = 'usage: %prog [options] [<test> ...]'
+ if usage is None:
+ usage = 'usage: %prog [options] [<test> ...]'
parser = optparse.OptionParser(usage=usage)
parser.add_option('-l', '--list', action='store_true', dest='list_tests',
help='Print test doc strings instead of running them')
@@ -1727,6 +1918,9 @@ def _create_parser():
parser.add_option('-p', '--parallel', action='store_const',
const=default_num_threads, dest='parallel',
help='Run the tests in parallel')
+ parser.add_option('--parallel-instances', action='store',
+ type='int', dest='parallel',
+ help='Run the given number of tests in parallel')
parser.add_option('-c', action='store_true', dest='is_child_process',
help='Flag if we are running this python test as a ' +
'child process')
@@ -1736,7 +1930,7 @@ def _create_parser():
parser.add_option('--url', action='store',
help='Base url to the repos (e.g. svn://localhost)')
parser.add_option('--fs-type', action='store',
- help='Subversion file system type (fsfs or bdb)')
+ help='Subversion file system type (fsfs, bdb or fsx)')
parser.add_option('--cleanup', action='store_true',
help='Whether to clean up')
parser.add_option('--enable-sasl', action='store_true',
@@ -1757,6 +1951,13 @@ def _create_parser():
help="Run 'svnadmin pack' automatically")
parser.add_option('--fsfs-sharding', action='store', type='int',
help='Default shard size (for fsfs)')
+ parser.add_option('--fsfs-version', type='int', action='store',
+ help='FSFS version (fsfs)')
+ parser.add_option('--dump-load-cross-check', action='store_true',
+ help="After every test, run a series of dump and load " +
+ "tests with svnadmin, svnrdump and svndumpfilter " +
+ " on the testcase repositories to cross-check " +
+ " dump file compatibility.")
parser.add_option('--config-file', action='store',
help="Configuration file for tests.")
parser.add_option('--set-log-level', action='callback', type='str',
@@ -1780,43 +1981,73 @@ def _create_parser():
help='Path to SSL server certificate.')
parser.add_option('--http-proxy', action='store',
help='Use the HTTP Proxy at hostname:port.')
+ parser.add_option('--http-proxy-username', action='store',
+ help='Username for the HTTP Proxy.')
+ parser.add_option('--http-proxy-password', action='store',
+ help='Password for the HTTP Proxy.')
+ parser.add_option('--httpd-version', action='store',
+ help='Assume HTTPD is this version.')
parser.add_option('--tools-bin', action='store', dest='tools_bin',
help='Use the svn tools installed in this path')
+ parser.add_option('--exclusive-wc-locks', action='store_true',
+ help='Use sqlite exclusive locking for working copies')
+ parser.add_option('--memcached-server', action='store',
+ help='Use memcached server at specified URL (FSFS only)')
# most of the defaults are None, but some are other values, set them here
parser.set_defaults(
server_minor_version=SVN_VER_MINOR,
url=file_scheme_prefix + \
- urllib.pathname2url(os.path.abspath(os.getcwd())),
+ svntest.wc.svn_uri_quote(
+ os.path.abspath(
+ os.getcwd()).replace(os.path.sep, '/')),
http_library=_default_http_library)
return parser
-def _parse_options(arglist=sys.argv[1:]):
+def parse_options(arglist=sys.argv[1:], usage=None):
"""Parse the arguments in arg_list, and set the global options object with
the results"""
global options
- parser = _create_parser()
+ parser = _create_parser(usage)
(options, args) = parser.parse_args(arglist)
- # some sanity checking
+ # Normalize url to have no trailing slash
+ if options.url:
+ if options.url[-1:] == '/':
+ options.test_area_url = options.url[:-1]
+ else:
+ options.test_area_url = options.url
+
+ # Some sanity checking
if options.fsfs_packing and not options.fsfs_sharding:
parser.error("--fsfs-packing requires --fsfs-sharding")
- # If you change the below condition then change
- # ../../../../build/run_tests.py too.
if options.server_minor_version not in range(3, SVN_VER_MINOR+1):
parser.error("test harness only supports server minor versions 3-%d"
% SVN_VER_MINOR)
- if options.url:
- if options.url[-1:] == '/': # Normalize url to have no trailing slash
- options.test_area_url = options.url[:-1]
- else:
- options.test_area_url = options.url
+ # Make sure the server-minor-version matches the fsfs-version parameter.
+ if options.fsfs_version:
+ if options.fsfs_version == 6:
+ if options.server_minor_version \
+ and options.server_minor_version != 8 \
+ and options.server_minor_version != SVN_VER_MINOR:
+ parser.error("--fsfs-version=6 requires --server-minor-version=8")
+ options.server_minor_version = 8
+ if options.fsfs_version == 4:
+ if options.server_minor_version \
+ and options.server_minor_version != 7 \
+ and options.server_minor_version != SVN_VER_MINOR:
+ parser.error("--fsfs-version=4 requires --server-minor-version=7")
+ options.server_minor_version = 7
+ pass
+ # ### Add more tweaks here if and when we support pre-cooked versions
+ # ### of FSFS repositories.
+ pass
return (parser, args)
@@ -1918,6 +2149,7 @@ def execute_tests(test_list, serial_only = False, test_name = None,
global svn_binary
global svnadmin_binary
global svnlook_binary
+ global svnrdump_binary
global svnsync_binary
global svndumpfilter_binary
global svnversion_binary
@@ -1946,7 +2178,7 @@ def execute_tests(test_list, serial_only = False, test_name = None,
if not options:
# Override which tests to run from the commandline
- (parser, args) = _parse_options()
+ (parser, args) = parse_options()
test_selection = args
else:
parser = _create_parser()
@@ -2003,7 +2235,7 @@ def execute_tests(test_list, serial_only = False, test_name = None,
# it to a number if possible
for testnum in list(range(1, len(test_list))):
test_case = TestRunner(test_list[testnum], testnum)
- if test_case.get_function_name() == str(arg):
+ if test_case.get_function_name() == str(arg).rstrip(','):
testnums.append(testnum)
appended = True
break
@@ -2016,7 +2248,9 @@ def execute_tests(test_list, serial_only = False, test_name = None,
# Calculate pristine_greek_repos_url from test_area_url.
pristine_greek_repos_url = options.test_area_url + '/' + \
- urllib.pathname2url(pristine_greek_repos_dir)
+ svntest.wc.svn_uri_quote(
+ pristine_greek_repos_dir.replace(
+ os.path.sep, '/'))
if options.use_jsvn:
if options.svn_bin is None:
@@ -2035,6 +2269,7 @@ def execute_tests(test_list, serial_only = False, test_name = None,
svn_binary = os.path.join(options.svn_bin, 'svn' + _exe)
svnadmin_binary = os.path.join(options.svn_bin, 'svnadmin' + _exe)
svnlook_binary = os.path.join(options.svn_bin, 'svnlook' + _exe)
+ svnrdump_binary = os.path.join(options.svn_bin, 'svnrdump' + _exe)
svnsync_binary = os.path.join(options.svn_bin, 'svnsync' + _exe)
svndumpfilter_binary = os.path.join(options.svn_bin,
'svndumpfilter' + _exe)
@@ -2093,29 +2328,38 @@ def execute_tests(test_list, serial_only = False, test_name = None,
# We are simply listing the tests so always exit with success.
return 0
- # don't run tests in parallel when the tests don't support it or there
- # are only a few tests to run.
+ # don't run tests in parallel when the tests don't support it or
+ # there are only a few tests to run.
+ options_parallel = options.parallel
if serial_only or len(testnums) < 2:
options.parallel = 0
- if not options.is_child_process:
- # Build out the default configuration directory
- create_config_dir(default_config_dir,
- ssl_cert=options.ssl_cert,
- ssl_url=options.test_area_url,
- http_proxy=options.http_proxy)
-
- # Setup the pristine repository
- svntest.actions.setup_pristine_greek_repository()
-
- # Run the tests.
- exit_code = _internal_run_tests(test_list, testnums, options.parallel,
- options.srcdir, progress_func)
+ try:
+ if not options.is_child_process:
+ # Build out the default configuration directory
+ create_config_dir(default_config_dir,
+ ssl_cert=options.ssl_cert,
+ ssl_url=options.test_area_url,
+ http_proxy=options.http_proxy,
+ exclusive_wc_locks=options.exclusive_wc_locks)
+
+ # Setup the pristine repository
+ svntest.actions.setup_pristine_greek_repository()
+
+ # Run the tests.
+ exit_code = _internal_run_tests(test_list, testnums, options.parallel,
+ options.srcdir, progress_func)
+ finally:
+ options.parallel = options_parallel
# Remove all scratchwork: the 'pristine' repository, greek tree, etc.
# This ensures that an 'import' will happen the next time we run.
if not options.is_child_process and not options.keep_local_tmp:
- safe_rmtree(temp_dir, 1)
+ try:
+ safe_rmtree(temp_dir, 1)
+ except:
+ logger.error("ERROR: cleanup of '%s' directory failed." % temp_dir)
+ exit_code = 1
# Cleanup after ourselves.
svntest.sandbox.cleanup_deferred_test_paths()