win-tests.py revision 322442
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: https://svn.apache.org/repos/asf/subversion/branches/1.9.x/win-tests.py $ 28# $LastChangedRevision: 1718291 $ 29 30import os, sys, subprocess 31import filecmp 32import shutil 33import traceback 34import logging 35try: 36 # Python >=3.0 37 import configparser 38except ImportError: 39 # Python <3.0 40 import ConfigParser as configparser 41import string 42import random 43 44import getopt 45try: 46 my_getopt = getopt.gnu_getopt 47except AttributeError: 48 my_getopt = getopt.getopt 49 50def _usage_exit(): 51 "print usage, exit the script" 52 53 print("Driver for running the tests on Windows.") 54 print("Usage: python win-tests.py [option] [test-path]") 55 print("") 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") 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(" --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") 97 print(" svnserve") 98 print(" -p, --parallel : run multiple tests in parallel") 99 print(" --server-minor-version : the minor version of the server being") 100 print(" tested") 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.") 106 107 sys.exit(0) 108 109CMDLINE_TEST_SCRIPT_PATH = 'subversion/tests/cmdline/' 110CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLINE_TEST_SCRIPT_PATH.replace('/', os.sep) 111 112sys.path.insert(0, os.path.join('build', 'generator')) 113sys.path.insert(1, 'build') 114 115import gen_win_dependencies 116import gen_base 117version_header = os.path.join('subversion', 'include', 'svn_version.h') 118cp = configparser.ConfigParser() 119cp.read('gen-make.opts') 120gen_obj = gen_win_dependencies.GenDependenciesBase('build.conf', version_header, 121 cp.items('options')) 122opts, 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=', 132 'ssl-cert=']) 133if len(args) > 1: 134 print('Warning: non-option arguments after the first one will be ignored') 135 136# Interpret the options and set parameters 137base_url, fs_type, verbose, cleanup = None, None, None, None 138repo_loc = 'local repository.' 139objdir = 'Debug' 140log = 'tests.log' 141faillog = 'fails.log' 142run_svnserve = None 143svnserve_args = None 144run_httpd = None 145httpd_port = None 146httpd_service = None 147httpd_no_log = None 148http_short_circuit = False 149advertise_httpv2 = True 150http_bulk_updates = True 151list_tests = None 152milestone_filter = None 153test_javahl = None 154test_swig = None 155enable_sasl = None 156svn_bin = None 157parallel = None 158fsfs_sharding = None 159fsfs_packing = None 160server_minor_version = None 161config_file = None 162log_to_stdout = None 163mode_filter=None 164tests_to_run = [] 165log_level = None 166ssl_cert = None 167 168for opt, val in opts: 169 if opt in ('-h', '--help'): 170 _usage_exit() 171 elif opt in ('-u', '--url'): 172 base_url = val 173 elif opt in ('-f', '--fs-type'): 174 fs_type = val 175 elif opt in ('-v', '--verbose'): 176 verbose = 1 177 log_level = logging.DEBUG 178 elif opt in ('-c', '--cleanup'): 179 cleanup = 1 180 elif opt in ('-t', '--test'): 181 tests_to_run.append(val) 182 elif opt in ['-r', '--release']: 183 objdir = 'Release' 184 elif opt in ['-d', '--debug']: 185 objdir = 'Debug' 186 elif opt == '--svnserve-args': 187 svnserve_args = val.split(',') 188 run_svnserve = 1 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) 193 run_httpd = 1 194 elif opt == '--httpd-port': 195 httpd_port = int(val) 196 elif opt == '--httpd-daemon': 197 httpd_service = 0 198 elif opt == '--httpd-service': 199 httpd_service = 1 200 elif opt == '--httpd-no-log': 201 httpd_no_log = 1 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': 211 fsfs_packing = 1 212 elif opt == '--javahl': 213 test_javahl = 1 214 elif opt == '--swig': 215 if val not in ['perl', 'python', 'ruby']: 216 sys.stderr.write('Running \'%s\' swig tests not supported (yet).\n' 217 % (val,)) 218 test_swig = val 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 = int(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 = getattr(logging, val, None) or int(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 257if fs_type == 'bdb': 258 all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \ 259 + gen_obj.scripts + gen_obj.bdb_scripts 260else: 261 all_tests = gen_obj.test_progs + gen_obj.scripts 262 263client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)] 264 265if run_httpd: 266 if not httpd_port: 267 httpd_port = random.randrange(1024, 30000) 268 if not base_url: 269 base_url = 'http://localhost:' + str(httpd_port) 270 271if base_url: 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' 279 run_svnserve = 1 280 else: 281 # Don't know this scheme, but who're we to judge whether it's 282 # correct or not? 283 log = 'url-tests.log' 284 faillog = 'url-fails.log' 285 286# Have to move the executables where the tests expect them to be 287copied_execs = [] # Store copied exec files to avoid the final dir scan 288 289def create_target_dir(dirname): 290 tgt_dir = os.path.join(abs_builddir, dirname) 291 if not os.path.exists(tgt_dir): 292 if verbose: 293 print("mkdir: %s" % tgt_dir) 294 os.makedirs(tgt_dir) 295 296def copy_changed_file(src, tgt=None, to_dir=None, cleanup=True): 297 if not os.path.isfile(src): 298 print('Could not find ' + src) 299 sys.exit(1) 300 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,)) 307 308 if os.path.exists(tgt): 309 assert os.path.isfile(tgt) 310 if filecmp.cmp(src, tgt): 311 if verbose: 312 print("same: %s" % src) 313 print(" and: %s" % tgt) 314 return 0 315 if verbose: 316 print("copy: %s" % src) 317 print(" to: %s" % tgt) 318 shutil.copy(src, tgt) 319 320 if cleanup: 321 copied_execs.append(tgt) 322 323def locate_libs(): 324 "Move DLLs to a known location and set env vars" 325 326 debug = (objdir == 'Debug') 327 328 for lib in gen_obj._libraries.values(): 329 330 if debug: 331 name, dir = lib.debug_dll_name, lib.debug_dll_dir 332 else: 333 name, dir = lib.dll_name, lib.dll_dir 334 335 if name and 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) 339 340 for name in lib.extra_bin: 341 src = os.path.join(dir, name) 342 copy_changed_file(src, to_dir=abs_builddir) 343 344 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, 351 cleanup=False) 352 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') 361 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) 365 366 os.environ['PATH'] = abs_builddir + os.pathsep + os.environ['PATH'] 367 368def fix_case(path): 369 path = os.path.normpath(path) 370 parts = path.split(os.path.sep) 371 drive = parts[0].upper() 372 parts = parts[1:] 373 path = drive + os.path.sep 374 for part in parts: 375 dirs = os.listdir(path) 376 for dir in dirs: 377 if dir.lower() == part.lower(): 378 path = os.path.join(path, dir) 379 break 380 return path 381 382class Svnserve: 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' 387 self.kind = objdir 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) 391 self.proc = None 392 393 def __del__(self): 394 "Stop svnserve when the object is deleted" 395 self.stop() 396 397 def _quote(self, arg): 398 if ' ' in arg: 399 return '"' + arg + '"' 400 else: 401 return arg 402 403 def start(self): 404 if not self.args: 405 args = [self.name, '-d', '-r', self.root] 406 else: 407 args = [self.name] + self.args 408 print('Starting %s %s' % (self.kind, self.name)) 409 410 self.proc = subprocess.Popen([self.path] + args[1:]) 411 412 def stop(self): 413 if self.proc is not None: 414 try: 415 print('Stopping %s' % self.name) 416 self.proc.poll(); 417 if self.proc.returncode is None: 418 self.proc.kill(); 419 return 420 except AttributeError: 421 pass 422 print('Svnserve.stop not implemented') 423 424class Httpd: 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 431 432 if httpv2: 433 self.httpv2_option = 'on' 434 else: 435 self.httpv2_option = 'off' 436 437 if bulk_updates: 438 self.bulkupdates_option = 'on' 439 else: 440 self.bulkupdates_option = 'off' 441 442 self.service = service 443 self.proc = None 444 self.path = os.path.join(self.httpd_dir, 'bin', self.name) 445 446 if short_circuit: 447 self.path_authz_option = 'short_circuit' 448 else: 449 self.path_authz_option = 'on' 450 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!") 456 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) 472 473 if self.service: 474 self.httpd_args = [self.name, '-n', self._quote(self.service_name), 475 '-f', self._quote(self.httpd_config)] 476 else: 477 self.httpd_args = [self.name, '-f', self._quote(self.httpd_config)] 478 479 create_target_dir(self.root_dir) 480 481 self._create_users_file() 482 self._create_groups_file() 483 self._create_mime_types_file() 484 self._create_dontdothat_file() 485 486 # Obtain version. 487 version_vals = gen_obj._libraries['httpd'].version.split('.') 488 self.httpd_ver = float('%s.%s' % (version_vals[0], version_vals[1])) 489 490 # Create httpd config file 491 fp = open(self.httpd_config, 'w') 492 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') 497 498 # Global Environment 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') 505 506 if not no_log: 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') 510 else: 511 fp.write('LogLevel Crit\n') 512 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')) 525 else: 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')) 530 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')) 534 535 # And for mod_dontdothat 536 fp.write(self._svn_module('dontdothat_module', 'mod_dontdothat.so')) 537 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') 543 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()) 548 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') 554 555 fp.write('TypesConfig ' + self._quote(self.httpd_mime_types) + '\n') 556 fp.write('HostNameLookups Off\n') 557 558 fp.close() 559 560 def __del__(self): 561 "Stop httpd when the object is deleted" 562 self.stop() 563 564 def _quote(self, arg): 565 if ' ' in arg: 566 return '"' + arg + '"' 567 else: 568 return arg 569 570 def _create_users_file(self): 571 "Create users file" 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']) 582 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') 588 fp.close() 589 590 def _create_mime_types_file(self): 591 "Create empty mime.types file" 592 fp = open(self.httpd_mime_types, 'w') 593 fp.close() 594 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) 602 603 fp = open(self.dontdothat_file, 'w') 604 fp.write('[recursive-actions]\n') 605 fp.write('/ = deny\n') 606 fp.close() 607 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' 611 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' 615 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 622 return \ 623 '<Location ' + location + '>\n' \ 624 ' DAV svn\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' \ 634 '</Location>\n' \ 635 '<Location ' + ddt_location + '>\n' \ 636 ' DAV svn\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' \ 647 '</Location>\n' 648 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') 653 return \ 654 '<Location /authz-test-work/anon>' + '\n' \ 655 ' DAV svn' + '\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' \ 669 ' DAV svn' + '\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' \ 682 ' DAV svn' + '\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' \ 695 ' DAV svn' + '\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' \ 707 ' DAV svn' + '\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' \ 720 ' DAV svn' + '\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' \ 733 ' DAV svn' + '\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' \ 746 ' DAV svn' + '\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' \ 761 ' DAV svn' + '\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' \ 778 ' DAV svn' + '\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' \ 795 796 def start(self): 797 if self.service: 798 self._start_service() 799 else: 800 self._start_daemon() 801 802 def stop(self): 803 if self.service: 804 self._stop_service() 805 else: 806 self._stop_daemon() 807 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']) 814 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']) 819 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:]) 825 826 def _stop_daemon(self): 827 "Stop the HTTPD daemon" 828 if self.proc is not None: 829 try: 830 print('Stopping %s' % self.name) 831 self.proc.poll(); 832 if self.proc.returncode is None: 833 self.proc.kill(); 834 return 835 except AttributeError: 836 pass 837 print('Httpd.stop_daemon not implemented') 838 839# Move the binaries to the test directory 840create_target_dir(abs_builddir) 841locate_libs() 842if create_dirs: 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) 846 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) 851 852# Create the base directory for Python tests 853create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH) 854 855# Ensure the tests directory is correctly cased 856abs_builddir = fix_case(abs_builddir) 857 858daemon = None 859# Run the tests 860 861# No need to start any servers if we are only listing the tests. 862if not list_tests: 863 if run_svnserve: 864 daemon = Svnserve(svnserve_args, objdir, abs_objdir, abs_builddir) 865 866 if run_httpd: 867 daemon = Httpd(abs_httpd_dir, abs_objdir, abs_builddir, httpd_port, 868 httpd_service, httpd_no_log, 869 advertise_httpv2, http_short_circuit, 870 http_bulk_updates) 871 872 # Start service daemon, if any 873 if daemon: 874 daemon.start() 875 876# Find the full path and filename of any test that is specified just by 877# its base name. 878if len(tests_to_run) != 0: 879 tests = [] 880 for t in tests_to_run: 881 tns = None 882 if '#' in t: 883 t, tns = t.split('#') 884 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] 889 890 if not test: 891 print("Skipping test '%s', test not found." % t) 892 elif tns: 893 tests.append('%s#%s' % (test[0], tns)) 894 else: 895 tests.extend(test) 896 897 tests_to_run = tests 898else: 899 tests_to_run = all_tests 900 901 902if list_tests: 903 print('Listing %s configuration on %s' % (objdir, repo_loc)) 904else: 905 print('Testing %s configuration on %s' % (objdir, repo_loc)) 906sys.path.insert(0, os.path.join(abs_srcdir, 'build')) 907 908if not test_javahl and not test_swig: 909 import run_tests 910 if log_to_stdout: 911 log_file = None 912 fail_log_file = None 913 else: 914 log_file = os.path.join(abs_builddir, log) 915 fail_log_file = os.path.join(abs_builddir, faillog) 916 917 if run_httpd: 918 httpd_version = gen_obj._libraries['httpd'].version 919 else: 920 httpd_version = None 921 922 opts, args = run_tests.create_parser().parse_args([]) 923 opts.url = base_url 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() 943 try: 944 os.chdir(abs_builddir) 945 failed = th.run(tests_to_run) 946 except: 947 os.chdir(old_cwd) 948 raise 949 else: 950 os.chdir(old_cwd) 951elif test_javahl: 952 failed = False 953 954 java_exe = None 955 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') 959 break 960 961 if not java_exe and 'java_sdk' in gen_obj._libraries: 962 jdk = gen_obj._libraries['java_sdk'] 963 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') 966 967 if not java_exe: 968 print('Java not found. Skipping Java tests') 969 else: 970 args = (os.path.abspath(java_exe),) 971 if (objdir == 'Debug'): 972 args = args + ('-Xcheck:jni',) 973 974 args = args + ( 975 '-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'), 976 '-Dtest.srcdir=' + os.path.join(abs_srcdir, 977 'subversion/bindings/javahl'), 978 '-Dtest.rooturl=', 979 '-Dtest.fstype=' + fs_type , 980 '-Dtest.tests=', 981 982 '-Djava.library.path=' 983 + os.path.join(abs_objdir, 984 'subversion/bindings/javahl/native'), 985 '-classpath', 986 os.path.join(abs_srcdir, 'subversion/bindings/javahl/classes') +';' + 987 gen_obj.junit_path 988 ) 989 990 sys.stderr.flush() 991 print('Running org.apache.subversion tests:') 992 sys.stdout.flush() 993 994 r = subprocess.call(args + tuple(['org.apache.subversion.javahl.RunTests'])) 995 sys.stdout.flush() 996 sys.stderr.flush() 997 if (r != 0): 998 print('[Test runner reported failure]') 999 failed = True 1000 1001 print('Running org.tigris.subversion tests:') 1002 sys.stdout.flush() 1003 r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests'])) 1004 sys.stdout.flush() 1005 sys.stderr.flush() 1006 if (r != 0): 1007 print('[Test runner reported failure]') 1008 failed = True 1009elif test_swig == 'perl': 1010 failed = False 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') 1015 1016 create_target_dir(swig_pl_svn) 1017 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) 1023 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) 1027 1028 pm_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'perl', 1029 'native') 1030 1031 tests = [] 1032 1033 for root, dirs, files in os.walk(pm_src): 1034 for name in files: 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)) 1040 1041 perl5lib = swig_pl_dir 1042 if 'PERL5LIB' in os.environ: 1043 perl5lib += os.pathsep + os.environ['PERL5LIB'] 1044 1045 perl_exe = 'perl.exe' 1046 1047 print('-- Running Swig Perl tests --') 1048 sys.stdout.flush() 1049 old_cwd = os.getcwd() 1050 try: 1051 os.chdir(pm_src) 1052 1053 os.environ['PERL5LIB'] = perl5lib 1054 os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES' 1055 1056 r = subprocess.call([ 1057 perl_exe, 1058 '-MExtUtils::Command::MM', 1059 '-e', 'test_harness()' 1060 ] + tests) 1061 finally: 1062 os.chdir(old_cwd) 1063 1064 if (r != 0): 1065 print('[Test runner reported failure]') 1066 failed = True 1067elif test_swig == 'python': 1068 failed = False 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') 1073 1074 create_target_dir(swig_py_libsvn) 1075 create_target_dir(swig_py_svn) 1076 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': 1080 1081 src = os.path.join(abs_objdir, i.filename) 1082 copy_changed_file(src, to_dir=swig_py_libsvn) 1083 1084 py_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'python') 1085 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) 1090 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), 1095 to_dir=swig_py_svn) 1096 1097 print('-- Running Swig Python tests --') 1098 sys.stdout.flush() 1099 1100 pythonpath = swig_py_dir 1101 if 'PYTHONPATH' in os.environ: 1102 pythonpath += os.pathsep + os.environ['PYTHONPATH'] 1103 1104 python_exe = 'python.exe' 1105 old_cwd = os.getcwd() 1106 try: 1107 os.environ['PYTHONPATH'] = pythonpath 1108 1109 r = subprocess.call([ 1110 python_exe, 1111 os.path.join(py_src, 'tests', 'run_all.py') 1112 ]) 1113 finally: 1114 os.chdir(old_cwd) 1115 1116 if (r != 0): 1117 print('[Test runner reported failure]') 1118 failed = True 1119 1120elif test_swig == 'ruby': 1121 failed = False 1122 1123 if 'ruby' not in gen_obj._libraries: 1124 print('Ruby not found. Skipping Ruby tests') 1125 else: 1126 ruby_lib = gen_obj._libraries['ruby'] 1127 1128 ruby_exe = 'ruby.exe' 1129 ruby_subdir = os.path.join('subversion', 'bindings', 'swig', 'ruby') 1130 ruby_args = [ 1131 '-I', os.path.join(abs_srcdir, ruby_subdir), 1132 os.path.join(abs_srcdir, ruby_subdir, 'test', 'run-test.rb'), 1133 '--verbose' 1134 ] 1135 1136 print('-- Running Swig Ruby tests --') 1137 sys.stdout.flush() 1138 old_cwd = os.getcwd() 1139 try: 1140 os.chdir(ruby_subdir) 1141 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) 1145 finally: 1146 os.chdir(old_cwd) 1147 1148 sys.stdout.flush() 1149 sys.stderr.flush() 1150 if (r != 0): 1151 print('[Test runner reported failure]') 1152 failed = True 1153 1154# Stop service daemon, if any 1155if daemon: 1156 del daemon 1157 1158# Remove the execs again 1159for tgt in copied_execs: 1160 try: 1161 if os.path.isfile(tgt): 1162 if verbose: 1163 print("kill: %s" % tgt) 1164 os.unlink(tgt) 1165 except: 1166 traceback.print_exc(file=sys.stdout) 1167 pass 1168 1169 1170if failed: 1171 sys.exit(1) 1172