Expand some doc strings in the test framework.
[svnrdump.git] / svntest / main.py
bloba9e1d147ecb5bffcf64ef17002d8f2fcadfdb20a
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
23 # under the License.
24 ######################################################################
26 import sys # for argv[]
27 import os
28 import shutil # for rmtree()
29 import re
30 import stat # for ST_MODE
31 import subprocess
32 import copy # for deepcopy()
33 import time # for time()
34 import traceback # for print_exc()
35 import threading
36 try:
37 # Python >=3.0
38 import queue
39 from urllib.parse import quote as urllib_parse_quote
40 from urllib.parse import unquote as urllib_parse_unquote
41 except ImportError:
42 # Python <3.0
43 import Queue as queue
44 from urllib import quote as urllib_parse_quote
45 from urllib import unquote as urllib_parse_unquote
47 import getopt
48 try:
49 my_getopt = getopt.gnu_getopt
50 except AttributeError:
51 my_getopt = getopt.getopt
53 import svntest
54 from svntest import Failure
55 from svntest import Skip
58 ######################################################################
60 # HOW TO USE THIS MODULE:
62 # Write a new python script that
64 # 1) imports this 'svntest' package
66 # 2) contains a number of related 'test' routines. (Each test
67 # routine should take no arguments, and return None on success
68 # or throw a Failure exception on failure. Each test should
69 # also contain a short docstring.)
71 # 3) places all the tests into a list that begins with None.
73 # 4) calls svntest.main.client_test() on the list.
75 # Also, your tests will probably want to use some of the common
76 # routines in the 'Utilities' section below.
78 #####################################################################
79 # Global stuff
81 class SVNProcessTerminatedBySignal(Failure):
82 "Exception raised if a spawned process segfaulted, aborted, etc."
83 pass
85 class SVNLineUnequal(Failure):
86 "Exception raised if two lines are unequal"
87 pass
89 class SVNUnmatchedError(Failure):
90 "Exception raised if an expected error is not found"
91 pass
93 class SVNCommitFailure(Failure):
94 "Exception raised if a commit failed"
95 pass
97 class SVNRepositoryCopyFailure(Failure):
98 "Exception raised if unable to copy a repository"
99 pass
101 class SVNRepositoryCreateFailure(Failure):
102 "Exception raised if unable to create a repository"
103 pass
105 # Windows specifics
106 if sys.platform == 'win32':
107 windows = True
108 file_scheme_prefix = 'file:///'
109 _exe = '.exe'
110 _bat = '.bat'
111 os.environ['SVN_DBG_STACKTRACES_TO_STDERR'] = 'y'
112 else:
113 windows = False
114 file_scheme_prefix = 'file://'
115 _exe = ''
116 _bat = ''
118 # The location of our mock svneditor script.
119 if windows:
120 svneditor_script = os.path.join(sys.path[0], 'svneditor.bat')
121 else:
122 svneditor_script = os.path.join(sys.path[0], 'svneditor.py')
124 # Username and password used by the working copies
125 wc_author = 'jrandom'
126 wc_passwd = 'rayjandom'
128 # Username and password used by the working copies for "second user"
129 # scenarios
130 wc_author2 = 'jconstant' # use the same password as wc_author
132 # Set C locale for command line programs
133 os.environ['LC_ALL'] = 'C'
135 # This function mimics the Python 2.3 urllib function of the same name.
136 def pathname2url(path):
137 """Convert the pathname PATH from the local syntax for a path to the form
138 used in the path component of a URL. This does not produce a complete URL.
139 The return value will already be quoted using the quote() function."""
140 return urllib_parse_quote(path.replace('\\', '/'))
142 # This function mimics the Python 2.3 urllib function of the same name.
143 def url2pathname(path):
144 """Convert the path component PATH from an encoded URL to the local syntax
145 for a path. This does not accept a complete URL. This function uses
146 unquote() to decode PATH."""
147 return os.path.normpath(urllib_parse_unquote(path))
149 ######################################################################
150 # Global variables set during option parsing. These should not be used
151 # until the variable command_line_parsed has been set to True, as is
152 # done in run_tests below.
153 command_line_parsed = False
155 # The locations of the svn, svnadmin and svnlook binaries, relative to
156 # the only scripts that import this file right now (they live in ../).
157 # Use --bin to override these defaults.
158 svn_binary = os.path.abspath('../../svn/svn' + _exe)
159 svnadmin_binary = os.path.abspath('../../svnadmin/svnadmin' + _exe)
160 svnlook_binary = os.path.abspath('../../svnlook/svnlook' + _exe)
161 svnsync_binary = os.path.abspath('../../svnsync/svnsync' + _exe)
162 svnversion_binary = os.path.abspath('../../svnversion/svnversion' + _exe)
163 svndumpfilter_binary = os.path.abspath('../../svndumpfilter/svndumpfilter' + \
164 _exe)
165 entriesdump_binary = os.path.abspath('entries-dump' + _exe)
167 # Global variable indicating if we want verbose output, that is,
168 # details of what commands each test does as it does them. This is
169 # incompatible with quiet_mode.
170 verbose_mode = False
172 # Global variable indicating if we want quiet output, that is, don't
173 # show PASS, XFAIL, or SKIP notices, but do show FAIL and XPASS. This
174 # is incompatible with verbose_mode.
175 quiet_mode = False
177 # Global variable indicating if we want test data cleaned up after success
178 cleanup_mode = False
180 # Global variable indicating if svnserve should use Cyrus SASL
181 enable_sasl = False
183 # Global variable indicating that SVNKit binaries should be used
184 use_jsvn = False
186 # Global variable indicating which DAV library to use if both are available
187 # ('neon', 'serf'). The default is neon for backward compatibility of the
188 # test suite.
189 preferred_http_library = 'neon'
191 # Global variable: Number of shards to use in FSFS
192 # 'None' means "use FSFS's default"
193 fsfs_sharding = None
195 # Global variable: automatically pack FSFS repositories after every commit
196 fsfs_packing = None
198 # Configuration file (copied into FSFS fsfs.conf).
199 config_file = None
201 # Global variable indicating what the minor version of the server
202 # tested against is (4 for 1.4.x, for example).
203 server_minor_version = 7
205 # Global variable indicating if this is a child process and no cleanup
206 # of global directories is needed.
207 is_child_process = False
209 # Global URL to testing area. Default to ra_local, current working dir.
210 test_area_url = file_scheme_prefix + pathname2url(os.path.abspath(os.getcwd()))
212 # Location to the pristine repository, will be calculated from test_area_url
213 # when we know what the user specified for --url.
214 pristine_url = None
216 # Global variable indicating the FS type for repository creations.
217 fs_type = None
219 # End of command-line-set global variables.
220 ######################################################################
222 # All temporary repositories and working copies are created underneath
223 # this dir, so there's one point at which to mount, e.g., a ramdisk.
224 work_dir = "svn-test-work"
226 # Constant for the merge info property.
227 SVN_PROP_MERGEINFO = "svn:mergeinfo"
229 # Where we want all the repositories and working copies to live.
230 # Each test will have its own!
231 general_repo_dir = os.path.join(work_dir, "repositories")
232 general_wc_dir = os.path.join(work_dir, "working_copies")
234 # temp directory in which we will create our 'pristine' local
235 # repository and other scratch data. This should be removed when we
236 # quit and when we startup.
237 temp_dir = os.path.join(work_dir, 'local_tmp')
239 # (derivatives of the tmp dir.)
240 pristine_dir = os.path.join(temp_dir, "repos")
241 greek_dump_dir = os.path.join(temp_dir, "greekfiles")
242 default_config_dir = os.path.abspath(os.path.join(temp_dir, "config"))
245 # Our pristine greek-tree state.
247 # If a test wishes to create an "expected" working-copy tree, it should
248 # call main.greek_state.copy(). That method will return a copy of this
249 # State object which can then be edited.
251 _item = svntest.wc.StateItem
252 greek_state = svntest.wc.State('', {
253 'iota' : _item("This is the file 'iota'.\n"),
254 'A' : _item(),
255 'A/mu' : _item("This is the file 'mu'.\n"),
256 'A/B' : _item(),
257 'A/B/lambda' : _item("This is the file 'lambda'.\n"),
258 'A/B/E' : _item(),
259 'A/B/E/alpha' : _item("This is the file 'alpha'.\n"),
260 'A/B/E/beta' : _item("This is the file 'beta'.\n"),
261 'A/B/F' : _item(),
262 'A/C' : _item(),
263 'A/D' : _item(),
264 'A/D/gamma' : _item("This is the file 'gamma'.\n"),
265 'A/D/G' : _item(),
266 'A/D/G/pi' : _item("This is the file 'pi'.\n"),
267 'A/D/G/rho' : _item("This is the file 'rho'.\n"),
268 'A/D/G/tau' : _item("This is the file 'tau'.\n"),
269 'A/D/H' : _item(),
270 'A/D/H/chi' : _item("This is the file 'chi'.\n"),
271 'A/D/H/psi' : _item("This is the file 'psi'.\n"),
272 'A/D/H/omega' : _item("This is the file 'omega'.\n"),
276 ######################################################################
277 # Utilities shared by the tests
278 def wrap_ex(func):
279 "Wrap a function, catch, print and ignore exceptions"
280 def w(*args, **kwds):
281 try:
282 return func(*args, **kwds)
283 except Failure, ex:
284 if ex.__class__ != Failure or ex.args:
285 ex_args = str(ex)
286 if ex_args:
287 print('EXCEPTION: %s: %s' % (ex.__class__.__name__, ex_args))
288 else:
289 print('EXCEPTION: %s' % ex.__class__.__name__)
290 return w
292 def setup_development_mode():
293 "Wraps functions in module actions"
294 l = [ 'run_and_verify_svn',
295 'run_and_verify_svnversion',
296 'run_and_verify_load',
297 'run_and_verify_dump',
298 'run_and_verify_checkout',
299 'run_and_verify_export',
300 'run_and_verify_update',
301 'run_and_verify_merge',
302 'run_and_verify_switch',
303 'run_and_verify_commit',
304 'run_and_verify_unquiet_status',
305 'run_and_verify_status',
306 'run_and_verify_diff_summarize',
307 'run_and_verify_diff_summarize_xml',
308 'run_and_validate_lock']
310 for func in l:
311 setattr(svntest.actions, func, wrap_ex(getattr(svntest.actions, func)))
313 def get_admin_name():
314 "Return name of SVN administrative subdirectory."
316 if (windows or sys.platform == 'cygwin') \
317 and 'SVN_ASP_DOT_NET_HACK' in os.environ:
318 return '_svn'
319 else:
320 return '.svn'
322 def get_start_commit_hook_path(repo_dir):
323 "Return the path of the start-commit-hook conf file in REPO_DIR."
325 return os.path.join(repo_dir, "hooks", "start-commit")
328 def get_pre_commit_hook_path(repo_dir):
329 "Return the path of the pre-commit-hook conf file in REPO_DIR."
331 return os.path.join(repo_dir, "hooks", "pre-commit")
334 def get_post_commit_hook_path(repo_dir):
335 "Return the path of the post-commit-hook conf file in REPO_DIR."
337 return os.path.join(repo_dir, "hooks", "post-commit")
339 def get_pre_revprop_change_hook_path(repo_dir):
340 "Return the path of the pre-revprop-change hook script in REPO_DIR."
342 return os.path.join(repo_dir, "hooks", "pre-revprop-change")
344 def get_svnserve_conf_file_path(repo_dir):
345 "Return the path of the svnserve.conf file in REPO_DIR."
347 return os.path.join(repo_dir, "conf", "svnserve.conf")
349 def get_fsfs_conf_file_path(repo_dir):
350 "Return the path of the fsfs.conf file in REPO_DIR."
352 return os.path.join(repo_dir, "db", "fsfs.conf")
354 def get_fsfs_format_file_path(repo_dir):
355 "Return the path of the format file in REPO_DIR."
357 return os.path.join(repo_dir, "db", "format")
359 # Run any binary, logging the command line and return code
360 def run_command(command, error_expected, binary_mode=0, *varargs):
361 """Run COMMAND with VARARGS. Return exit code as int; stdout, stderr
362 as lists of lines (including line terminators). See run_command_stdin()
363 for details. If ERROR_EXPECTED is None, any stderr also will be printed."""
365 return run_command_stdin(command, error_expected, binary_mode,
366 None, *varargs)
368 # A regular expression that matches arguments that are trivially safe
369 # to pass on a command line without quoting on any supported operating
370 # system:
371 _safe_arg_re = re.compile(r'^[A-Za-z\d\.\_\/\-\:\@]+$')
373 def _quote_arg(arg):
374 """Quote ARG for a command line.
376 Simply surround every argument in double-quotes unless it contains
377 only universally harmless characters.
379 WARNING: This function cannot handle arbitrary command-line
380 arguments. It can easily be confused by shell metacharacters. A
381 perfect job would be difficult and OS-dependent (see, for example,
382 http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp).
383 In other words, this function is just good enough for what we need
384 here."""
386 arg = str(arg)
387 if _safe_arg_re.match(arg):
388 return arg
389 else:
390 if os.name != 'nt':
391 arg = arg.replace('$', '\$')
392 return '"%s"' % (arg,)
394 def open_pipe(command, stdin=None, stdout=None, stderr=None):
395 """Opens a subprocess.Popen pipe to COMMAND using STDIN,
396 STDOUT, and STDERR.
398 Returns (infile, outfile, errfile, waiter); waiter
399 should be passed to wait_on_pipe."""
400 command = [str(x) for x in command]
402 # On Windows subprocess.Popen() won't accept a Python script as
403 # a valid program to execute, rather it wants the Python executable.
404 if (sys.platform == 'win32') and (command[0].endswith('.py')):
405 command.insert(0, sys.executable)
407 # Quote only the arguments on Windows. Later versions of subprocess,
408 # 2.5.2+ confirmed, don't require this quoting, but versions < 2.4.3 do.
409 if sys.platform == 'win32':
410 args = command[1:]
411 args = ' '.join([_quote_arg(x) for x in args])
412 command = command[0] + ' ' + args
413 command_string = command
414 else:
415 command_string = ' '.join(command)
417 if not stdin:
418 stdin = subprocess.PIPE
419 if not stdout:
420 stdout = subprocess.PIPE
421 if not stderr:
422 stderr = subprocess.PIPE
424 p = subprocess.Popen(command,
425 stdin=stdin,
426 stdout=stdout,
427 stderr=stderr,
428 close_fds=not windows)
429 return p.stdin, p.stdout, p.stderr, (p, command_string)
431 def wait_on_pipe(waiter, binary_mode, stdin=None):
432 """WAITER is (KID, COMMAND_STRING). Wait for KID (opened with open_pipe)
433 to finish, dying if it does. If KID fails, create an error message
434 containing any stdout and stderr from the kid. Show COMMAND_STRING in
435 diagnostic messages. Normalize Windows line endings of stdout and stderr
436 if not BINARY_MODE. Return KID's exit code as int; stdout, stderr as
437 lists of lines (including line terminators)."""
438 if waiter is None:
439 return
441 kid, command_string = waiter
442 stdout, stderr = kid.communicate(stdin)
443 exit_code = kid.returncode
445 # Normalize Windows line endings if in text mode.
446 if windows and not binary_mode:
447 stdout = stdout.replace('\r\n', '\n')
448 stderr = stderr.replace('\r\n', '\n')
450 # Convert output strings to lists.
451 stdout_lines = stdout.splitlines(True)
452 stderr_lines = stderr.splitlines(True)
454 if exit_code < 0:
455 if not windows:
456 exit_signal = os.WTERMSIG(-exit_code)
457 else:
458 exit_signal = exit_code
460 if stdout_lines is not None:
461 sys.stdout.write("".join(stdout_lines))
462 sys.stdout.flush()
463 if stderr_lines is not None:
464 sys.stderr.write("".join(stderr_lines))
465 sys.stderr.flush()
466 if verbose_mode:
467 # show the whole path to make it easier to start a debugger
468 sys.stderr.write("CMD: %s terminated by signal %d\n"
469 % (command_string, exit_signal))
470 sys.stderr.flush()
471 raise SVNProcessTerminatedBySignal
472 else:
473 if exit_code and verbose_mode:
474 sys.stderr.write("CMD: %s exited with %d\n"
475 % (command_string, exit_code))
476 return stdout_lines, stderr_lines, exit_code
478 def spawn_process(command, binary_mode=0, stdin_lines=None, *varargs):
479 """Run any binary, supplying input text, logging the command line.
480 Normalize Windows line endings of stdout and stderr if not BINARY_MODE.
481 Return exit code as int; stdout, stderr as lists of lines (including
482 line terminators)."""
483 if stdin_lines and not isinstance(stdin_lines, list):
484 raise TypeError("stdin_lines should have list type")
486 # Log the command line
487 if verbose_mode and not command.endswith('.py'):
488 sys.stdout.write('CMD: %s %s\n' % (os.path.basename(command),
489 ' '.join([_quote_arg(x) for x in varargs])))
490 sys.stdout.flush()
492 infile, outfile, errfile, kid = open_pipe([command] + list(varargs))
494 if stdin_lines:
495 for x in stdin_lines:
496 infile.write(x)
498 stdout_lines, stderr_lines, exit_code = wait_on_pipe(kid, binary_mode)
499 infile.close()
501 outfile.close()
502 errfile.close()
504 return exit_code, stdout_lines, stderr_lines
506 def run_command_stdin(command, error_expected, binary_mode=0,
507 stdin_lines=None, *varargs):
508 """Run COMMAND with VARARGS; input STDIN_LINES (a list of strings
509 which should include newline characters) to program via stdin - this
510 should not be very large, as if the program outputs more than the OS
511 is willing to buffer, this will deadlock, with both Python and
512 COMMAND waiting to write to each other for ever.
513 Normalize Windows line endings of stdout and stderr if not BINARY_MODE.
514 Return exit code as int; stdout, stderr as lists of lines (including
515 line terminators).
516 If ERROR_EXPECTED is None, any stderr also will be printed."""
518 if verbose_mode:
519 start = time.time()
521 exit_code, stdout_lines, stderr_lines = spawn_process(command,
522 binary_mode,
523 stdin_lines,
524 *varargs)
526 if verbose_mode:
527 stop = time.time()
528 print('<TIME = %.6f>' % (stop - start))
529 for x in stdout_lines:
530 sys.stdout.write(x)
531 for x in stderr_lines:
532 sys.stdout.write(x)
534 if (not error_expected) and (stderr_lines):
535 if not verbose_mode:
536 for x in stderr_lines:
537 sys.stdout.write(x)
538 raise Failure
540 return exit_code, \
541 [line for line in stdout_lines if not line.startswith("DBG:")], \
542 stderr_lines
544 def create_config_dir(cfgdir, config_contents=None, server_contents=None):
545 "Create config directories and files"
547 # config file names
548 cfgfile_cfg = os.path.join(cfgdir, 'config')
549 cfgfile_srv = os.path.join(cfgdir, 'servers')
551 # create the directory
552 if not os.path.isdir(cfgdir):
553 os.makedirs(cfgdir)
555 # define default config file contents if none provided
556 if config_contents is None:
557 config_contents = """
559 [auth]
560 password-stores =
562 [miscellany]
563 interactive-conflicts = false
566 # define default server file contents if none provided
567 if server_contents is None:
568 http_library_str = ""
569 if preferred_http_library:
570 http_library_str = "http-library=%s" % (preferred_http_library)
571 server_contents = """
573 [global]
575 store-plaintext-passwords=yes
576 store-passwords=yes
577 """ % (http_library_str)
579 file_write(cfgfile_cfg, config_contents)
580 file_write(cfgfile_srv, server_contents)
582 def _with_config_dir(args):
583 if '--config-dir' in args:
584 return args
585 else:
586 return args + ('--config-dir', default_config_dir)
588 def _with_auth(args):
589 assert '--password' not in args
590 args = args + ('--password', wc_passwd,
591 '--no-auth-cache' )
592 if '--username' in args:
593 return args
594 else:
595 return args + ('--username', wc_author )
597 # For running subversion and returning the output
598 def run_svn(error_expected, *varargs):
599 """Run svn with VARARGS; return exit code as int; stdout, stderr as
600 lists of lines (including line terminators).
601 If ERROR_EXPECTED is None, any stderr also will be printed. If
602 you're just checking that something does/doesn't come out of
603 stdout/stderr, you might want to use actions.run_and_verify_svn()."""
604 return run_command(svn_binary, error_expected, 0,
605 *(_with_auth(_with_config_dir(varargs))))
607 # For running svnadmin. Ignores the output.
608 def run_svnadmin(*varargs):
609 """Run svnadmin with VARARGS, returns exit code as int; stdout, stderr as
610 list of lines (including line terminators)."""
611 return run_command(svnadmin_binary, 1, 0, *varargs)
613 # For running svnlook. Ignores the output.
614 def run_svnlook(*varargs):
615 """Run svnlook with VARARGS, returns exit code as int; stdout, stderr as
616 list of lines (including line terminators)."""
617 return run_command(svnlook_binary, 1, 0, *varargs)
619 def run_svnsync(*varargs):
620 """Run svnsync with VARARGS, returns exit code as int; stdout, stderr as
621 list of lines (including line terminators)."""
622 return run_command(svnsync_binary, 1, 0, *(_with_config_dir(varargs)))
624 def run_svnversion(*varargs):
625 """Run svnversion with VARARGS, returns exit code as int; stdout, stderr
626 as list of lines (including line terminators)."""
627 return run_command(svnversion_binary, 1, 0, *varargs)
629 def run_entriesdump(path):
630 """Run the entries-dump helper, returning a dict of Entry objects."""
631 # use spawn_process rather than run_command to avoid copying all the data
632 # to stdout in verbose mode.
633 exit_code, stdout_lines, stderr_lines = spawn_process(entriesdump_binary,
634 0, None, path)
635 if verbose_mode:
636 ### finish the CMD output
637 print
638 if exit_code or stderr_lines:
639 ### report on this? or continue to just skip it?
640 return None
642 class Entry(object):
643 pass
644 entries = { }
645 exec(''.join([line for line in stdout_lines if not line.startswith("DBG:")]))
646 return entries
649 # Chmod recursively on a whole subtree
650 def chmod_tree(path, mode, mask):
651 for dirpath, dirs, files in os.walk(path):
652 for name in dirs + files:
653 fullname = os.path.join(dirpath, name)
654 if not os.path.islink(fullname):
655 new_mode = (os.stat(fullname)[stat.ST_MODE] & ~mask) | mode
656 os.chmod(fullname, new_mode)
658 # For clearing away working copies
659 def safe_rmtree(dirname, retry=0):
660 "Remove the tree at DIRNAME, making it writable first"
661 def rmtree(dirname):
662 chmod_tree(dirname, 0666, 0666)
663 shutil.rmtree(dirname)
665 if not os.path.exists(dirname):
666 return
668 if retry:
669 for delay in (0.5, 1, 2, 4):
670 try:
671 rmtree(dirname)
672 break
673 except:
674 time.sleep(delay)
675 else:
676 rmtree(dirname)
677 else:
678 rmtree(dirname)
680 # For making local mods to files
681 def file_append(path, new_text):
682 "Append NEW_TEXT to file at PATH"
683 open(path, 'a').write(new_text)
685 # Append in binary mode
686 def file_append_binary(path, new_text):
687 "Append NEW_TEXT to file at PATH in binary mode"
688 open(path, 'ab').write(new_text)
690 # For creating new files, and making local mods to existing files.
691 def file_write(path, contents, mode='w'):
692 """Write the CONTENTS to the file at PATH, opening file using MODE,
693 which is (w)rite by default."""
694 open(path, mode).write(contents)
696 # For reading the contents of a file
697 def file_read(path, mode = 'r'):
698 """Return the contents of the file at PATH, opening file using MODE,
699 which is (r)ead by default."""
700 fp = open(path, mode)
701 contents = fp.read()
702 fp.close()
703 return 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 fp = open(path, 'r')
709 fcontent = fp.read()
710 fp.close()
711 fcontent = fcontent.replace(contents, new_contents)
712 fp = open(path, 'w')
713 fp.write(fcontent)
714 fp.close()
716 # For creating blank new repositories
717 def create_repos(path):
718 """Create a brand-new SVN repository at PATH. If PATH does not yet
719 exist, create it."""
721 if not os.path.exists(path):
722 os.makedirs(path) # this creates all the intermediate dirs, if neccessary
724 opts = ("--bdb-txn-nosync",)
725 if server_minor_version < 5:
726 opts += ("--pre-1.5-compatible",)
727 elif server_minor_version < 6:
728 opts += ("--pre-1.6-compatible",)
729 if fs_type is not None:
730 opts += ("--fs-type=" + fs_type,)
731 exit_code, stdout, stderr = run_command(svnadmin_binary, 1, 0, "create",
732 path, *opts)
734 # Skip tests if we can't create the repository.
735 if stderr:
736 for line in stderr:
737 if line.find('Unknown FS type') != -1:
738 raise Skip
739 # If the FS type is known, assume the repos couldn't be created
740 # (e.g. due to a missing 'svnadmin' binary).
741 raise SVNRepositoryCreateFailure("".join(stderr).rstrip())
743 # Allow unauthenticated users to write to the repos, for ra_svn testing.
744 file_write(get_svnserve_conf_file_path(path),
745 "[general]\nauth-access = write\n");
746 if enable_sasl:
747 file_append(get_svnserve_conf_file_path(path),
748 "realm = svntest\n[sasl]\nuse-sasl = true\n")
749 else:
750 file_append(get_svnserve_conf_file_path(path), "password-db = passwd\n")
751 file_append(os.path.join(path, "conf", "passwd"),
752 "[users]\njrandom = rayjandom\njconstant = rayjandom\n");
754 if fs_type is None or fs_type == 'fsfs':
755 # fsfs.conf file
756 if config_file is not None:
757 shutil.copy(config_file, get_fsfs_conf_file_path(path))
759 # format file
760 if fsfs_sharding is not None:
761 def transform_line(line):
762 if line.startswith('layout '):
763 if fsfs_sharding > 0:
764 line = 'layout sharded %d' % fsfs_sharding
765 else:
766 line = 'layout linear'
767 return line
769 # read it
770 format_file_path = get_fsfs_format_file_path(path)
771 contents = file_read(format_file_path, 'rb')
773 # tweak it
774 new_contents = "".join([transform_line(line) + "\n"
775 for line in contents.split("\n")])
776 if new_contents[-1] == "\n":
777 # we don't currently allow empty lines (\n\n) in the format file.
778 new_contents = new_contents[:-1]
780 # replace it
781 os.chmod(format_file_path, 0666)
782 file_write(format_file_path, new_contents, 'wb')
784 # post-commit
785 # Note that some tests (currently only commit_tests) create their own
786 # post-commit hooks, which would override this one. :-(
787 if fsfs_packing:
788 # some tests chdir.
789 abs_path = os.path.abspath(path)
790 create_python_hook_script(get_post_commit_hook_path(abs_path),
791 "import subprocess\n"
792 "import sys\n"
793 "command = %s\n"
794 "sys.exit(subprocess.Popen(command).wait())\n"
795 % repr([svnadmin_binary, 'pack', abs_path]))
797 # make the repos world-writeable, for mod_dav_svn's sake.
798 chmod_tree(path, 0666, 0666)
800 # For copying a repository
801 def copy_repos(src_path, dst_path, head_revision, ignore_uuid = 1):
802 "Copy the repository SRC_PATH, with head revision HEAD_REVISION, to DST_PATH"
804 # Do an svnadmin dump|svnadmin load cycle. Print a fake pipe command so that
805 # the displayed CMDs can be run by hand
806 os.environ['SVN_DBG_QUIET'] = 'y'
807 create_repos(dst_path)
808 dump_args = ['dump', src_path]
809 load_args = ['load', dst_path]
811 if ignore_uuid:
812 load_args = load_args + ['--ignore-uuid']
813 if verbose_mode:
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)))
817 sys.stdout.flush()
818 start = time.time()
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
826 stop = time.time()
827 if verbose_mode:
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)
833 dump_in.close()
834 dump_out.close()
835 dump_err.close()
836 #load_in is dump_out so it's already closed.
837 load_out.close()
838 load_err.close()
840 del os.environ['SVN_DBG_QUIET']
842 dump_re = re.compile(r'^\* Dumped revision (\d+)\.\r?$')
843 expect_revision = 0
844 for dump_line in dump_stderr:
845 match = dump_re.match(dump_line)
846 if not match or match.group(1) != str(expect_revision):
847 print('ERROR: dump failed: %s' % dump_line.strip())
848 raise SVNRepositoryCopyFailure
849 expect_revision += 1
850 if expect_revision != head_revision + 1:
851 print('ERROR: dump failed; did not see revision %s' % head_revision)
852 raise SVNRepositoryCopyFailure
854 load_re = re.compile(r'^------- Committed revision (\d+) >>>\r?$')
855 expect_revision = 1
856 for load_line in load_stdout:
857 match = load_re.match(load_line)
858 if match:
859 if match.group(1) != str(expect_revision):
860 print('ERROR: load failed: %s' % load_line.strip())
861 raise SVNRepositoryCopyFailure
862 expect_revision += 1
863 if expect_revision != head_revision + 1:
864 print('ERROR: load failed; did not see revision %s' % head_revision)
865 raise SVNRepositoryCopyFailure
868 def canonicalize_url(input):
869 "Canonicalize the url, if the scheme is unknown, returns intact input"
871 m = re.match(r"^((file://)|((svn|svn\+ssh|http|https)(://)))", input)
872 if m:
873 scheme = m.group(1)
874 return scheme + re.sub(r'//*', '/', input[len(scheme):])
875 else:
876 return input
879 def create_python_hook_script (hook_path, hook_script_code):
880 """Create a Python hook script at HOOK_PATH with the specified
881 HOOK_SCRIPT_CODE."""
883 if windows:
884 # Use an absolute path since the working directory is not guaranteed
885 hook_path = os.path.abspath(hook_path)
886 # Fill the python file.
887 file_write ("%s.py" % hook_path, hook_script_code)
888 # Fill the batch wrapper file.
889 file_append ("%s.bat" % hook_path,
890 "@\"%s\" %s.py %%*\n" % (sys.executable, hook_path))
891 else:
892 # For all other platforms
893 file_write (hook_path, "#!%s\n%s" % (sys.executable, hook_script_code))
894 os.chmod (hook_path, 0755)
896 def write_restrictive_svnserve_conf(repo_dir, anon_access="none"):
897 "Create a restrictive authz file ( no anynomous access )."
899 fp = open(get_svnserve_conf_file_path(repo_dir), 'w')
900 fp.write("[general]\nanon-access = %s\nauth-access = write\n"
901 "authz-db = authz\n" % anon_access)
902 if enable_sasl == 1:
903 fp.write("realm = svntest\n[sasl]\nuse-sasl = true\n");
904 else:
905 fp.write("password-db = passwd\n")
906 fp.close()
908 # Warning: because mod_dav_svn uses one shared authz file for all
909 # repositories, you *cannot* use write_authz_file in any test that
910 # might be run in parallel.
912 # write_authz_file can *only* be used in test suites which disable
913 # parallel execution at the bottom like so
914 # if __name__ == '__main__':
915 # svntest.main.run_tests(test_list, serial_only = True)
916 def write_authz_file(sbox, rules, sections=None):
917 """Write an authz file to SBOX, appropriate for the RA method used,
918 with authorizations rules RULES mapping paths to strings containing
919 the rules. You can add sections SECTIONS (ex. groups, aliases...) with
920 an appropriate list of mappings.
922 fp = open(sbox.authz_file, 'w')
924 # When the sandbox repository is read only it's name will be different from
925 # the repository name.
926 repo_name = sbox.repo_dir
927 while repo_name[-1] == '/':
928 repo_name = repo_name[:-1]
929 repo_name = os.path.basename(repo_name)
931 if sbox.repo_url.startswith("http"):
932 prefix = repo_name + ":"
933 else:
934 prefix = ""
935 if sections:
936 for p, r in sections.items():
937 fp.write("[%s]\n%s\n" % (p, r))
939 for p, r in rules.items():
940 fp.write("[%s%s]\n%s\n" % (prefix, p, r))
941 fp.close()
943 def use_editor(func):
944 os.environ['SVN_EDITOR'] = svneditor_script
945 os.environ['SVN_MERGE'] = svneditor_script
946 os.environ['SVNTEST_EDITOR_FUNC'] = func
947 os.environ['SVN_TEST_PYTHON'] = sys.executable
949 def mergeinfo_notify_line(revstart, revend):
950 """Return an expected output line that describes the beginning of a
951 mergeinfo recording notification on revisions REVSTART through REVEND."""
952 if (revend is None):
953 if (revstart < 0):
954 revstart = abs(revstart)
955 return "--- Recording mergeinfo for reverse merge of r%ld .*:\n" \
956 % (revstart)
957 else:
958 return "--- Recording mergeinfo for merge of r%ld .*:\n" % (revstart)
959 elif (revstart < revend):
960 return "--- Recording mergeinfo for merge of r%ld through r%ld .*:\n" \
961 % (revstart, revend)
962 else:
963 return "--- Recording mergeinfo for reverse merge of r%ld through " \
964 "r%ld .*:\n" % (revstart, revend)
966 def merge_notify_line(revstart=None, revend=None, same_URL=True,
967 foreign=False):
968 """Return an expected output line that describes the beginning of a
969 merge operation on revisions REVSTART through REVEND. Omit both
970 REVSTART and REVEND for the case where the left and right sides of
971 the merge are from different URLs."""
972 from_foreign_phrase = foreign and "\(from foreign repository\) " or ""
973 if not same_URL:
974 return "--- Merging differences between %srepository URLs into '.+':\n" \
975 % (foreign and "foreign " or "")
976 if revend is None:
977 if revstart is None:
978 # The left and right sides of the merge are from different URLs.
979 return "--- Merging differences between %srepository URLs into '.+':\n" \
980 % (foreign and "foreign " or "")
981 elif revstart < 0:
982 return "--- Reverse-merging %sr%ld into '.+':\n" \
983 % (from_foreign_phrase, abs(revstart))
984 else:
985 return "--- Merging %sr%ld into '.+':\n" \
986 % (from_foreign_phrase, revstart)
987 else:
988 if revstart > revend:
989 return "--- Reverse-merging %sr%ld through r%ld into '.+':\n" \
990 % (from_foreign_phrase, revstart, revend)
991 else:
992 return "--- Merging %sr%ld through r%ld into '.+':\n" \
993 % (from_foreign_phrase, revstart, revend)
996 ######################################################################
997 # Functions which check the test configuration
998 # (useful for conditional XFails)
1000 def _check_command_line_parsed():
1001 """Raise an exception if the command line has not yet been parsed."""
1002 if not command_line_parsed:
1003 raise Failure("Condition cannot be tested until command line is parsed")
1005 def is_ra_type_dav():
1006 _check_command_line_parsed()
1007 return test_area_url.startswith('http')
1009 def is_ra_type_dav_neon():
1010 """Return True iff running tests over RA-Neon.
1011 CAUTION: Result is only valid if svn was built to support both."""
1012 _check_command_line_parsed()
1013 return test_area_url.startswith('http') and \
1014 (preferred_http_library == "neon")
1016 def is_ra_type_dav_serf():
1017 """Return True iff running tests over RA-Serf.
1018 CAUTION: Result is only valid if svn was built to support both."""
1019 _check_command_line_parsed()
1020 return test_area_url.startswith('http') and \
1021 (preferred_http_library == "serf")
1023 def is_ra_type_svn():
1024 """Return True iff running tests over RA-svn."""
1025 _check_command_line_parsed()
1026 return test_area_url.startswith('svn')
1028 def is_ra_type_file():
1029 """Return True iff running tests over RA-local."""
1030 _check_command_line_parsed()
1031 return test_area_url.startswith('file')
1033 def is_fs_type_fsfs():
1034 _check_command_line_parsed()
1035 # This assumes that fsfs is the default fs implementation.
1036 return fs_type == 'fsfs' or fs_type is None
1038 def is_os_windows():
1039 return os.name == 'nt'
1041 def is_posix_os():
1042 return os.name == 'posix'
1044 def is_os_darwin():
1045 return sys.platform == 'darwin'
1047 def is_fs_case_insensitive():
1048 return (is_os_darwin() or is_os_windows())
1050 def server_has_mergeinfo():
1051 _check_command_line_parsed()
1052 return server_minor_version >= 5
1054 def server_has_revprop_commit():
1055 _check_command_line_parsed()
1056 return server_minor_version >= 5
1058 def server_sends_copyfrom_on_update():
1059 _check_command_line_parsed()
1060 return server_minor_version >= 5
1062 def server_authz_has_aliases():
1063 _check_command_line_parsed()
1064 return server_minor_version >= 5
1066 def server_gets_client_capabilities():
1067 _check_command_line_parsed()
1068 return server_minor_version >= 5
1070 def server_has_partial_replay():
1071 _check_command_line_parsed()
1072 return server_minor_version >= 5
1074 def server_enforces_date_syntax():
1075 _check_command_line_parsed()
1076 return server_minor_version >= 5
1078 ######################################################################
1081 class TestSpawningThread(threading.Thread):
1082 """A thread that runs test cases in their own processes.
1083 Receives test numbers to run from the queue, and saves results into
1084 the results field."""
1085 def __init__(self, queue):
1086 threading.Thread.__init__(self)
1087 self.queue = queue
1088 self.results = []
1090 def run(self):
1091 while True:
1092 try:
1093 next_index = self.queue.get_nowait()
1094 except queue.Empty:
1095 return
1097 self.run_one(next_index)
1099 def run_one(self, index):
1100 command = sys.argv[0]
1102 args = []
1103 args.append(str(index))
1104 args.append('-c')
1105 # add some startup arguments from this process
1106 if fs_type:
1107 args.append('--fs-type=' + fs_type)
1108 if test_area_url:
1109 args.append('--url=' + test_area_url)
1110 if verbose_mode:
1111 args.append('-v')
1112 if cleanup_mode:
1113 args.append('--cleanup')
1114 if enable_sasl:
1115 args.append('--enable-sasl')
1116 if preferred_http_library:
1117 args.append('--http-library=' + preferred_http_library)
1118 if server_minor_version:
1119 args.append('--server-minor-version=' + str(server_minor_version))
1121 result, stdout_lines, stderr_lines = spawn_process(command, 1, None, *args)
1122 self.results.append((index, result, stdout_lines, stderr_lines))
1124 if result != 1:
1125 sys.stdout.write('.')
1126 else:
1127 sys.stdout.write('F')
1129 sys.stdout.flush()
1131 class TestRunner:
1132 """Encapsulate a single test case (predicate), including logic for
1133 runing the test and test list output."""
1135 def __init__(self, func, index):
1136 self.pred = svntest.testcase.create_test_case(func)
1137 self.index = index
1139 def list(self):
1140 if verbose_mode and self.pred.inprogress:
1141 print(" %2d %-5s %s [[%s]]" % (self.index,
1142 self.pred.list_mode(),
1143 self.pred.description,
1144 self.pred.inprogress))
1145 else:
1146 print(" %2d %-5s %s" % (self.index,
1147 self.pred.list_mode(),
1148 self.pred.description))
1149 sys.stdout.flush()
1151 def get_function_name(self):
1152 return self.pred.get_function_name()
1154 def _print_name(self, prefix):
1155 if self.pred.inprogress:
1156 print("%s %s %s: %s [[WIMP: %s]]" % (prefix,
1157 os.path.basename(sys.argv[0]),
1158 str(self.index),
1159 self.pred.description,
1160 self.pred.inprogress))
1161 else:
1162 print("%s %s %s: %s" % (prefix,
1163 os.path.basename(sys.argv[0]),
1164 str(self.index),
1165 self.pred.description))
1166 sys.stdout.flush()
1168 def run(self):
1169 """Run self.pred and return the result. The return value is
1170 - 0 if the test was successful
1171 - 1 if it errored in a way that indicates test failure
1172 - 2 if the test skipped
1174 sbox_name = self.pred.get_sandbox_name()
1175 if sbox_name:
1176 sandbox = svntest.sandbox.Sandbox(sbox_name, self.index)
1177 else:
1178 sandbox = None
1180 # Explicitly set this so that commands that commit but don't supply a
1181 # log message will fail rather than invoke an editor.
1182 # Tests that want to use an editor should invoke svntest.main.use_editor.
1183 os.environ['SVN_EDITOR'] = ''
1184 os.environ['SVNTEST_EDITOR_FUNC'] = ''
1186 if use_jsvn:
1187 # Set this SVNKit specific variable to the current test (test name plus
1188 # its index) being run so that SVNKit daemon could use this test name
1189 # for its separate log file
1190 os.environ['SVN_CURRENT_TEST'] = os.path.basename(sys.argv[0]) + "_" + \
1191 str(self.index)
1193 svntest.actions.no_sleep_for_timestamps()
1195 saved_dir = os.getcwd()
1196 try:
1197 rc = self.pred.run(sandbox)
1198 if rc is not None:
1199 self._print_name('STYLE ERROR in')
1200 print('Test driver returned a status code.')
1201 sys.exit(255)
1202 result = svntest.testcase.RESULT_OK
1203 except Skip, ex:
1204 result = svntest.testcase.RESULT_SKIP
1205 except Failure, ex:
1206 result = svntest.testcase.RESULT_FAIL
1207 # We captured Failure and its subclasses. We don't want to print
1208 # anything for plain old Failure since that just indicates test
1209 # failure, rather than relevant information. However, if there
1210 # *is* information in the exception's arguments, then print it.
1211 if ex.__class__ != Failure or ex.args:
1212 ex_args = str(ex)
1213 if ex_args:
1214 print('EXCEPTION: %s: %s' % (ex.__class__.__name__, ex_args))
1215 else:
1216 print('EXCEPTION: %s' % ex.__class__.__name__)
1217 traceback.print_exc(file=sys.stdout)
1218 sys.stdout.flush()
1219 except KeyboardInterrupt:
1220 print('Interrupted')
1221 sys.exit(0)
1222 except SystemExit, ex:
1223 print('EXCEPTION: SystemExit(%d), skipping cleanup' % ex.code)
1224 self._print_name(ex.code and 'FAIL: ' or 'PASS: ')
1225 raise
1226 except:
1227 result = svntest.testcase.RESULT_FAIL
1228 print('UNEXPECTED EXCEPTION:')
1229 traceback.print_exc(file=sys.stdout)
1230 sys.stdout.flush()
1232 os.chdir(saved_dir)
1233 exit_code, result_text, result_benignity = self.pred.results(result)
1234 if not (quiet_mode and result_benignity):
1235 self._print_name(result_text)
1236 if sandbox is not None and exit_code != 1 and cleanup_mode:
1237 sandbox.cleanup_test_paths()
1238 return exit_code
1240 ######################################################################
1241 # Main testing functions
1243 # These two functions each take a TEST_LIST as input. The TEST_LIST
1244 # should be a list of test functions; each test function should take
1245 # no arguments and return a 0 on success, non-zero on failure.
1246 # Ideally, each test should also have a short, one-line docstring (so
1247 # it can be displayed by the 'list' command.)
1249 # Func to run one test in the list.
1250 def run_one_test(n, test_list, finished_tests = None):
1251 """Run the Nth client test in TEST_LIST, return the result.
1253 If we're running the tests in parallel spawn the test in a new process.
1256 if (n < 1) or (n > len(test_list) - 1):
1257 print("There is no test %s.\n" % n)
1258 return 1
1260 # Run the test.
1261 exit_code = TestRunner(test_list[n], n).run()
1262 return exit_code
1264 def _internal_run_tests(test_list, testnums, parallel):
1265 """Run the tests from TEST_LIST whose indices are listed in TESTNUMS.
1267 If we're running the tests in parallel spawn as much parallel processes
1268 as requested and gather the results in a temp. buffer when a child
1269 process is finished.
1272 exit_code = 0
1273 finished_tests = []
1274 tests_started = 0
1276 if not parallel:
1277 for testnum in testnums:
1278 if run_one_test(testnum, test_list) == 1:
1279 exit_code = 1
1280 else:
1281 number_queue = queue.Queue()
1282 for num in testnums:
1283 number_queue.put(num)
1285 threads = [ TestSpawningThread(number_queue) for i in range(parallel) ]
1286 for t in threads:
1287 t.start()
1289 for t in threads:
1290 t.join()
1292 # list of (index, result, stdout, stderr)
1293 results = []
1294 for t in threads:
1295 results += t.results
1296 results.sort()
1298 # terminate the line of dots
1299 print("")
1301 # all tests are finished, find out the result and print the logs.
1302 for (index, result, stdout_lines, stderr_lines) in results:
1303 if stdout_lines:
1304 for line in stdout_lines:
1305 sys.stdout.write(line)
1306 if stderr_lines:
1307 for line in stderr_lines:
1308 sys.stdout.write(line)
1309 if result == 1:
1310 exit_code = 1
1312 svntest.sandbox.cleanup_deferred_test_paths()
1313 return exit_code
1316 def usage():
1317 prog_name = os.path.basename(sys.argv[0])
1318 print("%s [--url] [--fs-type] [--verbose|--quiet] [--parallel] \\" %
1319 prog_name)
1320 print("%s [--enable-sasl] [--cleanup] [--bin] [<test> ...]"
1321 % (" " * len(prog_name)))
1322 print("%s " % (" " * len(prog_name)))
1323 print("%s [--list] [<test> ...]\n" % prog_name)
1324 print("Arguments:")
1325 print(" <test> The number of the test to run, or a range of test\n"
1326 " numbers, like 10:12 or 10-12. Multiple numbers and\n"
1327 " ranges are ok. If you supply none, all tests are run.\n"
1328 " You can also pass the name of a test function to run.\n")
1329 print("Options:")
1330 print(" --list Print test doc strings instead of running them")
1331 print(" --fs-type Subversion file system type (fsfs or bdb)")
1332 print(" --http-library Make svn use this DAV library (neon or serf) if\n"
1333 " it supports both, else assume it's using this one;\n"
1334 " the default is neon")
1335 print(" --url Base url to the repos (e.g. svn://localhost)")
1336 print(" --verbose Print binary command-lines (not with --quiet)")
1337 print(" --quiet Print only unexpected results (not with --verbose)")
1338 print(" --cleanup Whether to clean up")
1339 print(" --enable-sasl Whether to enable SASL authentication")
1340 print(" --parallel Run the tests in parallel")
1341 print(" --bin Use the svn binaries installed in this path")
1342 print(" --use-jsvn Use the jsvn (SVNKit based) binaries. Can be\n"
1343 " combined with --bin to point to a specific path")
1344 print(" --development Test development mode: provides more detailed test\n"
1345 " output and ignores all exceptions in the \n"
1346 " run_and_verify* functions. This option is only \n"
1347 " useful during test development!")
1348 print(" --server-minor-version Set the minor version for the server.\n"
1349 " Supports version 4 or 5.")
1350 print(" --fsfs-sharding Default shard size (for fsfs)\n"
1351 " --fsfs-packing Run 'svnadmin pack' automatically")
1352 print(" --config-file Configuration file for tests.")
1353 print(" --keep-local-tmp Don't remove svn-test-work/local_tmp after test\n"
1354 " run is complete. Useful for debugging failures.")
1355 print(" --help This information")
1358 # Main func. This is the "entry point" that all the test scripts call
1359 # to run their list of tests.
1361 # This routine parses sys.argv to decide what to do.
1362 def run_tests(test_list, serial_only = False):
1363 """Main routine to run all tests in TEST_LIST.
1365 NOTE: this function does not return. It does a sys.exit() with the
1366 appropriate exit code.
1369 global test_area_url
1370 global pristine_url
1371 global fs_type
1372 global verbose_mode
1373 global quiet_mode
1374 global cleanup_mode
1375 global enable_sasl
1376 global is_child_process
1377 global svn_binary
1378 global svnadmin_binary
1379 global svnlook_binary
1380 global svnsync_binary
1381 global svndumpfilter_binary
1382 global svnversion_binary
1383 global command_line_parsed
1384 global preferred_http_library
1385 global fsfs_sharding
1386 global fsfs_packing
1387 global config_file
1388 global server_minor_version
1389 global use_jsvn
1391 testnums = []
1392 # Should the tests be listed (as opposed to executed)?
1393 list_tests = False
1395 parallel = 0
1396 svn_bin = None
1397 use_jsvn = False
1398 keep_local_tmp = False
1399 config_file = None
1401 try:
1402 opts, args = my_getopt(sys.argv[1:], 'vqhpc',
1403 ['url=', 'fs-type=', 'verbose', 'quiet', 'cleanup',
1404 'list', 'enable-sasl', 'help', 'parallel',
1405 'bin=', 'http-library=', 'server-minor-version=',
1406 'fsfs-packing', 'fsfs-sharding=',
1407 'use-jsvn', 'development', 'keep-local-tmp',
1408 'config-file='])
1409 except getopt.GetoptError, e:
1410 print("ERROR: %s\n" % e)
1411 usage()
1412 sys.exit(1)
1414 for arg in args:
1415 if arg == "list":
1416 # This is an old deprecated variant of the "--list" option:
1417 list_tests = True
1418 elif arg.startswith('BASE_URL='):
1419 test_area_url = arg[9:]
1420 else:
1421 appended = False
1422 try:
1423 testnums.append(int(arg))
1424 appended = True
1425 except ValueError:
1426 # Do nothing for now.
1427 appended = False
1429 if not appended:
1430 try:
1431 # Check if the argument is a range
1432 numberstrings = arg.split(':');
1433 if len(numberstrings) != 2:
1434 numberstrings = arg.split('-');
1435 if len(numberstrings) != 2:
1436 raise ValueError
1437 left = int(numberstrings[0])
1438 right = int(numberstrings[1])
1439 if left > right:
1440 raise ValueError
1442 for nr in range(left,right+1):
1443 testnums.append(nr)
1444 else:
1445 appended = True
1446 except ValueError:
1447 appended = False
1449 if not appended:
1450 try:
1451 # Check if the argument is a function name, and translate
1452 # it to a number if possible
1453 for testnum in list(range(1, len(test_list))):
1454 test_case = TestRunner(test_list[testnum], testnum)
1455 if test_case.get_function_name() == str(arg):
1456 testnums.append(testnum)
1457 appended = True
1458 break
1459 except ValueError:
1460 appended = False
1462 if not appended:
1463 print("ERROR: invalid test number, range of numbers, " +
1464 "or function '%s'\n" % arg)
1465 usage()
1466 sys.exit(1)
1468 for opt, val in opts:
1469 if opt == "--url":
1470 test_area_url = val
1472 elif opt == "--fs-type":
1473 fs_type = val
1475 elif opt == "-v" or opt == "--verbose":
1476 verbose_mode = True
1478 elif opt == "-q" or opt == "--quiet":
1479 quiet_mode = True
1481 elif opt == "--cleanup":
1482 cleanup_mode = True
1484 elif opt == "--list":
1485 list_tests = True
1487 elif opt == "--enable-sasl":
1488 enable_sasl = True
1490 elif opt == "-h" or opt == "--help":
1491 usage()
1492 sys.exit(0)
1494 elif opt == '-p' or opt == "--parallel":
1495 parallel = 5 # use 5 parallel threads.
1497 elif opt == '-c':
1498 is_child_process = True
1500 elif opt == '--bin':
1501 svn_bin = val
1503 elif opt == '--http-library':
1504 preferred_http_library = val
1506 elif opt == '--fsfs-sharding':
1507 fsfs_sharding = int(val)
1508 elif opt == '--fsfs-packing':
1509 fsfs_packing = 1
1511 elif opt == '--server-minor-version':
1512 server_minor_version = int(val)
1513 if server_minor_version < 4 or server_minor_version > 7:
1514 print("ERROR: test harness only supports server minor versions 4-6")
1515 sys.exit(1)
1517 elif opt == '--use-jsvn':
1518 use_jsvn = True
1520 elif opt == '--keep-local-tmp':
1521 keep_local_tmp = True
1523 elif opt == '--development':
1524 setup_development_mode()
1526 elif opt == '--config-file':
1527 config_file = val
1529 if fsfs_packing is not None and fsfs_sharding is None:
1530 raise Exception('--fsfs-packing requires --fsfs-sharding')
1532 if test_area_url[-1:] == '/': # Normalize url to have no trailing slash
1533 test_area_url = test_area_url[:-1]
1535 if verbose_mode and quiet_mode:
1536 sys.stderr.write("ERROR: 'verbose' and 'quiet' are incompatible\n")
1537 sys.exit(1)
1539 # Calculate pristine_url from test_area_url.
1540 pristine_url = test_area_url + '/' + pathname2url(pristine_dir)
1542 if use_jsvn:
1543 if svn_bin is None:
1544 svn_bin = ''
1545 svn_binary = os.path.join(svn_bin, 'jsvn' + _bat)
1546 svnadmin_binary = os.path.join(svn_bin, 'jsvnadmin' + _bat)
1547 svnlook_binary = os.path.join(svn_bin, 'jsvnlook' + _bat)
1548 svnsync_binary = os.path.join(svn_bin, 'jsvnsync' + _bat)
1549 svndumpfilter_binary = os.path.join(svn_bin, 'jsvndumpfilter' + _bat)
1550 svnversion_binary = os.path.join(svn_bin, 'jsvnversion' + _bat)
1551 else:
1552 if svn_bin:
1553 svn_binary = os.path.join(svn_bin, 'svn' + _exe)
1554 svnadmin_binary = os.path.join(svn_bin, 'svnadmin' + _exe)
1555 svnlook_binary = os.path.join(svn_bin, 'svnlook' + _exe)
1556 svnsync_binary = os.path.join(svn_bin, 'svnsync' + _exe)
1557 svndumpfilter_binary = os.path.join(svn_bin, 'svndumpfilter' + _exe)
1558 svnversion_binary = os.path.join(svn_bin, 'svnversion' + _exe)
1560 command_line_parsed = True
1562 ######################################################################
1564 # Cleanup: if a previous run crashed or interrupted the python
1565 # interpreter, then `temp_dir' was never removed. This can cause wonkiness.
1566 if not is_child_process:
1567 safe_rmtree(temp_dir, 1)
1569 if not testnums:
1570 # If no test numbers were listed explicitly, include all of them:
1571 testnums = list(range(1, len(test_list)))
1573 if list_tests:
1574 print("Test # Mode Test Description")
1575 print("------ ----- ----------------")
1576 for testnum in testnums:
1577 TestRunner(test_list[testnum], testnum).list()
1579 # done. just exit with success.
1580 sys.exit(0)
1582 # don't run tests in parallel when the tests don't support it or there
1583 # are only a few tests to run.
1584 if serial_only or len(testnums) < 2:
1585 parallel = 0
1587 # Build out the default configuration directory
1588 create_config_dir(default_config_dir)
1590 # Setup the pristine repository
1591 svntest.actions.setup_pristine_repository()
1593 # Run the tests.
1594 exit_code = _internal_run_tests(test_list, testnums, parallel)
1596 # Remove all scratchwork: the 'pristine' repository, greek tree, etc.
1597 # This ensures that an 'import' will happen the next time we run.
1598 if not is_child_process and not keep_local_tmp:
1599 safe_rmtree(temp_dir, 1)
1601 # Cleanup after ourselves.
1602 svntest.sandbox.cleanup_deferred_test_paths()
1604 # Return the appropriate exit code from the tests.
1605 sys.exit(exit_code)