]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/win-tests.py
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / subversion / win-tests.py
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 """
22 Driver for running the tests on Windows.
23
24 For a list of options, run this script with the --help option.
25 """
26
27 # $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.9.x/win-tests.py $
28 # $LastChangedRevision: 1718291 $
29
30 import os, sys, subprocess
31 import filecmp
32 import shutil
33 import traceback
34 import logging
35 try:
36   # Python >=3.0
37   import configparser
38 except ImportError:
39   # Python <3.0
40   import ConfigParser as configparser
41 import string
42 import random
43
44 import getopt
45 try:
46     my_getopt = getopt.gnu_getopt
47 except AttributeError:
48     my_getopt = getopt.getopt
49
50 def _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
109 CMDLINE_TEST_SCRIPT_PATH = 'subversion/tests/cmdline/'
110 CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLINE_TEST_SCRIPT_PATH.replace('/', os.sep)
111
112 sys.path.insert(0, os.path.join('build', 'generator'))
113 sys.path.insert(1, 'build')
114
115 import gen_win_dependencies
116 import gen_base
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,
121                                                    cp.items('options'))
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=',
132                         'ssl-cert='])
133 if len(args) > 1:
134   print('Warning: non-option arguments after the first one will be ignored')
135
136 # Interpret the options and set parameters
137 base_url, fs_type, verbose, cleanup = None, None, None, None
138 repo_loc = 'local repository.'
139 objdir = 'Debug'
140 log = 'tests.log'
141 faillog = 'fails.log'
142 run_svnserve = None
143 svnserve_args = None
144 run_httpd = None
145 httpd_port = None
146 httpd_service = None
147 httpd_no_log = None
148 http_short_circuit = False
149 advertise_httpv2 = True
150 http_bulk_updates = True
151 list_tests = None
152 milestone_filter = None
153 test_javahl = None
154 test_swig = None
155 enable_sasl = None
156 svn_bin = None
157 parallel = None
158 fsfs_sharding = None
159 fsfs_packing = None
160 server_minor_version = None
161 config_file = None
162 log_to_stdout = None
163 mode_filter=None
164 tests_to_run = []
165 log_level = None
166 ssl_cert = None
167
168 for 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
244 abs_srcdir = os.path.abspath("")
245 abs_objdir = os.path.join(abs_srcdir, objdir)
246 if len(args) == 0:
247   abs_builddir = abs_objdir
248   create_dirs = 0
249 else:
250   abs_builddir = os.path.abspath(args[0])
251   create_dirs = 1
252
253 # Default to fsfs explicitly
254 if not fs_type:
255   fs_type = 'fsfs'
256
257 if fs_type == 'bdb':
258   all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \
259             + gen_obj.scripts + gen_obj.bdb_scripts
260 else:
261   all_tests = gen_obj.test_progs + gen_obj.scripts
262
263 client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)]
264
265 if 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
271 if 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
287 copied_execs = []   # Store copied exec files to avoid the final dir scan
288
289 def 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
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)
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
323 def 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
368 def 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
382 class 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
424 class 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
840 create_target_dir(abs_builddir)
841 locate_libs()
842 if 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
853 create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH)
854
855 # Ensure the tests directory is correctly cased
856 abs_builddir = fix_case(abs_builddir)
857
858 daemon = None
859 # Run the tests
860
861 # No need to start any servers if we are only listing the tests.
862 if 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.
878 if 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
898 else:
899   tests_to_run = all_tests
900
901
902 if list_tests:
903   print('Listing %s configuration on %s' % (objdir, repo_loc))
904 else:
905   print('Testing %s configuration on %s' % (objdir, repo_loc))
906 sys.path.insert(0, os.path.join(abs_srcdir, 'build'))
907
908 if 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)
951 elif 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
1009 elif 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
1067 elif 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
1120 elif 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
1155 if daemon:
1156   del daemon
1157
1158 # Remove the execs again
1159 for 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
1170 if failed:
1171   sys.exit(1)