3 # Licensed to the Apache Software Foundation (ASF) under one
4 # or more contributor license agreements. See the NOTICE file
5 # distributed with this work for additional information
6 # regarding copyright ownership. The ASF licenses this file
7 # to you under the Apache License, Version 2.0 (the
8 # "License"); you may not use this file except in compliance
9 # with the License. You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing,
14 # software distributed under the License is distributed on an
15 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 # KIND, either express or implied. See the License for the
17 # specific language governing permissions and limitations
22 Driver for running the tests on Windows.
24 For a list of options, run this script with the --help option.
27 # $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.9.x/win-tests.py $
28 # $LastChangedRevision: 1718291 $
30 import os, sys, subprocess
40 import ConfigParser as configparser
46 my_getopt = getopt.gnu_getopt
47 except AttributeError:
48 my_getopt = getopt.getopt
51 "print usage, exit the script"
53 print("Driver for running the tests on Windows.")
54 print("Usage: python win-tests.py [option] [test-path]")
56 print("Valid options:")
57 print(" -r, --release : test the Release configuration")
58 print(" -d, --debug : test the Debug configuration (default)")
59 print(" --bin=PATH : use the svn binaries installed in PATH")
60 print(" -u URL, --url=URL : run ra_dav or ra_svn tests against URL;")
61 print(" will start svnserve for ra_svn tests")
62 print(" -v, --verbose : talk more")
63 print(" -f, --fs-type=type : filesystem type to use (fsfs is default)")
64 print(" -c, --cleanup : cleanup after running a test")
65 print(" -t, --test=TEST : Run the TEST test (all is default); use")
66 print(" TEST#n to run a particular test number,")
67 print(" multiples also accepted e.g. '2,4-7'")
68 print(" --log-level=LEVEL : Set log level to LEVEL (E.g. DEBUG)")
69 print(" --log-to-stdout : Write log results to stdout")
71 print(" --svnserve-args=list : comma-separated list of arguments for")
73 print(" default is '-d,-r,<test-path-root>'")
74 print(" --asp.net-hack : use '_svn' instead of '.svn' for the admin")
76 print(" --httpd-dir : location where Apache HTTPD is installed")
77 print(" --httpd-port : port for Apache HTTPD; random port number")
78 print(" will be used, if not specified")
79 print(" --httpd-daemon : Run Apache httpd as daemon")
80 print(" --httpd-service : Run Apache httpd as Windows service (default)")
81 print(" --httpd-no-log : Disable httpd logging")
82 print(" --http-short-circuit : Use SVNPathAuthz short_circuit on HTTP server")
83 print(" --disable-http-v2 : Do not advertise support for HTTPv2 on server")
84 print(" --disable-bulk-updates : Disable bulk updates on HTTP server")
85 print(" --ssl-cert : Path to SSL server certificate to trust.")
86 print(" --javahl : Run the javahl tests instead of the normal tests")
87 print(" --swig=language : Run the swig perl/python/ruby tests instead of")
88 print(" the normal tests")
89 print(" --list : print test doc strings only")
90 print(" --milestone-filter=RE : RE is a regular expression pattern that (when")
91 print(" used with --list) limits the tests listed to")
92 print(" those with an associated issue in the tracker")
93 print(" which has a target milestone that matches RE.")
94 print(" --mode-filter=TYPE : limit tests to expected TYPE = XFAIL, SKIP, PASS,")
95 print(" or 'ALL' (default)")
96 print(" --enable-sasl : enable Cyrus SASL authentication for")
98 print(" -p, --parallel : run multiple tests in parallel")
99 print(" --server-minor-version : the minor version of the server being")
101 print(" --config-file : Configuration file for tests")
102 print(" --fsfs-sharding : Specify shard size (for fsfs)")
103 print(" --fsfs-packing : Run 'svnadmin pack' automatically")
104 print(" -q, --quiet : Deprecated; this is the default.")
105 print(" Use --set-log-level instead.")
109 CMDLINE_TEST_SCRIPT_PATH = 'subversion/tests/cmdline/'
110 CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLINE_TEST_SCRIPT_PATH.replace('/', os.sep)
112 sys.path.insert(0, os.path.join('build', 'generator'))
113 sys.path.insert(1, 'build')
115 import gen_win_dependencies
117 version_header = os.path.join('subversion', 'include', 'svn_version.h')
118 cp = configparser.ConfigParser()
119 cp.read('gen-make.opts')
120 gen_obj = gen_win_dependencies.GenDependenciesBase('build.conf', version_header,
122 opts, args = my_getopt(sys.argv[1:], 'hrdvqct:pu:f:',
123 ['release', 'debug', 'verbose', 'quiet', 'cleanup',
124 'test=', 'url=', 'svnserve-args=', 'fs-type=', 'asp.net-hack',
125 'httpd-dir=', 'httpd-port=', 'httpd-daemon',
126 'httpd-server', 'http-short-circuit', 'httpd-no-log',
127 'disable-http-v2', 'disable-bulk-updates', 'help',
128 'fsfs-packing', 'fsfs-sharding=', 'javahl', 'swig=',
129 'list', 'enable-sasl', 'bin=', 'parallel',
130 'config-file=', 'server-minor-version=', 'log-level=',
131 'log-to-stdout', 'mode-filter=', 'milestone-filter=',
134 print('Warning: non-option arguments after the first one will be ignored')
136 # Interpret the options and set parameters
137 base_url, fs_type, verbose, cleanup = None, None, None, None
138 repo_loc = 'local repository.'
141 faillog = 'fails.log'
148 http_short_circuit = False
149 advertise_httpv2 = True
150 http_bulk_updates = True
152 milestone_filter = None
160 server_minor_version = None
168 for opt, val in opts:
169 if opt in ('-h', '--help'):
171 elif opt in ('-u', '--url'):
173 elif opt in ('-f', '--fs-type'):
175 elif opt in ('-v', '--verbose'):
177 log_level = logging.DEBUG
178 elif opt in ('-c', '--cleanup'):
180 elif opt in ('-t', '--test'):
181 tests_to_run.append(val)
182 elif opt in ['-r', '--release']:
184 elif opt in ['-d', '--debug']:
186 elif opt == '--svnserve-args':
187 svnserve_args = val.split(',')
189 elif opt == '--asp.net-hack':
190 os.environ['SVN_ASP_DOT_NET_HACK'] = opt
191 elif opt == '--httpd-dir':
192 abs_httpd_dir = os.path.abspath(val)
194 elif opt == '--httpd-port':
195 httpd_port = int(val)
196 elif opt == '--httpd-daemon':
198 elif opt == '--httpd-service':
200 elif opt == '--httpd-no-log':
202 elif opt == '--http-short-circuit':
203 http_short_circuit = True
204 elif opt == '--disable-http-v2':
205 advertise_httpv2 = False
206 elif opt == '--disable-bulk-updates':
207 http_bulk_updates = False
208 elif opt == '--fsfs-sharding':
209 fsfs_sharding = int(val)
210 elif opt == '--fsfs-packing':
212 elif opt == '--javahl':
214 elif opt == '--swig':
215 if val not in ['perl', 'python', 'ruby']:
216 sys.stderr.write('Running \'%s\' swig tests not supported (yet).\n'
219 elif opt == '--list':
221 elif opt == '--milestone-filter':
222 milestone_filter = val
223 elif opt == '--mode-filter':
225 elif opt == '--enable-sasl':
227 base_url = "svn://localhost/"
228 elif opt == '--server-minor-version':
229 server_minor_version = int(val)
232 elif opt in ('-p', '--parallel'):
234 elif opt in ('--config-file'):
236 elif opt == '--log-to-stdout':
238 elif opt == '--log-level':
239 log_level = getattr(logging, val, None) or int(val)
240 elif opt == '--ssl-cert':
243 # Calculate the source and test directory names
244 abs_srcdir = os.path.abspath("")
245 abs_objdir = os.path.join(abs_srcdir, objdir)
247 abs_builddir = abs_objdir
250 abs_builddir = os.path.abspath(args[0])
253 # Default to fsfs explicitly
258 all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \
259 + gen_obj.scripts + gen_obj.bdb_scripts
261 all_tests = gen_obj.test_progs + gen_obj.scripts
263 client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)]
267 httpd_port = random.randrange(1024, 30000)
269 base_url = 'http://localhost:' + str(httpd_port)
272 repo_loc = 'remote repository ' + base_url + '.'
273 if base_url[:4] == 'http':
274 log = 'dav-tests.log'
275 faillog = 'dav-fails.log'
276 elif base_url[:3] == 'svn':
277 log = 'svn-tests.log'
278 faillog = 'svn-fails.log'
281 # Don't know this scheme, but who're we to judge whether it's
283 log = 'url-tests.log'
284 faillog = 'url-fails.log'
286 # Have to move the executables where the tests expect them to be
287 copied_execs = [] # Store copied exec files to avoid the final dir scan
289 def create_target_dir(dirname):
290 tgt_dir = os.path.join(abs_builddir, dirname)
291 if not os.path.exists(tgt_dir):
293 print("mkdir: %s" % tgt_dir)
296 def copy_changed_file(src, tgt=None, to_dir=None, cleanup=True):
297 if not os.path.isfile(src):
298 print('Could not find ' + src)
301 if to_dir and not tgt:
302 tgt = os.path.join(to_dir, os.path.basename(src))
303 elif not tgt or (tgt and to_dir):
304 raise RuntimeError("Using 'tgt' *or* 'to_dir' is required" % (tgt,))
305 elif tgt and os.path.isdir(tgt):
306 raise RuntimeError("'%s' is a directory. Use to_dir=" % (tgt,))
308 if os.path.exists(tgt):
309 assert os.path.isfile(tgt)
310 if filecmp.cmp(src, tgt):
312 print("same: %s" % src)
313 print(" and: %s" % tgt)
316 print("copy: %s" % src)
317 print(" to: %s" % tgt)
318 shutil.copy(src, tgt)
321 copied_execs.append(tgt)
324 "Move DLLs to a known location and set env vars"
326 debug = (objdir == 'Debug')
328 for lib in gen_obj._libraries.values():
331 name, dir = lib.debug_dll_name, lib.debug_dll_dir
333 name, dir = lib.dll_name, lib.dll_dir
336 src = os.path.join(dir, name)
337 if os.path.exists(src):
338 copy_changed_file(src, to_dir=abs_builddir, cleanup=False)
340 for name in lib.extra_bin:
341 src = os.path.join(dir, name)
342 copy_changed_file(src, to_dir=abs_builddir)
345 # Copy the Subversion library DLLs
346 for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
347 if isinstance(i, gen_base.TargetLib) and i.msvc_export:
348 src = os.path.join(abs_objdir, i.filename)
349 if os.path.isfile(src):
350 copy_changed_file(src, to_dir=abs_builddir,
353 # Copy the Apache modules
354 if run_httpd and cp.has_option('options', '--with-httpd'):
355 mod_dav_svn_path = os.path.join(abs_objdir, 'subversion',
356 'mod_dav_svn', 'mod_dav_svn.so')
357 mod_authz_svn_path = os.path.join(abs_objdir, 'subversion',
358 'mod_authz_svn', 'mod_authz_svn.so')
359 mod_dontdothat_path = os.path.join(abs_objdir, 'tools', 'server-side',
360 'mod_dontdothat', 'mod_dontdothat.so')
362 copy_changed_file(mod_dav_svn_path, to_dir=abs_builddir, cleanup=False)
363 copy_changed_file(mod_authz_svn_path, to_dir=abs_builddir, cleanup=False)
364 copy_changed_file(mod_dontdothat_path, to_dir=abs_builddir, cleanup=False)
366 os.environ['PATH'] = abs_builddir + os.pathsep + os.environ['PATH']
369 path = os.path.normpath(path)
370 parts = path.split(os.path.sep)
371 drive = parts[0].upper()
373 path = drive + os.path.sep
375 dirs = os.listdir(path)
377 if dir.lower() == part.lower():
378 path = os.path.join(path, dir)
383 "Run svnserve for ra_svn tests"
384 def __init__(self, svnserve_args, objdir, abs_objdir, abs_builddir):
385 self.args = svnserve_args
386 self.name = 'svnserve.exe'
388 self.path = os.path.join(abs_objdir,
389 'subversion', 'svnserve', self.name)
390 self.root = os.path.join(abs_builddir, CMDLINE_TEST_SCRIPT_NATIVE_PATH)
394 "Stop svnserve when the object is deleted"
397 def _quote(self, arg):
399 return '"' + arg + '"'
405 args = [self.name, '-d', '-r', self.root]
407 args = [self.name] + self.args
408 print('Starting %s %s' % (self.kind, self.name))
410 self.proc = subprocess.Popen([self.path] + args[1:])
413 if self.proc is not None:
415 print('Stopping %s' % self.name)
417 if self.proc.returncode is None:
420 except AttributeError:
422 print('Svnserve.stop not implemented')
425 "Run httpd for DAV tests"
426 def __init__(self, abs_httpd_dir, abs_objdir, abs_builddir, httpd_port,
427 service, no_log, httpv2, short_circuit, bulk_updates):
428 self.name = 'apache.exe'
429 self.httpd_port = httpd_port
430 self.httpd_dir = abs_httpd_dir
433 self.httpv2_option = 'on'
435 self.httpv2_option = 'off'
438 self.bulkupdates_option = 'on'
440 self.bulkupdates_option = 'off'
442 self.service = service
444 self.path = os.path.join(self.httpd_dir, 'bin', self.name)
447 self.path_authz_option = 'short_circuit'
449 self.path_authz_option = 'on'
451 if not os.path.exists(self.path):
452 self.name = 'httpd.exe'
453 self.path = os.path.join(self.httpd_dir, 'bin', self.name)
454 if not os.path.exists(self.path):
455 raise RuntimeError("Could not find a valid httpd binary!")
457 self.root_dir = os.path.join(CMDLINE_TEST_SCRIPT_NATIVE_PATH, 'httpd')
458 self.root = os.path.join(abs_builddir, self.root_dir)
459 self.authz_file = os.path.join(abs_builddir,
460 CMDLINE_TEST_SCRIPT_NATIVE_PATH,
461 'svn-test-work', 'authz')
462 self.dontdothat_file = os.path.join(abs_builddir,
463 CMDLINE_TEST_SCRIPT_NATIVE_PATH,
464 'svn-test-work', 'dontdothat')
465 self.httpd_config = os.path.join(self.root, 'httpd.conf')
466 self.httpd_users = os.path.join(self.root, 'users')
467 self.httpd_mime_types = os.path.join(self.root, 'mime.types')
468 self.httpd_groups = os.path.join(self.root, 'groups')
469 self.abs_builddir = abs_builddir
470 self.abs_objdir = abs_objdir
471 self.service_name = 'svn-test-httpd-' + str(httpd_port)
474 self.httpd_args = [self.name, '-n', self._quote(self.service_name),
475 '-f', self._quote(self.httpd_config)]
477 self.httpd_args = [self.name, '-f', self._quote(self.httpd_config)]
479 create_target_dir(self.root_dir)
481 self._create_users_file()
482 self._create_groups_file()
483 self._create_mime_types_file()
484 self._create_dontdothat_file()
487 version_vals = gen_obj._libraries['httpd'].version.split('.')
488 self.httpd_ver = float('%s.%s' % (version_vals[0], version_vals[1]))
490 # Create httpd config file
491 fp = open(self.httpd_config, 'w')
493 # Limit the number of threads (default = 64)
494 fp.write('<IfModule mpm_winnt.c>\n')
495 fp.write('ThreadsPerChild 16\n')
496 fp.write('</IfModule>\n')
499 fp.write('ServerRoot ' + self._quote(self.root) + '\n')
500 fp.write('DocumentRoot ' + self._quote(self.root) + '\n')
501 fp.write('ServerName localhost\n')
502 fp.write('PidFile pid\n')
503 fp.write('ErrorLog log\n')
504 fp.write('Listen ' + str(self.httpd_port) + '\n')
507 fp.write('LogFormat "%h %l %u %t \\"%r\\" %>s %b" common\n')
508 fp.write('Customlog log common\n')
509 fp.write('LogLevel Debug\n')
511 fp.write('LogLevel Crit\n')
513 # Write LoadModule for minimal system module
514 fp.write(self._sys_module('dav_module', 'mod_dav.so'))
515 if self.httpd_ver >= 2.3:
516 fp.write(self._sys_module('access_compat_module', 'mod_access_compat.so'))
517 fp.write(self._sys_module('authz_core_module', 'mod_authz_core.so'))
518 fp.write(self._sys_module('authz_user_module', 'mod_authz_user.so'))
519 fp.write(self._sys_module('authn_core_module', 'mod_authn_core.so'))
520 if self.httpd_ver >= 2.2:
521 fp.write(self._sys_module('auth_basic_module', 'mod_auth_basic.so'))
522 fp.write(self._sys_module('authn_file_module', 'mod_authn_file.so'))
523 fp.write(self._sys_module('authz_groupfile_module', 'mod_authz_groupfile.so'))
524 fp.write(self._sys_module('authz_host_module', 'mod_authz_host.so'))
526 fp.write(self._sys_module('auth_module', 'mod_auth.so'))
527 fp.write(self._sys_module('alias_module', 'mod_alias.so'))
528 fp.write(self._sys_module('mime_module', 'mod_mime.so'))
529 fp.write(self._sys_module('log_config_module', 'mod_log_config.so'))
531 # Write LoadModule for Subversion modules
532 fp.write(self._svn_module('dav_svn_module', 'mod_dav_svn.so'))
533 fp.write(self._svn_module('authz_svn_module', 'mod_authz_svn.so'))
535 # And for mod_dontdothat
536 fp.write(self._svn_module('dontdothat_module', 'mod_dontdothat.so'))
538 # Don't handle .htaccess, symlinks, etc.
539 fp.write('<Directory />\n')
540 fp.write('AllowOverride None\n')
541 fp.write('Options None\n')
542 fp.write('</Directory>\n\n')
544 # Define two locations for repositories
545 fp.write(self._svn_repo('repositories'))
546 fp.write(self._svn_repo('local_tmp'))
547 fp.write(self._svn_authz_repo())
549 # And two redirects for the redirect tests
550 fp.write('RedirectMatch permanent ^/svn-test-work/repositories/'
551 'REDIRECT-PERM-(.*)$ /svn-test-work/repositories/$1\n')
552 fp.write('RedirectMatch ^/svn-test-work/repositories/'
553 'REDIRECT-TEMP-(.*)$ /svn-test-work/repositories/$1\n')
555 fp.write('TypesConfig ' + self._quote(self.httpd_mime_types) + '\n')
556 fp.write('HostNameLookups Off\n')
561 "Stop httpd when the object is deleted"
564 def _quote(self, arg):
566 return '"' + arg + '"'
570 def _create_users_file(self):
572 htpasswd = os.path.join(self.httpd_dir, 'bin', 'htpasswd.exe')
573 # Create the cheapest to compare password form for our testsuite
574 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bcp', self.httpd_users,
575 'jrandom', 'rayjandom'])
576 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
577 'jconstant', 'rayjandom'])
578 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
579 'JRANDOM', 'rayjandom'])
580 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
581 'JCONSTANT', 'rayjandom'])
583 def _create_groups_file(self):
584 "Create groups for mod_authz_svn tests"
585 fp = open(self.httpd_groups, 'w')
586 fp.write('random: jrandom\n')
587 fp.write('constant: jconstant\n')
590 def _create_mime_types_file(self):
591 "Create empty mime.types file"
592 fp = open(self.httpd_mime_types, 'w')
595 def _create_dontdothat_file(self):
596 "Create empty mime.types file"
597 # If the tests have not previously been run or were cleaned
598 # up, then 'svn-test-work' does not exist yet.
599 parent_dir = os.path.dirname(self.dontdothat_file)
600 if not os.path.exists(parent_dir):
601 os.makedirs(parent_dir)
603 fp = open(self.dontdothat_file, 'w')
604 fp.write('[recursive-actions]\n')
605 fp.write('/ = deny\n')
608 def _sys_module(self, name, path):
609 full_path = os.path.join(self.httpd_dir, 'modules', path)
610 return 'LoadModule ' + name + " " + self._quote(full_path) + '\n'
612 def _svn_module(self, name, path):
613 full_path = os.path.join(self.abs_builddir, path)
614 return 'LoadModule ' + name + ' ' + self._quote(full_path) + '\n'
616 def _svn_repo(self, name):
617 path = os.path.join(self.abs_builddir,
618 CMDLINE_TEST_SCRIPT_NATIVE_PATH,
619 'svn-test-work', name)
620 location = '/svn-test-work/' + name
621 ddt_location = '/ddt-test-work/' + name
623 '<Location ' + location + '>\n' \
625 ' SVNParentPath ' + self._quote(path) + '\n' \
626 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
627 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
628 ' SVNAllowBulkUpdates ' + self.bulkupdates_option + '\n' \
629 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
630 ' AuthType Basic\n' \
631 ' AuthName "Subversion Repository"\n' \
632 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
633 ' Require valid-user\n' \
635 '<Location ' + ddt_location + '>\n' \
637 ' SVNParentPath ' + self._quote(path) + '\n' \
638 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
639 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
640 ' SVNAllowBulkUpdates ' + self.bulkupdates_option + '\n' \
641 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
642 ' AuthType Basic\n' \
643 ' AuthName "Subversion Repository"\n' \
644 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
645 ' Require valid-user\n' \
646 ' DontDoThatConfigFile ' + self._quote(self.dontdothat_file) + '\n' \
649 def _svn_authz_repo(self):
650 local_tmp = os.path.join(self.abs_builddir,
651 CMDLINE_TEST_SCRIPT_NATIVE_PATH,
652 'svn-test-work', 'local_tmp')
654 '<Location /authz-test-work/anon>' + '\n' \
656 ' SVNParentPath ' + local_tmp + '\n' \
657 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
658 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
659 ' SVNListParentPath On' + '\n' \
660 ' <IfModule mod_authz_core.c>' + '\n' \
661 ' Require all granted' + '\n' \
662 ' </IfModule>' + '\n' \
663 ' <IfModule !mod_authz_core.c>' + '\n' \
664 ' Allow from all' + '\n' \
665 ' </IfModule>' + '\n' \
666 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
667 '</Location>' + '\n' \
668 '<Location /authz-test-work/mixed>' + '\n' \
670 ' SVNParentPath ' + local_tmp + '\n' \
671 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
672 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
673 ' SVNListParentPath On' + '\n' \
674 ' AuthType Basic' + '\n' \
675 ' AuthName "Subversion Repository"' + '\n' \
676 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
677 ' Require valid-user' + '\n' \
678 ' Satisfy Any' + '\n' \
679 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
680 '</Location>' + '\n' \
681 '<Location /authz-test-work/mixed-noauthwhenanon>' + '\n' \
683 ' SVNParentPath ' + local_tmp + '\n' \
684 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
685 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
686 ' SVNListParentPath On' + '\n' \
687 ' AuthType Basic' + '\n' \
688 ' AuthName "Subversion Repository"' + '\n' \
689 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
690 ' Require valid-user' + '\n' \
691 ' AuthzSVNNoAuthWhenAnonymousAllowed On' + '\n' \
692 ' SVNPathAuthz On' + '\n' \
693 '</Location>' + '\n' \
694 '<Location /authz-test-work/authn>' + '\n' \
696 ' SVNParentPath ' + local_tmp + '\n' \
697 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
698 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
699 ' SVNListParentPath On' + '\n' \
700 ' AuthType Basic' + '\n' \
701 ' AuthName "Subversion Repository"' + '\n' \
702 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
703 ' Require valid-user' + '\n' \
704 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
705 '</Location>' + '\n' \
706 '<Location /authz-test-work/authn-anonoff>' + '\n' \
708 ' SVNParentPath ' + local_tmp + '\n' \
709 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
710 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
711 ' SVNListParentPath On' + '\n' \
712 ' AuthType Basic' + '\n' \
713 ' AuthName "Subversion Repository"' + '\n' \
714 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
715 ' Require valid-user' + '\n' \
716 ' AuthzSVNAnonymous Off' + '\n' \
717 ' SVNPathAuthz On' + '\n' \
718 '</Location>' + '\n' \
719 '<Location /authz-test-work/authn-lcuser>' + '\n' \
721 ' SVNParentPath ' + local_tmp + '\n' \
722 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
723 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
724 ' SVNListParentPath On' + '\n' \
725 ' AuthType Basic' + '\n' \
726 ' AuthName "Subversion Repository"' + '\n' \
727 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
728 ' Require valid-user' + '\n' \
729 ' AuthzForceUsernameCase Lower' + '\n' \
730 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
731 '</Location>' + '\n' \
732 '<Location /authz-test-work/authn-lcuser>' + '\n' \
734 ' SVNParentPath ' + local_tmp + '\n' \
735 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
736 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
737 ' SVNListParentPath On' + '\n' \
738 ' AuthType Basic' + '\n' \
739 ' AuthName "Subversion Repository"' + '\n' \
740 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
741 ' Require valid-user' + '\n' \
742 ' AuthzForceUsernameCase Lower' + '\n' \
743 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
744 '</Location>' + '\n' \
745 '<Location /authz-test-work/authn-group>' + '\n' \
747 ' SVNParentPath ' + local_tmp + '\n' \
748 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
749 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
750 ' SVNListParentPath On' + '\n' \
751 ' AuthType Basic' + '\n' \
752 ' AuthName "Subversion Repository"' + '\n' \
753 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
754 ' AuthGroupFile ' + self._quote(self.httpd_groups) + '\n' \
755 ' Require group random' + '\n' \
756 ' AuthzSVNAuthoritative Off' + '\n' \
757 ' SVNPathAuthz On' + '\n' \
758 '</Location>' + '\n' \
759 '<IfModule mod_authz_core.c>' + '\n' \
760 '<Location /authz-test-work/sallrany>' + '\n' \
762 ' SVNParentPath ' + local_tmp + '\n' \
763 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
764 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
765 ' SVNListParentPath On' + '\n' \
766 ' AuthType Basic' + '\n' \
767 ' AuthName "Subversion Repository"' + '\n' \
768 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
769 ' AuthzSendForbiddenOnFailure On' + '\n' \
770 ' Satisfy All' + '\n' \
771 ' <RequireAny>' + '\n' \
772 ' Require valid-user' + '\n' \
773 ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \
774 ' </RequireAny>' + '\n' \
775 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
776 '</Location>' + '\n' \
777 '<Location /authz-test-work/sallrall>'+ '\n' \
779 ' SVNParentPath ' + local_tmp + '\n' \
780 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
781 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
782 ' SVNListParentPath On' + '\n' \
783 ' AuthType Basic' + '\n' \
784 ' AuthName "Subversion Repository"' + '\n' \
785 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
786 ' AuthzSendForbiddenOnFailure On' + '\n' \
787 ' Satisfy All' + '\n' \
788 ' <RequireAll>' + '\n' \
789 ' Require valid-user' + '\n' \
790 ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \
791 '</RequireAll>' + '\n' \
792 ' SVNPathAuthz ' + self.path_authz_option + '\n' \
793 '</Location>' + '\n' \
794 '</IfModule>' + '\n' \
798 self._start_service()
808 def _start_service(self):
809 "Install and start HTTPD service"
810 print('Installing service %s' % self.service_name)
811 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'install'])
812 print('Starting service %s' % self.service_name)
813 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'start'])
815 def _stop_service(self):
816 "Stop and uninstall HTTPD service"
817 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'stop'])
818 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'uninstall'])
820 def _start_daemon(self):
821 "Start HTTPD as daemon"
822 print('Starting httpd as daemon')
823 print(self.httpd_args)
824 self.proc = subprocess.Popen([self.path] + self.httpd_args[1:])
826 def _stop_daemon(self):
827 "Stop the HTTPD daemon"
828 if self.proc is not None:
830 print('Stopping %s' % self.name)
832 if self.proc.returncode is None:
835 except AttributeError:
837 print('Httpd.stop_daemon not implemented')
839 # Move the binaries to the test directory
840 create_target_dir(abs_builddir)
843 for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
844 if isinstance(i, gen_base.TargetExe):
845 src = os.path.join(abs_objdir, i.filename)
847 if os.path.isfile(src):
848 dst = os.path.join(abs_builddir, i.filename)
849 create_target_dir(os.path.dirname(dst))
850 copy_changed_file(src, dst)
852 # Create the base directory for Python tests
853 create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH)
855 # Ensure the tests directory is correctly cased
856 abs_builddir = fix_case(abs_builddir)
861 # No need to start any servers if we are only listing the tests.
864 daemon = Svnserve(svnserve_args, objdir, abs_objdir, abs_builddir)
867 daemon = Httpd(abs_httpd_dir, abs_objdir, abs_builddir, httpd_port,
868 httpd_service, httpd_no_log,
869 advertise_httpv2, http_short_circuit,
872 # Start service daemon, if any
876 # Find the full path and filename of any test that is specified just by
878 if len(tests_to_run) != 0:
880 for t in tests_to_run:
883 t, tns = t.split('#')
885 test = [x for x in all_tests if x.split('/')[-1] == t]
886 if not test and not (t.endswith('-test.exe') or t.endswith('_tests.py')):
887 # The lengths of '-test.exe' and of '_tests.py' are both 9.
888 test = [x for x in all_tests if x.split('/')[-1][:-9] == t]
891 print("Skipping test '%s', test not found." % t)
893 tests.append('%s#%s' % (test[0], tns))
899 tests_to_run = all_tests
903 print('Listing %s configuration on %s' % (objdir, repo_loc))
905 print('Testing %s configuration on %s' % (objdir, repo_loc))
906 sys.path.insert(0, os.path.join(abs_srcdir, 'build'))
908 if not test_javahl and not test_swig:
914 log_file = os.path.join(abs_builddir, log)
915 fail_log_file = os.path.join(abs_builddir, faillog)
918 httpd_version = gen_obj._libraries['httpd'].version
922 opts, args = run_tests.create_parser().parse_args([])
924 opts.fs_type = fs_type
925 opts.http_library = 'serf'
926 opts.server_minor_version = server_minor_version
927 opts.cleanup = cleanup
928 opts.enable_sasl = enable_sasl
929 opts.parallel = parallel
930 opts.config_file = config_file
931 opts.fsfs_sharding = fsfs_sharding
932 opts.fsfs_packing = fsfs_packing
933 opts.list_tests = list_tests
934 opts.svn_bin = svn_bin
935 opts.mode_filter = mode_filter
936 opts.milestone_filter = milestone_filter
937 opts.httpd_version = httpd_version
938 opts.set_log_level = log_level
939 opts.ssl_cert = ssl_cert
940 th = run_tests.TestHarness(abs_srcdir, abs_builddir,
941 log_file, fail_log_file, opts)
942 old_cwd = os.getcwd()
944 os.chdir(abs_builddir)
945 failed = th.run(tests_to_run)
956 for path in os.environ["PATH"].split(os.pathsep):
957 if os.path.isfile(os.path.join(path, 'java.exe')):
958 java_exe = os.path.join(path, 'java.exe')
961 if not java_exe and 'java_sdk' in gen_obj._libraries:
962 jdk = gen_obj._libraries['java_sdk']
964 if os.path.isfile(os.path.join(jdk.lib_dir, '../bin/java.exe')):
965 java_exe = os.path.join(jdk.lib_dir, '../bin/java.exe')
968 print('Java not found. Skipping Java tests')
970 args = (os.path.abspath(java_exe),)
971 if (objdir == 'Debug'):
972 args = args + ('-Xcheck:jni',)
975 '-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'),
976 '-Dtest.srcdir=' + os.path.join(abs_srcdir,
977 'subversion/bindings/javahl'),
979 '-Dtest.fstype=' + fs_type ,
982 '-Djava.library.path='
983 + os.path.join(abs_objdir,
984 'subversion/bindings/javahl/native'),
986 os.path.join(abs_srcdir, 'subversion/bindings/javahl/classes') +';' +
991 print('Running org.apache.subversion tests:')
994 r = subprocess.call(args + tuple(['org.apache.subversion.javahl.RunTests']))
998 print('[Test runner reported failure]')
1001 print('Running org.tigris.subversion tests:')
1003 r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests']))
1007 print('[Test runner reported failure]')
1009 elif test_swig == 'perl':
1011 swig_dir = os.path.join(abs_builddir, 'swig')
1012 swig_pl_dir = os.path.join(swig_dir, 'p5lib')
1013 swig_pl_svn = os.path.join(swig_pl_dir, 'SVN')
1014 swig_pl_auto_svn = os.path.join(swig_pl_dir, 'auto', 'SVN')
1016 create_target_dir(swig_pl_svn)
1018 for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
1019 if isinstance(i, gen_base.TargetSWIG) and i.lang == 'perl':
1020 mod_dir = os.path.join(swig_pl_auto_svn, '_' + i.name[5:].capitalize())
1021 create_target_dir(mod_dir)
1022 copy_changed_file(os.path.join(abs_objdir, i.filename), to_dir=mod_dir)
1024 elif isinstance(i, gen_base.TargetSWIGLib) and i.lang == 'perl':
1025 copy_changed_file(os.path.join(abs_objdir, i.filename),
1026 to_dir=abs_builddir)
1028 pm_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'perl',
1033 for root, dirs, files in os.walk(pm_src):
1035 if name.endswith('.pm'):
1036 fn = os.path.join(root, name)
1037 copy_changed_file(fn, to_dir=swig_pl_svn)
1038 elif name.endswith('.t'):
1039 tests.append(os.path.relpath(os.path.join(root, name), pm_src))
1041 perl5lib = swig_pl_dir
1042 if 'PERL5LIB' in os.environ:
1043 perl5lib += os.pathsep + os.environ['PERL5LIB']
1045 perl_exe = 'perl.exe'
1047 print('-- Running Swig Perl tests --')
1049 old_cwd = os.getcwd()
1053 os.environ['PERL5LIB'] = perl5lib
1054 os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES'
1056 r = subprocess.call([
1058 '-MExtUtils::Command::MM',
1059 '-e', 'test_harness()'
1065 print('[Test runner reported failure]')
1067 elif test_swig == 'python':
1069 swig_dir = os.path.join(abs_builddir, 'swig')
1070 swig_py_dir = os.path.join(swig_dir, 'pylib')
1071 swig_py_libsvn = os.path.join(swig_py_dir, 'libsvn')
1072 swig_py_svn = os.path.join(swig_py_dir, 'svn')
1074 create_target_dir(swig_py_libsvn)
1075 create_target_dir(swig_py_svn)
1077 for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
1078 if (isinstance(i, gen_base.TargetSWIG)
1079 or isinstance(i, gen_base.TargetSWIGLib)) and i.lang == 'python':
1081 src = os.path.join(abs_objdir, i.filename)
1082 copy_changed_file(src, to_dir=swig_py_libsvn)
1084 py_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'python')
1086 for py_file in os.listdir(py_src):
1087 if py_file.endswith('.py'):
1088 copy_changed_file(os.path.join(py_src, py_file),
1089 to_dir=swig_py_libsvn)
1091 py_src_svn = os.path.join(py_src, 'svn')
1092 for py_file in os.listdir(py_src_svn):
1093 if py_file.endswith('.py'):
1094 copy_changed_file(os.path.join(py_src_svn, py_file),
1097 print('-- Running Swig Python tests --')
1100 pythonpath = swig_py_dir
1101 if 'PYTHONPATH' in os.environ:
1102 pythonpath += os.pathsep + os.environ['PYTHONPATH']
1104 python_exe = 'python.exe'
1105 old_cwd = os.getcwd()
1107 os.environ['PYTHONPATH'] = pythonpath
1109 r = subprocess.call([
1111 os.path.join(py_src, 'tests', 'run_all.py')
1117 print('[Test runner reported failure]')
1120 elif test_swig == 'ruby':
1123 if 'ruby' not in gen_obj._libraries:
1124 print('Ruby not found. Skipping Ruby tests')
1126 ruby_lib = gen_obj._libraries['ruby']
1128 ruby_exe = 'ruby.exe'
1129 ruby_subdir = os.path.join('subversion', 'bindings', 'swig', 'ruby')
1131 '-I', os.path.join(abs_srcdir, ruby_subdir),
1132 os.path.join(abs_srcdir, ruby_subdir, 'test', 'run-test.rb'),
1136 print('-- Running Swig Ruby tests --')
1138 old_cwd = os.getcwd()
1140 os.chdir(ruby_subdir)
1142 os.environ["BUILD_TYPE"] = objdir
1143 os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES'
1144 r = subprocess.call([ruby_exe] + ruby_args)
1151 print('[Test runner reported failure]')
1154 # Stop service daemon, if any
1158 # Remove the execs again
1159 for tgt in copied_execs:
1161 if os.path.isfile(tgt):
1163 print("kill: %s" % tgt)
1166 traceback.print_exc(file=sys.stdout)