]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/subversion/win-tests.py
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.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.8.x/win-tests.py $
28 # $LastChangedRevision: 1692801 $
29
30 import os, sys, subprocess
31 import filecmp
32 import shutil
33 import traceback
34 try:
35   # Python >=3.0
36   import configparser
37 except ImportError:
38   # Python <3.0
39   import ConfigParser as configparser
40 import string
41 import random
42
43 import getopt
44 try:
45     my_getopt = getopt.gnu_getopt
46 except AttributeError:
47     my_getopt = getopt.getopt
48
49 def _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
105 CMDLINE_TEST_SCRIPT_PATH = 'subversion/tests/cmdline/'
106 CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLINE_TEST_SCRIPT_PATH.replace('/', os.sep)
107
108 sys.path.insert(0, os.path.join('build', 'generator'))
109 sys.path.insert(1, 'build')
110
111 import gen_win
112 version_header = os.path.join('subversion', 'include', 'svn_version.h')
113 cp = configparser.ConfigParser()
114 cp.read('gen-make.opts')
115 gen_obj = gen_win.GeneratorBase('build.conf', version_header,
116                                 cp.items('options'))
117 all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \
118           + gen_obj.scripts + gen_obj.bdb_scripts
119 client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)]
120
121 svn_dlls = []
122 for 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
127 opts, 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='])
138 if len(args) > 1:
139   print('Warning: non-option arguments after the first one will be ignored')
140
141 # Interpret the options and set parameters
142 base_url, fs_type, verbose, quiet, cleanup = None, None, None, None, None
143 repo_loc = 'local repository.'
144 objdir = 'Debug'
145 log = 'tests.log'
146 faillog = 'fails.log'
147 run_svnserve = None
148 svnserve_args = None
149 run_httpd = None
150 httpd_port = None
151 httpd_service = None
152 httpd_no_log = None
153 http_short_circuit = False
154 advertise_httpv2 = True
155 http_bulk_updates = True
156 list_tests = None
157 milestone_filter = None
158 test_javahl = None
159 enable_sasl = None
160 svn_bin = None
161 parallel = None
162 fsfs_sharding = None
163 fsfs_packing = None
164 server_minor_version = None
165 config_file = None
166 log_to_stdout = None
167 mode_filter=None
168 tests_to_run = []
169 log_level = None
170 ssl_cert = None
171
172 for 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
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 # Don't run bdb tests if they want to test fsfs
258 if fs_type == 'fsfs':
259   all_tests = gen_obj.test_progs + gen_obj.scripts
260
261 if 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
267 if 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
283 copied_execs = []   # Store copied exec files to avoid the final dir scan
284
285 def 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
292 def 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
311 def 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
322 def 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
376 def 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
390 class 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
440 class 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
871 locate_libs()
872 if 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
888 create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH)
889
890 # Ensure the tests directory is correctly cased
891 abs_builddir = fix_case(abs_builddir)
892
893 daemon = None
894 # Run the tests
895
896 # No need to start any servers if we are only listing the tests.
897 if 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.
913 if 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
933 else:
934   tests_to_run = all_tests
935
936
937 if list_tests:
938   print('Listing %s configuration on %s' % (objdir, repo_loc))
939 else:
940   print('Testing %s configuration on %s' % (objdir, repo_loc))
941 sys.path.insert(0, os.path.join(abs_srcdir, 'build'))
942
943 if 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)
976 else:
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
1016 if daemon:
1017   del daemon
1018
1019 # Remove the execs again
1020 for 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
1031 if failed:
1032   sys.exit(1)