win-tests.py revision 286506
1# 2# 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 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 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 18# under the License. 19# 20# 21""" 22Driver for running the tests on Windows. 23 24For a list of options, run this script with the --help option. 25""" 26 27# $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.8.x/win-tests.py $ 28# $LastChangedRevision: 1692801 $ 29 30import os, sys, subprocess 31import filecmp 32import shutil 33import traceback 34try: 35 # Python >=3.0 36 import configparser 37except ImportError: 38 # Python <3.0 39 import ConfigParser as configparser 40import string 41import random 42 43import getopt 44try: 45 my_getopt = getopt.gnu_getopt 46except AttributeError: 47 my_getopt = getopt.getopt 48 49def _usage_exit(): 50 "print usage, exit the script" 51 52 print("Driver for running the tests on Windows.") 53 print("Usage: python win-tests.py [option] [test-path]") 54 print("") 55 print("Valid options:") 56 print(" -r, --release : test the Release configuration") 57 print(" -d, --debug : test the Debug configuration (default)") 58 print(" --bin=PATH : use the svn binaries installed in PATH") 59 print(" -u URL, --url=URL : run ra_dav or ra_svn tests against URL;") 60 print(" will start svnserve for ra_svn tests") 61 print(" -v, --verbose : talk more") 62 print(" -q, --quiet : talk less") 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") 70 71 print(" --svnserve-args=list : comma-separated list of arguments for") 72 print(" svnserve") 73 print(" default is '-d,-r,<test-path-root>'") 74 print(" --asp.net-hack : use '_svn' instead of '.svn' for the admin") 75 print(" dir name") 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(" --list : print test doc strings only") 88 print(" --milestone-filter=RE : RE is a regular expression pattern that (when") 89 print(" used with --list) limits the tests listed to") 90 print(" those with an associated issue in the tracker") 91 print(" which has a target milestone that matches RE.") 92 print(" --mode-filter=TYPE : limit tests to expected TYPE = XFAIL, SKIP, PASS,") 93 print(" or 'ALL' (default)") 94 print(" --enable-sasl : enable Cyrus SASL authentication for") 95 print(" svnserve") 96 print(" -p, --parallel : run multiple tests in parallel") 97 print(" --server-minor-version : the minor version of the server being") 98 print(" tested") 99 print(" --config-file : Configuration file for tests") 100 print(" --fsfs-sharding : Specify shard size (for fsfs)") 101 print(" --fsfs-packing : Run 'svnadmin pack' automatically") 102 103 sys.exit(0) 104 105CMDLINE_TEST_SCRIPT_PATH = 'subversion/tests/cmdline/' 106CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLINE_TEST_SCRIPT_PATH.replace('/', os.sep) 107 108sys.path.insert(0, os.path.join('build', 'generator')) 109sys.path.insert(1, 'build') 110 111import gen_win 112version_header = os.path.join('subversion', 'include', 'svn_version.h') 113cp = configparser.ConfigParser() 114cp.read('gen-make.opts') 115gen_obj = gen_win.GeneratorBase('build.conf', version_header, 116 cp.items('options')) 117all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \ 118 + gen_obj.scripts + gen_obj.bdb_scripts 119client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)] 120 121svn_dlls = [] 122for section in gen_obj.sections.values(): 123 if section.options.get("msvc-export"): 124 dll_basename = section.name + "-" + str(gen_obj.version) + ".dll" 125 svn_dlls.append(os.path.join("subversion", section.name, dll_basename)) 126 127opts, args = my_getopt(sys.argv[1:], 'hrdvqct:pu:f:', 128 ['release', 'debug', 'verbose', 'quiet', 'cleanup', 129 'test=', 'url=', 'svnserve-args=', 'fs-type=', 'asp.net-hack', 130 'httpd-dir=', 'httpd-port=', 'httpd-daemon', 131 'httpd-server', 'http-short-circuit', 'httpd-no-log', 132 'disable-http-v2', 'disable-bulk-updates', 'help', 133 'fsfs-packing', 'fsfs-sharding=', 'javahl', 134 'list', 'enable-sasl', 'bin=', 'parallel', 135 'config-file=', 'server-minor-version=', 'log-level=', 136 'log-to-stdout', 'mode-filter=', 'milestone-filter=', 137 'ssl-cert=']) 138if len(args) > 1: 139 print('Warning: non-option arguments after the first one will be ignored') 140 141# Interpret the options and set parameters 142base_url, fs_type, verbose, quiet, cleanup = None, None, None, None, None 143repo_loc = 'local repository.' 144objdir = 'Debug' 145log = 'tests.log' 146faillog = 'fails.log' 147run_svnserve = None 148svnserve_args = None 149run_httpd = None 150httpd_port = None 151httpd_service = None 152httpd_no_log = None 153http_short_circuit = False 154advertise_httpv2 = True 155http_bulk_updates = True 156list_tests = None 157milestone_filter = None 158test_javahl = None 159enable_sasl = None 160svn_bin = None 161parallel = None 162fsfs_sharding = None 163fsfs_packing = None 164server_minor_version = None 165config_file = None 166log_to_stdout = None 167mode_filter=None 168tests_to_run = [] 169log_level = None 170ssl_cert = None 171 172for opt, val in opts: 173 if opt in ('-h', '--help'): 174 _usage_exit() 175 elif opt in ('-u', '--url'): 176 base_url = val 177 elif opt in ('-f', '--fs-type'): 178 fs_type = val 179 elif opt in ('-v', '--verbose'): 180 verbose = 1 181 elif opt in ('-q', '--quiet'): 182 quiet = 1 183 elif opt in ('-c', '--cleanup'): 184 cleanup = 1 185 elif opt in ('-t', '--test'): 186 tests_to_run.append(val) 187 elif opt in ['-r', '--release']: 188 objdir = 'Release' 189 elif opt in ['-d', '--debug']: 190 objdir = 'Debug' 191 elif opt == '--svnserve-args': 192 svnserve_args = val.split(',') 193 run_svnserve = 1 194 elif opt == '--asp.net-hack': 195 os.environ['SVN_ASP_DOT_NET_HACK'] = opt 196 elif opt == '--httpd-dir': 197 abs_httpd_dir = os.path.abspath(val) 198 run_httpd = 1 199 elif opt == '--httpd-port': 200 httpd_port = int(val) 201 elif opt == '--httpd-daemon': 202 httpd_service = 0 203 elif opt == '--httpd-service': 204 httpd_service = 1 205 elif opt == '--httpd-no-log': 206 httpd_no_log = 1 207 elif opt == '--http-short-circuit': 208 http_short_circuit = True 209 elif opt == '--disable-http-v2': 210 advertise_httpv2 = False 211 elif opt == '--disable-bulk-updates': 212 http_bulk_updates = False 213 elif opt == '--fsfs-sharding': 214 fsfs_sharding = int(val) 215 elif opt == '--fsfs-packing': 216 fsfs_packing = 1 217 elif opt == '--javahl': 218 test_javahl = 1 219 elif opt == '--list': 220 list_tests = 1 221 elif opt == '--milestone-filter': 222 milestone_filter = val 223 elif opt == '--mode-filter': 224 mode_filter = val 225 elif opt == '--enable-sasl': 226 enable_sasl = 1 227 base_url = "svn://localhost/" 228 elif opt == '--server-minor-version': 229 server_minor_version = val 230 elif opt == '--bin': 231 svn_bin = val 232 elif opt in ('-p', '--parallel'): 233 parallel = 1 234 elif opt in ('--config-file'): 235 config_file = val 236 elif opt == '--log-to-stdout': 237 log_to_stdout = 1 238 elif opt == '--log-level': 239 log_level = val 240 elif opt == '--ssl-cert': 241 ssl_cert = val 242 243# Calculate the source and test directory names 244abs_srcdir = os.path.abspath("") 245abs_objdir = os.path.join(abs_srcdir, objdir) 246if len(args) == 0: 247 abs_builddir = abs_objdir 248 create_dirs = 0 249else: 250 abs_builddir = os.path.abspath(args[0]) 251 create_dirs = 1 252 253# Default to fsfs explicitly 254if not fs_type: 255 fs_type = 'fsfs' 256 257# Don't run bdb tests if they want to test fsfs 258if fs_type == 'fsfs': 259 all_tests = gen_obj.test_progs + gen_obj.scripts 260 261if run_httpd: 262 if not httpd_port: 263 httpd_port = random.randrange(1024, 30000) 264 if not base_url: 265 base_url = 'http://localhost:' + str(httpd_port) 266 267if base_url: 268 repo_loc = 'remote repository ' + base_url + '.' 269 if base_url[:4] == 'http': 270 log = 'dav-tests.log' 271 faillog = 'dav-fails.log' 272 elif base_url[:3] == 'svn': 273 log = 'svn-tests.log' 274 faillog = 'svn-fails.log' 275 run_svnserve = 1 276 else: 277 # Don't know this scheme, but who're we to judge whether it's 278 # correct or not? 279 log = 'url-tests.log' 280 faillog = 'url-fails.log' 281 282# Have to move the executables where the tests expect them to be 283copied_execs = [] # Store copied exec files to avoid the final dir scan 284 285def create_target_dir(dirname): 286 tgt_dir = os.path.join(abs_builddir, dirname) 287 if not os.path.exists(tgt_dir): 288 if verbose: 289 print("mkdir: %s" % tgt_dir) 290 os.makedirs(tgt_dir) 291 292def copy_changed_file(src, tgt): 293 if not os.path.isfile(src): 294 print('Could not find ' + src) 295 sys.exit(1) 296 if os.path.isdir(tgt): 297 tgt = os.path.join(tgt, os.path.basename(src)) 298 if os.path.exists(tgt): 299 assert os.path.isfile(tgt) 300 if filecmp.cmp(src, tgt): 301 if verbose: 302 print("same: %s" % src) 303 print(" and: %s" % tgt) 304 return 0 305 if verbose: 306 print("copy: %s" % src) 307 print(" to: %s" % tgt) 308 shutil.copy(src, tgt) 309 return 1 310 311def copy_execs(baton, dirname, names): 312 copied_execs = baton 313 for name in names: 314 if not name.endswith('.exe'): 315 continue 316 src = os.path.join(dirname, name) 317 tgt = os.path.join(abs_builddir, dirname, name) 318 create_target_dir(dirname) 319 if copy_changed_file(src, tgt): 320 copied_execs.append(tgt) 321 322def locate_libs(): 323 "Move DLLs to a known location and set env vars" 324 325 dlls = [] 326 327 # look for APR 1.x dll's and use those if found 328 apr_test_path = os.path.join(gen_obj.apr_path, objdir, 'libapr-1.dll') 329 if os.path.exists(apr_test_path): 330 suffix = "-1" 331 else: 332 suffix = "" 333 334 if cp.has_option('options', '--with-static-apr'): 335 dlls.append(os.path.join(gen_obj.apr_path, objdir, 336 'libapr%s.dll' % (suffix))) 337 dlls.append(os.path.join(gen_obj.apr_util_path, objdir, 338 'libaprutil%s.dll' % (suffix))) 339 340 if gen_obj.libintl_path is not None: 341 dlls.append(os.path.join(gen_obj.libintl_path, 'bin', 'intl3_svn.dll')) 342 343 if gen_obj.bdb_lib is not None: 344 partial_path = os.path.join(gen_obj.bdb_path, 'bin', gen_obj.bdb_lib) 345 if objdir == 'Debug': 346 dlls.append(partial_path + 'd.dll') 347 else: 348 dlls.append(partial_path + '.dll') 349 350 if gen_obj.sasl_path is not None: 351 dlls.append(os.path.join(gen_obj.sasl_path, 'lib', 'libsasl.dll')) 352 353 for dll in dlls: 354 copy_changed_file(dll, abs_objdir) 355 356 # Copy the Subversion library DLLs 357 if not cp.has_option('options', '--disable-shared'): 358 for svn_dll in svn_dlls: 359 copy_changed_file(os.path.join(abs_objdir, svn_dll), abs_objdir) 360 361 # Copy the Apache modules 362 if run_httpd and cp.has_option('options', '--with-httpd'): 363 mod_dav_svn_path = os.path.join(abs_objdir, 'subversion', 364 'mod_dav_svn', 'mod_dav_svn.so') 365 mod_authz_svn_path = os.path.join(abs_objdir, 'subversion', 366 'mod_authz_svn', 'mod_authz_svn.so') 367 mod_dontdothat_path = os.path.join(abs_objdir, 'tools', 'server-side', 368 'mod_dontdothat', 'mod_dontdothat.so') 369 370 copy_changed_file(mod_dav_svn_path, abs_objdir) 371 copy_changed_file(mod_authz_svn_path, abs_objdir) 372 copy_changed_file(mod_dontdothat_path, abs_objdir) 373 374 os.environ['PATH'] = abs_objdir + os.pathsep + os.environ['PATH'] 375 376def fix_case(path): 377 path = os.path.normpath(path) 378 parts = path.split(os.path.sep) 379 drive = parts[0].upper() 380 parts = parts[1:] 381 path = drive + os.path.sep 382 for part in parts: 383 dirs = os.listdir(path) 384 for dir in dirs: 385 if dir.lower() == part.lower(): 386 path = os.path.join(path, dir) 387 break 388 return path 389 390class Svnserve: 391 "Run svnserve for ra_svn tests" 392 def __init__(self, svnserve_args, objdir, abs_objdir, abs_builddir): 393 self.args = svnserve_args 394 self.name = 'svnserve.exe' 395 self.kind = objdir 396 self.path = os.path.join(abs_objdir, 397 'subversion', 'svnserve', self.name) 398 self.root = os.path.join(abs_builddir, CMDLINE_TEST_SCRIPT_NATIVE_PATH) 399 self.proc_handle = None 400 401 def __del__(self): 402 "Stop svnserve when the object is deleted" 403 self.stop() 404 405 def _quote(self, arg): 406 if ' ' in arg: 407 return '"' + arg + '"' 408 else: 409 return arg 410 411 def start(self): 412 if not self.args: 413 args = [self.name, '-d', '-r', self.root] 414 else: 415 args = [self.name] + self.args 416 print('Starting %s %s' % (self.kind, self.name)) 417 try: 418 import win32process 419 import win32con 420 args = ' '.join([self._quote(x) for x in args]) 421 self.proc_handle = ( 422 win32process.CreateProcess(self._quote(self.path), args, 423 None, None, 0, 424 win32con.CREATE_NEW_CONSOLE, 425 None, None, win32process.STARTUPINFO()))[0] 426 except ImportError: 427 os.spawnv(os.P_NOWAIT, self.path, args) 428 429 def stop(self): 430 if self.proc_handle is not None: 431 try: 432 import win32process 433 print('Stopping %s' % self.name) 434 win32process.TerminateProcess(self.proc_handle, 0) 435 return 436 except ImportError: 437 pass 438 print('Svnserve.stop not implemented') 439 440class Httpd: 441 "Run httpd for DAV tests" 442 def __init__(self, abs_httpd_dir, abs_objdir, abs_builddir, httpd_port, 443 service, no_log, httpv2, short_circuit, bulk_updates): 444 self.name = 'apache.exe' 445 self.httpd_port = httpd_port 446 self.httpd_dir = abs_httpd_dir 447 448 if httpv2: 449 self.httpv2_option = 'on' 450 else: 451 self.httpv2_option = 'off' 452 453 if bulk_updates: 454 self.bulkupdates_option = 'on' 455 else: 456 self.bulkupdates_option = 'off' 457 458 self.service = service 459 self.proc_handle = None 460 self.path = os.path.join(self.httpd_dir, 'bin', self.name) 461 462 if short_circuit: 463 self.path_authz_option = 'short_circuit' 464 else: 465 self.path_authz_option = 'on' 466 467 if not os.path.exists(self.path): 468 self.name = 'httpd.exe' 469 self.path = os.path.join(self.httpd_dir, 'bin', self.name) 470 if not os.path.exists(self.path): 471 raise RuntimeError("Could not find a valid httpd binary!") 472 473 self.root_dir = os.path.join(CMDLINE_TEST_SCRIPT_NATIVE_PATH, 'httpd') 474 self.root = os.path.join(abs_builddir, self.root_dir) 475 self.authz_file = os.path.join(abs_builddir, 476 CMDLINE_TEST_SCRIPT_NATIVE_PATH, 477 'svn-test-work', 'authz') 478 self.dontdothat_file = os.path.join(abs_builddir, 479 CMDLINE_TEST_SCRIPT_NATIVE_PATH, 480 'svn-test-work', 'dontdothat') 481 self.httpd_config = os.path.join(self.root, 'httpd.conf') 482 self.httpd_users = os.path.join(self.root, 'users') 483 self.httpd_mime_types = os.path.join(self.root, 'mime.types') 484 self.httpd_groups = os.path.join(self.root, 'groups') 485 self.abs_builddir = abs_builddir 486 self.abs_objdir = abs_objdir 487 self.service_name = 'svn-test-httpd-' + str(httpd_port) 488 489 if self.service: 490 self.httpd_args = [self.name, '-n', self._quote(self.service_name), 491 '-f', self._quote(self.httpd_config)] 492 else: 493 self.httpd_args = [self.name, '-f', self._quote(self.httpd_config)] 494 495 create_target_dir(self.root_dir) 496 497 self._create_users_file() 498 self._create_groups_file() 499 self._create_mime_types_file() 500 self._create_dontdothat_file() 501 502 # Determine version. 503 if os.path.exists(os.path.join(self.httpd_dir, 504 'modules', 'mod_access_compat.so')): 505 self.httpd_ver = 2.3 506 elif os.path.exists(os.path.join(self.httpd_dir, 507 'modules', 'mod_auth_basic.so')): 508 self.httpd_ver = 2.2 509 else: 510 self.httpd_ver = 2.0 511 512 # Create httpd config file 513 fp = open(self.httpd_config, 'w') 514 515 # Limit the number of threads (default = 64) 516 fp.write('<IfModule mpm_winnt.c>\n') 517 fp.write('ThreadsPerChild 16\n') 518 fp.write('</IfModule>\n') 519 520 # Global Environment 521 fp.write('ServerRoot ' + self._quote(self.root) + '\n') 522 fp.write('DocumentRoot ' + self._quote(self.root) + '\n') 523 fp.write('ServerName localhost\n') 524 fp.write('PidFile pid\n') 525 fp.write('ErrorLog log\n') 526 fp.write('Listen ' + str(self.httpd_port) + '\n') 527 528 if not no_log: 529 fp.write('LogFormat "%h %l %u %t \\"%r\\" %>s %b" common\n') 530 fp.write('Customlog log common\n') 531 fp.write('LogLevel Debug\n') 532 else: 533 fp.write('LogLevel Crit\n') 534 535 # Write LoadModule for minimal system module 536 fp.write(self._sys_module('dav_module', 'mod_dav.so')) 537 if self.httpd_ver >= 2.3: 538 fp.write(self._sys_module('access_compat_module', 'mod_access_compat.so')) 539 fp.write(self._sys_module('authz_core_module', 'mod_authz_core.so')) 540 fp.write(self._sys_module('authz_user_module', 'mod_authz_user.so')) 541 fp.write(self._sys_module('authn_core_module', 'mod_authn_core.so')) 542 if self.httpd_ver >= 2.2: 543 fp.write(self._sys_module('auth_basic_module', 'mod_auth_basic.so')) 544 fp.write(self._sys_module('authn_file_module', 'mod_authn_file.so')) 545 fp.write(self._sys_module('authz_groupfile_module', 'mod_authz_groupfile.so')) 546 fp.write(self._sys_module('authz_host_module', 'mod_authz_host.so')) 547 else: 548 fp.write(self._sys_module('auth_module', 'mod_auth.so')) 549 fp.write(self._sys_module('alias_module', 'mod_alias.so')) 550 fp.write(self._sys_module('mime_module', 'mod_mime.so')) 551 fp.write(self._sys_module('log_config_module', 'mod_log_config.so')) 552 553 # Write LoadModule for Subversion modules 554 fp.write(self._svn_module('dav_svn_module', 'mod_dav_svn.so')) 555 fp.write(self._svn_module('authz_svn_module', 'mod_authz_svn.so')) 556 557 # And for mod_dontdothat 558 fp.write(self._svn_module('dontdothat_module', 'mod_dontdothat.so')) 559 560 # Don't handle .htaccess, symlinks, etc. 561 fp.write('<Directory />\n') 562 fp.write('AllowOverride None\n') 563 fp.write('Options None\n') 564 fp.write('</Directory>\n\n') 565 566 # Define two locations for repositories 567 fp.write(self._svn_repo('repositories')) 568 fp.write(self._svn_repo('local_tmp')) 569 fp.write(self._svn_authz_repo()) 570 571 # And two redirects for the redirect tests 572 fp.write('RedirectMatch permanent ^/svn-test-work/repositories/' 573 'REDIRECT-PERM-(.*)$ /svn-test-work/repositories/$1\n') 574 fp.write('RedirectMatch ^/svn-test-work/repositories/' 575 'REDIRECT-TEMP-(.*)$ /svn-test-work/repositories/$1\n') 576 577 fp.write('TypesConfig ' + self._quote(self.httpd_mime_types) + '\n') 578 fp.write('HostNameLookups Off\n') 579 580 fp.close() 581 582 def __del__(self): 583 "Stop httpd when the object is deleted" 584 self.stop() 585 586 def _quote(self, arg): 587 if ' ' in arg: 588 return '"' + arg + '"' 589 else: 590 return arg 591 592 def _create_users_file(self): 593 "Create users file" 594 htpasswd = os.path.join(self.httpd_dir, 'bin', 'htpasswd.exe') 595 # Create the cheapest to compare password form for our testsuite 596 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bcp', self.httpd_users, 597 'jrandom', 'rayjandom']) 598 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users, 599 'jconstant', 'rayjandom']) 600 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users, 601 'JRANDOM', 'rayjandom']) 602 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users, 603 'JCONSTANT', 'rayjandom']) 604 605 def _create_groups_file(self): 606 "Create groups for mod_authz_svn tests" 607 fp = open(self.httpd_groups, 'w') 608 fp.write('random: jrandom\n') 609 fp.write('constant: jconstant\n') 610 fp.close() 611 612 def _create_mime_types_file(self): 613 "Create empty mime.types file" 614 fp = open(self.httpd_mime_types, 'w') 615 fp.close() 616 617 def _create_dontdothat_file(self): 618 "Create empty mime.types file" 619 # If the tests have not previously been run or were cleaned 620 # up, then 'svn-test-work' does not exist yet. 621 parent_dir = os.path.dirname(self.dontdothat_file) 622 if not os.path.exists(parent_dir): 623 os.makedirs(parent_dir) 624 625 fp = open(self.dontdothat_file, 'w') 626 fp.write('[recursive-actions]\n') 627 fp.write('/ = deny\n') 628 fp.close() 629 630 def _sys_module(self, name, path): 631 full_path = os.path.join(self.httpd_dir, 'modules', path) 632 return 'LoadModule ' + name + " " + self._quote(full_path) + '\n' 633 634 def _svn_module(self, name, path): 635 full_path = os.path.join(self.abs_objdir, path) 636 return 'LoadModule ' + name + ' ' + self._quote(full_path) + '\n' 637 638 def _svn_repo(self, name): 639 path = os.path.join(self.abs_builddir, 640 CMDLINE_TEST_SCRIPT_NATIVE_PATH, 641 'svn-test-work', name) 642 location = '/svn-test-work/' + name 643 ddt_location = '/ddt-test-work/' + name 644 return \ 645 '<Location ' + location + '>\n' \ 646 ' DAV svn\n' \ 647 ' SVNParentPath ' + self._quote(path) + '\n' \ 648 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 649 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 650 ' SVNAllowBulkUpdates ' + self.bulkupdates_option + '\n' \ 651 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 652 ' AuthType Basic\n' \ 653 ' AuthName "Subversion Repository"\n' \ 654 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 655 ' Require valid-user\n' \ 656 '</Location>\n' \ 657 '<Location ' + ddt_location + '>\n' \ 658 ' DAV svn\n' \ 659 ' SVNParentPath ' + self._quote(path) + '\n' \ 660 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 661 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 662 ' SVNAllowBulkUpdates ' + self.bulkupdates_option + '\n' \ 663 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 664 ' AuthType Basic\n' \ 665 ' AuthName "Subversion Repository"\n' \ 666 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 667 ' Require valid-user\n' \ 668 ' DontDoThatConfigFile ' + self._quote(self.dontdothat_file) + '\n' \ 669 '</Location>\n' 670 671 def _svn_authz_repo(self): 672 local_tmp = os.path.join(self.abs_builddir, 673 CMDLINE_TEST_SCRIPT_NATIVE_PATH, 674 'svn-test-work', 'local_tmp') 675 return \ 676 '<Location /authz-test-work/anon>' + '\n' \ 677 ' DAV svn' + '\n' \ 678 ' SVNParentPath ' + local_tmp + '\n' \ 679 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 680 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 681 ' SVNListParentPath On' + '\n' \ 682 ' <IfModule mod_authz_core.c>' + '\n' \ 683 ' Require all granted' + '\n' \ 684 ' </IfModule>' + '\n' \ 685 ' <IfModule !mod_authz_core.c>' + '\n' \ 686 ' Allow from all' + '\n' \ 687 ' </IfModule>' + '\n' \ 688 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 689 '</Location>' + '\n' \ 690 '<Location /authz-test-work/mixed>' + '\n' \ 691 ' DAV svn' + '\n' \ 692 ' SVNParentPath ' + local_tmp + '\n' \ 693 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 694 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 695 ' SVNListParentPath On' + '\n' \ 696 ' AuthType Basic' + '\n' \ 697 ' AuthName "Subversion Repository"' + '\n' \ 698 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 699 ' Require valid-user' + '\n' \ 700 ' Satisfy Any' + '\n' \ 701 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 702 '</Location>' + '\n' \ 703 '<Location /authz-test-work/mixed-noauthwhenanon>' + '\n' \ 704 ' DAV svn' + '\n' \ 705 ' SVNParentPath ' + local_tmp + '\n' \ 706 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 707 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 708 ' SVNListParentPath On' + '\n' \ 709 ' AuthType Basic' + '\n' \ 710 ' AuthName "Subversion Repository"' + '\n' \ 711 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 712 ' Require valid-user' + '\n' \ 713 ' AuthzSVNNoAuthWhenAnonymousAllowed On' + '\n' \ 714 ' SVNPathAuthz On' + '\n' \ 715 '</Location>' + '\n' \ 716 '<Location /authz-test-work/authn>' + '\n' \ 717 ' DAV svn' + '\n' \ 718 ' SVNParentPath ' + local_tmp + '\n' \ 719 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 720 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 721 ' SVNListParentPath On' + '\n' \ 722 ' AuthType Basic' + '\n' \ 723 ' AuthName "Subversion Repository"' + '\n' \ 724 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 725 ' Require valid-user' + '\n' \ 726 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 727 '</Location>' + '\n' \ 728 '<Location /authz-test-work/authn-anonoff>' + '\n' \ 729 ' DAV svn' + '\n' \ 730 ' SVNParentPath ' + local_tmp + '\n' \ 731 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 732 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 733 ' SVNListParentPath On' + '\n' \ 734 ' AuthType Basic' + '\n' \ 735 ' AuthName "Subversion Repository"' + '\n' \ 736 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 737 ' Require valid-user' + '\n' \ 738 ' AuthzSVNAnonymous Off' + '\n' \ 739 ' SVNPathAuthz On' + '\n' \ 740 '</Location>' + '\n' \ 741 '<Location /authz-test-work/authn-lcuser>' + '\n' \ 742 ' DAV svn' + '\n' \ 743 ' SVNParentPath ' + local_tmp + '\n' \ 744 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 745 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 746 ' SVNListParentPath On' + '\n' \ 747 ' AuthType Basic' + '\n' \ 748 ' AuthName "Subversion Repository"' + '\n' \ 749 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 750 ' Require valid-user' + '\n' \ 751 ' AuthzForceUsernameCase Lower' + '\n' \ 752 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 753 '</Location>' + '\n' \ 754 '<Location /authz-test-work/authn-lcuser>' + '\n' \ 755 ' DAV svn' + '\n' \ 756 ' SVNParentPath ' + local_tmp + '\n' \ 757 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 758 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 759 ' SVNListParentPath On' + '\n' \ 760 ' AuthType Basic' + '\n' \ 761 ' AuthName "Subversion Repository"' + '\n' \ 762 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 763 ' Require valid-user' + '\n' \ 764 ' AuthzForceUsernameCase Lower' + '\n' \ 765 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 766 '</Location>' + '\n' \ 767 '<Location /authz-test-work/authn-group>' + '\n' \ 768 ' DAV svn' + '\n' \ 769 ' SVNParentPath ' + local_tmp + '\n' \ 770 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 771 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 772 ' SVNListParentPath On' + '\n' \ 773 ' AuthType Basic' + '\n' \ 774 ' AuthName "Subversion Repository"' + '\n' \ 775 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 776 ' AuthGroupFile ' + self._quote(self.httpd_groups) + '\n' \ 777 ' Require group random' + '\n' \ 778 ' AuthzSVNAuthoritative Off' + '\n' \ 779 ' SVNPathAuthz On' + '\n' \ 780 '</Location>' + '\n' \ 781 '<IfModule mod_authz_core.c>' + '\n' \ 782 '<Location /authz-test-work/sallrany>' + '\n' \ 783 ' DAV svn' + '\n' \ 784 ' SVNParentPath ' + local_tmp + '\n' \ 785 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 786 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 787 ' SVNListParentPath On' + '\n' \ 788 ' AuthType Basic' + '\n' \ 789 ' AuthName "Subversion Repository"' + '\n' \ 790 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 791 ' AuthzSendForbiddenOnFailure On' + '\n' \ 792 ' Satisfy All' + '\n' \ 793 ' <RequireAny>' + '\n' \ 794 ' Require valid-user' + '\n' \ 795 ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \ 796 ' </RequireAny>' + '\n' \ 797 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 798 '</Location>' + '\n' \ 799 '<Location /authz-test-work/sallrall>'+ '\n' \ 800 ' DAV svn' + '\n' \ 801 ' SVNParentPath ' + local_tmp + '\n' \ 802 ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ 803 ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ 804 ' SVNListParentPath On' + '\n' \ 805 ' AuthType Basic' + '\n' \ 806 ' AuthName "Subversion Repository"' + '\n' \ 807 ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ 808 ' AuthzSendForbiddenOnFailure On' + '\n' \ 809 ' Satisfy All' + '\n' \ 810 ' <RequireAll>' + '\n' \ 811 ' Require valid-user' + '\n' \ 812 ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \ 813 ' </RequireAll>' + '\n' \ 814 ' SVNPathAuthz ' + self.path_authz_option + '\n' \ 815 '</Location>' + '\n' \ 816 '</IfModule>' + '\n' \ 817 818 def start(self): 819 if self.service: 820 self._start_service() 821 else: 822 self._start_daemon() 823 824 def stop(self): 825 if self.service: 826 self._stop_service() 827 else: 828 self._stop_daemon() 829 830 def _start_service(self): 831 "Install and start HTTPD service" 832 print('Installing service %s' % self.service_name) 833 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'install']) 834 print('Starting service %s' % self.service_name) 835 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'start']) 836 837 def _stop_service(self): 838 "Stop and uninstall HTTPD service" 839 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'stop']) 840 os.spawnv(os.P_WAIT, self.path, self.httpd_args + ['-k', 'uninstall']) 841 842 def _start_daemon(self): 843 "Start HTTPD as daemon" 844 print('Starting httpd as daemon') 845 print(self.httpd_args) 846 try: 847 import win32process 848 import win32con 849 args = ' '.join([self._quote(x) for x in self.httpd_args]) 850 self.proc_handle = ( 851 win32process.CreateProcess(self._quote(self.path), args, 852 None, None, 0, 853 win32con.CREATE_NEW_CONSOLE, 854 None, None, win32process.STARTUPINFO()))[0] 855 except ImportError: 856 os.spawnv(os.P_NOWAIT, self.path, self.httpd_args) 857 858 def _stop_daemon(self): 859 "Stop the HTTPD daemon" 860 if self.proc_handle is not None: 861 try: 862 import win32process 863 print('Stopping %s' % self.name) 864 win32process.TerminateProcess(self.proc_handle, 0) 865 return 866 except ImportError: 867 pass 868 print('Httpd.stop_daemon not implemented') 869 870# Move the binaries to the test directory 871locate_libs() 872if create_dirs: 873 old_cwd = os.getcwd() 874 try: 875 os.chdir(abs_objdir) 876 baton = copied_execs 877 for dirpath, dirs, files in os.walk('subversion'): 878 copy_execs(baton, dirpath, files) 879 for dirpath, dirs, files in os.walk('tools/server-side'): 880 copy_execs(baton, dirpath, files) 881 except: 882 os.chdir(old_cwd) 883 raise 884 else: 885 os.chdir(old_cwd) 886 887# Create the base directory for Python tests 888create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH) 889 890# Ensure the tests directory is correctly cased 891abs_builddir = fix_case(abs_builddir) 892 893daemon = None 894# Run the tests 895 896# No need to start any servers if we are only listing the tests. 897if not list_tests: 898 if run_svnserve: 899 daemon = Svnserve(svnserve_args, objdir, abs_objdir, abs_builddir) 900 901 if run_httpd: 902 daemon = Httpd(abs_httpd_dir, abs_objdir, abs_builddir, httpd_port, 903 httpd_service, httpd_no_log, 904 advertise_httpv2, http_short_circuit, 905 http_bulk_updates) 906 907 # Start service daemon, if any 908 if daemon: 909 daemon.start() 910 911# Find the full path and filename of any test that is specified just by 912# its base name. 913if len(tests_to_run) != 0: 914 tests = [] 915 for t in tests_to_run: 916 tns = None 917 if '#' in t: 918 t, tns = t.split('#') 919 920 test = [x for x in all_tests if x.split('/')[-1] == t] 921 if not test and not (t.endswith('-test.exe') or t.endswith('_tests.py')): 922 # The lengths of '-test.exe' and of '_tests.py' are both 9. 923 test = [x for x in all_tests if x.split('/')[-1][:-9] == t] 924 925 if not test: 926 print("Skipping test '%s', test not found." % t) 927 elif tns: 928 tests.append('%s#%s' % (test[0], tns)) 929 else: 930 tests.extend(test) 931 932 tests_to_run = tests 933else: 934 tests_to_run = all_tests 935 936 937if list_tests: 938 print('Listing %s configuration on %s' % (objdir, repo_loc)) 939else: 940 print('Testing %s configuration on %s' % (objdir, repo_loc)) 941sys.path.insert(0, os.path.join(abs_srcdir, 'build')) 942 943if not test_javahl: 944 import run_tests 945 if log_to_stdout: 946 log_file = None 947 fail_log_file = None 948 else: 949 log_file = os.path.join(abs_builddir, log) 950 fail_log_file = os.path.join(abs_builddir, faillog) 951 952 if run_httpd: 953 httpd_version = "%.1f" % daemon.httpd_ver 954 else: 955 httpd_version = None 956 th = run_tests.TestHarness(abs_srcdir, abs_builddir, 957 log_file, 958 fail_log_file, 959 base_url, fs_type, 'serf', 960 server_minor_version, not quiet, 961 cleanup, enable_sasl, parallel, config_file, 962 fsfs_sharding, fsfs_packing, 963 list_tests, svn_bin, mode_filter, 964 milestone_filter, 965 httpd_version=httpd_version, 966 set_log_level=log_level, ssl_cert=ssl_cert) 967 old_cwd = os.getcwd() 968 try: 969 os.chdir(abs_builddir) 970 failed = th.run(tests_to_run) 971 except: 972 os.chdir(old_cwd) 973 raise 974 else: 975 os.chdir(old_cwd) 976else: 977 failed = False 978 args = ( 979 'java.exe', 980 '-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'), 981 '-Dtest.srcdir=' + os.path.join(abs_srcdir, 982 'subversion/bindings/javahl'), 983 '-Dtest.rooturl=', 984 '-Dtest.fstype=' + fs_type , 985 '-Dtest.tests=', 986 987 '-Djava.library.path=' 988 + os.path.join(abs_objdir, 989 'subversion/bindings/javahl/native'), 990 '-classpath', 991 os.path.join(abs_srcdir, 'subversion/bindings/javahl/classes') +';' + 992 gen_obj.junit_path 993 ) 994 995 sys.stderr.flush() 996 print('Running org.apache.subversion tests:') 997 sys.stdout.flush() 998 999 r = subprocess.call(args + tuple(['org.apache.subversion.javahl.RunTests'])) 1000 sys.stdout.flush() 1001 sys.stderr.flush() 1002 if (r != 0): 1003 print('[Test runner reported failure]') 1004 failed = True 1005 1006 print('Running org.tigris.subversion tests:') 1007 sys.stdout.flush() 1008 r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests'])) 1009 sys.stdout.flush() 1010 sys.stderr.flush() 1011 if (r != 0): 1012 print('[Test runner reported failure]') 1013 failed = True 1014 1015# Stop service daemon, if any 1016if daemon: 1017 del daemon 1018 1019# Remove the execs again 1020for tgt in copied_execs: 1021 try: 1022 if os.path.isfile(tgt): 1023 if verbose: 1024 print("kill: %s" % tgt) 1025 os.unlink(tgt) 1026 except: 1027 traceback.print_exc(file=sys.stdout) 1028 pass 1029 1030 1031if failed: 1032 sys.exit(1) 1033