##// END OF EJS Templates
run-tests: add flag to provide extra hgrc options for test runs
Augie Fackler -
r14134:8468ec11 default
parent child Browse files
Show More
@@ -1,1151 +1,1159 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # run-tests.py - Run a set of tests on Mercurial
3 # run-tests.py - Run a set of tests on Mercurial
4 #
4 #
5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 # Modifying this script is tricky because it has many modes:
10 # Modifying this script is tricky because it has many modes:
11 # - serial (default) vs parallel (-jN, N > 1)
11 # - serial (default) vs parallel (-jN, N > 1)
12 # - no coverage (default) vs coverage (-c, -C, -s)
12 # - no coverage (default) vs coverage (-c, -C, -s)
13 # - temp install (default) vs specific hg script (--with-hg, --local)
13 # - temp install (default) vs specific hg script (--with-hg, --local)
14 # - tests are a mix of shell scripts and Python scripts
14 # - tests are a mix of shell scripts and Python scripts
15 #
15 #
16 # If you change this script, it is recommended that you ensure you
16 # If you change this script, it is recommended that you ensure you
17 # haven't broken it by running it in various modes with a representative
17 # haven't broken it by running it in various modes with a representative
18 # sample of test scripts. For example:
18 # sample of test scripts. For example:
19 #
19 #
20 # 1) serial, no coverage, temp install:
20 # 1) serial, no coverage, temp install:
21 # ./run-tests.py test-s*
21 # ./run-tests.py test-s*
22 # 2) serial, no coverage, local hg:
22 # 2) serial, no coverage, local hg:
23 # ./run-tests.py --local test-s*
23 # ./run-tests.py --local test-s*
24 # 3) serial, coverage, temp install:
24 # 3) serial, coverage, temp install:
25 # ./run-tests.py -c test-s*
25 # ./run-tests.py -c test-s*
26 # 4) serial, coverage, local hg:
26 # 4) serial, coverage, local hg:
27 # ./run-tests.py -c --local test-s* # unsupported
27 # ./run-tests.py -c --local test-s* # unsupported
28 # 5) parallel, no coverage, temp install:
28 # 5) parallel, no coverage, temp install:
29 # ./run-tests.py -j2 test-s*
29 # ./run-tests.py -j2 test-s*
30 # 6) parallel, no coverage, local hg:
30 # 6) parallel, no coverage, local hg:
31 # ./run-tests.py -j2 --local test-s*
31 # ./run-tests.py -j2 --local test-s*
32 # 7) parallel, coverage, temp install:
32 # 7) parallel, coverage, temp install:
33 # ./run-tests.py -j2 -c test-s* # currently broken
33 # ./run-tests.py -j2 -c test-s* # currently broken
34 # 8) parallel, coverage, local install:
34 # 8) parallel, coverage, local install:
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
36 # 9) parallel, custom tmp dir:
36 # 9) parallel, custom tmp dir:
37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
38 #
38 #
39 # (You could use any subset of the tests: test-s* happens to match
39 # (You could use any subset of the tests: test-s* happens to match
40 # enough that it's worth doing parallel runs, few enough that it
40 # enough that it's worth doing parallel runs, few enough that it
41 # completes fairly quickly, includes both shell and Python scripts, and
41 # completes fairly quickly, includes both shell and Python scripts, and
42 # includes some scripts that run daemon processes.)
42 # includes some scripts that run daemon processes.)
43
43
44 from distutils import version
44 from distutils import version
45 import difflib
45 import difflib
46 import errno
46 import errno
47 import optparse
47 import optparse
48 import os
48 import os
49 import shutil
49 import shutil
50 import subprocess
50 import subprocess
51 import signal
51 import signal
52 import sys
52 import sys
53 import tempfile
53 import tempfile
54 import time
54 import time
55 import re
55 import re
56 import threading
56 import threading
57
57
58 processlock = threading.Lock()
58 processlock = threading.Lock()
59
59
60 closefds = os.name == 'posix'
60 closefds = os.name == 'posix'
61 def Popen4(cmd, wd, timeout):
61 def Popen4(cmd, wd, timeout):
62 processlock.acquire()
62 processlock.acquire()
63 orig = os.getcwd()
63 orig = os.getcwd()
64 os.chdir(wd)
64 os.chdir(wd)
65 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
65 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
66 close_fds=closefds,
66 close_fds=closefds,
67 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
67 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
68 stderr=subprocess.STDOUT)
68 stderr=subprocess.STDOUT)
69 os.chdir(orig)
69 os.chdir(orig)
70 processlock.release()
70 processlock.release()
71
71
72 p.fromchild = p.stdout
72 p.fromchild = p.stdout
73 p.tochild = p.stdin
73 p.tochild = p.stdin
74 p.childerr = p.stderr
74 p.childerr = p.stderr
75
75
76 if timeout:
76 if timeout:
77 p.timeout = False
77 p.timeout = False
78 def t():
78 def t():
79 start = time.time()
79 start = time.time()
80 while time.time() - start < timeout and p.returncode is None:
80 while time.time() - start < timeout and p.returncode is None:
81 time.sleep(1)
81 time.sleep(1)
82 p.timeout = True
82 p.timeout = True
83 if p.returncode is None:
83 if p.returncode is None:
84 try:
84 try:
85 p.terminate()
85 p.terminate()
86 except OSError:
86 except OSError:
87 pass
87 pass
88 threading.Thread(target=t).start()
88 threading.Thread(target=t).start()
89
89
90 return p
90 return p
91
91
92 # reserved exit code to skip test (used by hghave)
92 # reserved exit code to skip test (used by hghave)
93 SKIPPED_STATUS = 80
93 SKIPPED_STATUS = 80
94 SKIPPED_PREFIX = 'skipped: '
94 SKIPPED_PREFIX = 'skipped: '
95 FAILED_PREFIX = 'hghave check failed: '
95 FAILED_PREFIX = 'hghave check failed: '
96 PYTHON = sys.executable
96 PYTHON = sys.executable
97 IMPL_PATH = 'PYTHONPATH'
97 IMPL_PATH = 'PYTHONPATH'
98 if 'java' in sys.platform:
98 if 'java' in sys.platform:
99 IMPL_PATH = 'JYTHONPATH'
99 IMPL_PATH = 'JYTHONPATH'
100
100
101 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
101 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
102
102
103 defaults = {
103 defaults = {
104 'jobs': ('HGTEST_JOBS', 1),
104 'jobs': ('HGTEST_JOBS', 1),
105 'timeout': ('HGTEST_TIMEOUT', 180),
105 'timeout': ('HGTEST_TIMEOUT', 180),
106 'port': ('HGTEST_PORT', 20059),
106 'port': ('HGTEST_PORT', 20059),
107 }
107 }
108
108
109 def parseargs():
109 def parseargs():
110 parser = optparse.OptionParser("%prog [options] [tests]")
110 parser = optparse.OptionParser("%prog [options] [tests]")
111
111
112 # keep these sorted
112 # keep these sorted
113 parser.add_option("--blacklist", action="append",
113 parser.add_option("--blacklist", action="append",
114 help="skip tests listed in the specified blacklist file")
114 help="skip tests listed in the specified blacklist file")
115 parser.add_option("-C", "--annotate", action="store_true",
115 parser.add_option("-C", "--annotate", action="store_true",
116 help="output files annotated with coverage")
116 help="output files annotated with coverage")
117 parser.add_option("--child", type="int",
117 parser.add_option("--child", type="int",
118 help="run as child process, summary to given fd")
118 help="run as child process, summary to given fd")
119 parser.add_option("-c", "--cover", action="store_true",
119 parser.add_option("-c", "--cover", action="store_true",
120 help="print a test coverage report")
120 help="print a test coverage report")
121 parser.add_option("-d", "--debug", action="store_true",
121 parser.add_option("-d", "--debug", action="store_true",
122 help="debug mode: write output of test scripts to console"
122 help="debug mode: write output of test scripts to console"
123 " rather than capturing and diff'ing it (disables timeout)")
123 " rather than capturing and diff'ing it (disables timeout)")
124 parser.add_option("-f", "--first", action="store_true",
124 parser.add_option("-f", "--first", action="store_true",
125 help="exit on the first test failure")
125 help="exit on the first test failure")
126 parser.add_option("--inotify", action="store_true",
126 parser.add_option("--inotify", action="store_true",
127 help="enable inotify extension when running tests")
127 help="enable inotify extension when running tests")
128 parser.add_option("-i", "--interactive", action="store_true",
128 parser.add_option("-i", "--interactive", action="store_true",
129 help="prompt to accept changed output")
129 help="prompt to accept changed output")
130 parser.add_option("-j", "--jobs", type="int",
130 parser.add_option("-j", "--jobs", type="int",
131 help="number of jobs to run in parallel"
131 help="number of jobs to run in parallel"
132 " (default: $%s or %d)" % defaults['jobs'])
132 " (default: $%s or %d)" % defaults['jobs'])
133 parser.add_option("--keep-tmpdir", action="store_true",
133 parser.add_option("--keep-tmpdir", action="store_true",
134 help="keep temporary directory after running tests")
134 help="keep temporary directory after running tests")
135 parser.add_option("-k", "--keywords",
135 parser.add_option("-k", "--keywords",
136 help="run tests matching keywords")
136 help="run tests matching keywords")
137 parser.add_option("-l", "--local", action="store_true",
137 parser.add_option("-l", "--local", action="store_true",
138 help="shortcut for --with-hg=<testdir>/../hg")
138 help="shortcut for --with-hg=<testdir>/../hg")
139 parser.add_option("-n", "--nodiff", action="store_true",
139 parser.add_option("-n", "--nodiff", action="store_true",
140 help="skip showing test changes")
140 help="skip showing test changes")
141 parser.add_option("-p", "--port", type="int",
141 parser.add_option("-p", "--port", type="int",
142 help="port on which servers should listen"
142 help="port on which servers should listen"
143 " (default: $%s or %d)" % defaults['port'])
143 " (default: $%s or %d)" % defaults['port'])
144 parser.add_option("--pure", action="store_true",
144 parser.add_option("--pure", action="store_true",
145 help="use pure Python code instead of C extensions")
145 help="use pure Python code instead of C extensions")
146 parser.add_option("-R", "--restart", action="store_true",
146 parser.add_option("-R", "--restart", action="store_true",
147 help="restart at last error")
147 help="restart at last error")
148 parser.add_option("-r", "--retest", action="store_true",
148 parser.add_option("-r", "--retest", action="store_true",
149 help="retest failed tests")
149 help="retest failed tests")
150 parser.add_option("-S", "--noskips", action="store_true",
150 parser.add_option("-S", "--noskips", action="store_true",
151 help="don't report skip tests verbosely")
151 help="don't report skip tests verbosely")
152 parser.add_option("-t", "--timeout", type="int",
152 parser.add_option("-t", "--timeout", type="int",
153 help="kill errant tests after TIMEOUT seconds"
153 help="kill errant tests after TIMEOUT seconds"
154 " (default: $%s or %d)" % defaults['timeout'])
154 " (default: $%s or %d)" % defaults['timeout'])
155 parser.add_option("--tmpdir", type="string",
155 parser.add_option("--tmpdir", type="string",
156 help="run tests in the given temporary directory"
156 help="run tests in the given temporary directory"
157 " (implies --keep-tmpdir)")
157 " (implies --keep-tmpdir)")
158 parser.add_option("-v", "--verbose", action="store_true",
158 parser.add_option("-v", "--verbose", action="store_true",
159 help="output verbose messages")
159 help="output verbose messages")
160 parser.add_option("--view", type="string",
160 parser.add_option("--view", type="string",
161 help="external diff viewer")
161 help="external diff viewer")
162 parser.add_option("--with-hg", type="string",
162 parser.add_option("--with-hg", type="string",
163 metavar="HG",
163 metavar="HG",
164 help="test using specified hg script rather than a "
164 help="test using specified hg script rather than a "
165 "temporary installation")
165 "temporary installation")
166 parser.add_option("-3", "--py3k-warnings", action="store_true",
166 parser.add_option("-3", "--py3k-warnings", action="store_true",
167 help="enable Py3k warnings on Python 2.6+")
167 help="enable Py3k warnings on Python 2.6+")
168 parser.add_option('--extra-config-opt', action="append",
169 help='set the given config opt in the test hgrc')
168
170
169 for option, default in defaults.items():
171 for option, default in defaults.items():
170 defaults[option] = int(os.environ.get(*default))
172 defaults[option] = int(os.environ.get(*default))
171 parser.set_defaults(**defaults)
173 parser.set_defaults(**defaults)
172 (options, args) = parser.parse_args()
174 (options, args) = parser.parse_args()
173
175
174 # jython is always pure
176 # jython is always pure
175 if 'java' in sys.platform or '__pypy__' in sys.modules:
177 if 'java' in sys.platform or '__pypy__' in sys.modules:
176 options.pure = True
178 options.pure = True
177
179
178 if options.with_hg:
180 if options.with_hg:
179 if not (os.path.isfile(options.with_hg) and
181 if not (os.path.isfile(options.with_hg) and
180 os.access(options.with_hg, os.X_OK)):
182 os.access(options.with_hg, os.X_OK)):
181 parser.error('--with-hg must specify an executable hg script')
183 parser.error('--with-hg must specify an executable hg script')
182 if not os.path.basename(options.with_hg) == 'hg':
184 if not os.path.basename(options.with_hg) == 'hg':
183 sys.stderr.write('warning: --with-hg should specify an hg script')
185 sys.stderr.write('warning: --with-hg should specify an hg script')
184 if options.local:
186 if options.local:
185 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
187 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
186 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
188 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
187 if not os.access(hgbin, os.X_OK):
189 if not os.access(hgbin, os.X_OK):
188 parser.error('--local specified, but %r not found or not executable'
190 parser.error('--local specified, but %r not found or not executable'
189 % hgbin)
191 % hgbin)
190 options.with_hg = hgbin
192 options.with_hg = hgbin
191
193
192 options.anycoverage = options.cover or options.annotate
194 options.anycoverage = options.cover or options.annotate
193 if options.anycoverage:
195 if options.anycoverage:
194 try:
196 try:
195 import coverage
197 import coverage
196 covver = version.StrictVersion(coverage.__version__).version
198 covver = version.StrictVersion(coverage.__version__).version
197 if covver < (3, 3):
199 if covver < (3, 3):
198 parser.error('coverage options require coverage 3.3 or later')
200 parser.error('coverage options require coverage 3.3 or later')
199 except ImportError:
201 except ImportError:
200 parser.error('coverage options now require the coverage package')
202 parser.error('coverage options now require the coverage package')
201
203
202 if options.anycoverage and options.local:
204 if options.anycoverage and options.local:
203 # this needs some path mangling somewhere, I guess
205 # this needs some path mangling somewhere, I guess
204 parser.error("sorry, coverage options do not work when --local "
206 parser.error("sorry, coverage options do not work when --local "
205 "is specified")
207 "is specified")
206
208
207 global vlog
209 global vlog
208 if options.verbose:
210 if options.verbose:
209 if options.jobs > 1 or options.child is not None:
211 if options.jobs > 1 or options.child is not None:
210 pid = "[%d]" % os.getpid()
212 pid = "[%d]" % os.getpid()
211 else:
213 else:
212 pid = None
214 pid = None
213 def vlog(*msg):
215 def vlog(*msg):
214 iolock.acquire()
216 iolock.acquire()
215 if pid:
217 if pid:
216 print pid,
218 print pid,
217 for m in msg:
219 for m in msg:
218 print m,
220 print m,
219 print
221 print
220 sys.stdout.flush()
222 sys.stdout.flush()
221 iolock.release()
223 iolock.release()
222 else:
224 else:
223 vlog = lambda *msg: None
225 vlog = lambda *msg: None
224
226
225 if options.tmpdir:
227 if options.tmpdir:
226 options.tmpdir = os.path.expanduser(options.tmpdir)
228 options.tmpdir = os.path.expanduser(options.tmpdir)
227
229
228 if options.jobs < 1:
230 if options.jobs < 1:
229 parser.error('--jobs must be positive')
231 parser.error('--jobs must be positive')
230 if options.interactive and options.jobs > 1:
232 if options.interactive and options.jobs > 1:
231 print '(--interactive overrides --jobs)'
233 print '(--interactive overrides --jobs)'
232 options.jobs = 1
234 options.jobs = 1
233 if options.interactive and options.debug:
235 if options.interactive and options.debug:
234 parser.error("-i/--interactive and -d/--debug are incompatible")
236 parser.error("-i/--interactive and -d/--debug are incompatible")
235 if options.debug:
237 if options.debug:
236 if options.timeout != defaults['timeout']:
238 if options.timeout != defaults['timeout']:
237 sys.stderr.write(
239 sys.stderr.write(
238 'warning: --timeout option ignored with --debug\n')
240 'warning: --timeout option ignored with --debug\n')
239 options.timeout = 0
241 options.timeout = 0
240 if options.py3k_warnings:
242 if options.py3k_warnings:
241 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
243 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
242 parser.error('--py3k-warnings can only be used on Python 2.6+')
244 parser.error('--py3k-warnings can only be used on Python 2.6+')
243 if options.blacklist:
245 if options.blacklist:
244 blacklist = dict()
246 blacklist = dict()
245 for filename in options.blacklist:
247 for filename in options.blacklist:
246 try:
248 try:
247 path = os.path.expanduser(os.path.expandvars(filename))
249 path = os.path.expanduser(os.path.expandvars(filename))
248 f = open(path, "r")
250 f = open(path, "r")
249 except IOError, err:
251 except IOError, err:
250 if err.errno != errno.ENOENT:
252 if err.errno != errno.ENOENT:
251 raise
253 raise
252 print "warning: no such blacklist file: %s" % filename
254 print "warning: no such blacklist file: %s" % filename
253 continue
255 continue
254
256
255 for line in f.readlines():
257 for line in f.readlines():
256 line = line.split('#', 1)[0].strip()
258 line = line.split('#', 1)[0].strip()
257 if line:
259 if line:
258 blacklist[line] = filename
260 blacklist[line] = filename
259
261
260 f.close()
262 f.close()
261
263
262 options.blacklist = blacklist
264 options.blacklist = blacklist
263
265
264 return (options, args)
266 return (options, args)
265
267
266 def rename(src, dst):
268 def rename(src, dst):
267 """Like os.rename(), trade atomicity and opened files friendliness
269 """Like os.rename(), trade atomicity and opened files friendliness
268 for existing destination support.
270 for existing destination support.
269 """
271 """
270 shutil.copy(src, dst)
272 shutil.copy(src, dst)
271 os.remove(src)
273 os.remove(src)
272
274
273 def splitnewlines(text):
275 def splitnewlines(text):
274 '''like str.splitlines, but only split on newlines.
276 '''like str.splitlines, but only split on newlines.
275 keep line endings.'''
277 keep line endings.'''
276 i = 0
278 i = 0
277 lines = []
279 lines = []
278 while True:
280 while True:
279 n = text.find('\n', i)
281 n = text.find('\n', i)
280 if n == -1:
282 if n == -1:
281 last = text[i:]
283 last = text[i:]
282 if last:
284 if last:
283 lines.append(last)
285 lines.append(last)
284 return lines
286 return lines
285 lines.append(text[i:n + 1])
287 lines.append(text[i:n + 1])
286 i = n + 1
288 i = n + 1
287
289
288 def parsehghaveoutput(lines):
290 def parsehghaveoutput(lines):
289 '''Parse hghave log lines.
291 '''Parse hghave log lines.
290 Return tuple of lists (missing, failed):
292 Return tuple of lists (missing, failed):
291 * the missing/unknown features
293 * the missing/unknown features
292 * the features for which existence check failed'''
294 * the features for which existence check failed'''
293 missing = []
295 missing = []
294 failed = []
296 failed = []
295 for line in lines:
297 for line in lines:
296 if line.startswith(SKIPPED_PREFIX):
298 if line.startswith(SKIPPED_PREFIX):
297 line = line.splitlines()[0]
299 line = line.splitlines()[0]
298 missing.append(line[len(SKIPPED_PREFIX):])
300 missing.append(line[len(SKIPPED_PREFIX):])
299 elif line.startswith(FAILED_PREFIX):
301 elif line.startswith(FAILED_PREFIX):
300 line = line.splitlines()[0]
302 line = line.splitlines()[0]
301 failed.append(line[len(FAILED_PREFIX):])
303 failed.append(line[len(FAILED_PREFIX):])
302
304
303 return missing, failed
305 return missing, failed
304
306
305 def showdiff(expected, output, ref, err):
307 def showdiff(expected, output, ref, err):
306 print
308 print
307 for line in difflib.unified_diff(expected, output, ref, err):
309 for line in difflib.unified_diff(expected, output, ref, err):
308 sys.stdout.write(line)
310 sys.stdout.write(line)
309
311
310 def findprogram(program):
312 def findprogram(program):
311 """Search PATH for a executable program"""
313 """Search PATH for a executable program"""
312 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
314 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
313 name = os.path.join(p, program)
315 name = os.path.join(p, program)
314 if os.access(name, os.X_OK):
316 if os.access(name, os.X_OK):
315 return name
317 return name
316 return None
318 return None
317
319
318 def checktools():
320 def checktools():
319 # Before we go any further, check for pre-requisite tools
321 # Before we go any further, check for pre-requisite tools
320 # stuff from coreutils (cat, rm, etc) are not tested
322 # stuff from coreutils (cat, rm, etc) are not tested
321 for p in requiredtools:
323 for p in requiredtools:
322 if os.name == 'nt':
324 if os.name == 'nt':
323 p += '.exe'
325 p += '.exe'
324 found = findprogram(p)
326 found = findprogram(p)
325 if found:
327 if found:
326 vlog("# Found prerequisite", p, "at", found)
328 vlog("# Found prerequisite", p, "at", found)
327 else:
329 else:
328 print "WARNING: Did not find prerequisite tool: "+p
330 print "WARNING: Did not find prerequisite tool: "+p
329
331
330 def killdaemons():
332 def killdaemons():
331 # Kill off any leftover daemon processes
333 # Kill off any leftover daemon processes
332 try:
334 try:
333 fp = open(DAEMON_PIDS)
335 fp = open(DAEMON_PIDS)
334 for line in fp:
336 for line in fp:
335 try:
337 try:
336 pid = int(line)
338 pid = int(line)
337 except ValueError:
339 except ValueError:
338 continue
340 continue
339 try:
341 try:
340 os.kill(pid, 0)
342 os.kill(pid, 0)
341 vlog('# Killing daemon process %d' % pid)
343 vlog('# Killing daemon process %d' % pid)
342 os.kill(pid, signal.SIGTERM)
344 os.kill(pid, signal.SIGTERM)
343 time.sleep(0.25)
345 time.sleep(0.25)
344 os.kill(pid, 0)
346 os.kill(pid, 0)
345 vlog('# Daemon process %d is stuck - really killing it' % pid)
347 vlog('# Daemon process %d is stuck - really killing it' % pid)
346 os.kill(pid, signal.SIGKILL)
348 os.kill(pid, signal.SIGKILL)
347 except OSError, err:
349 except OSError, err:
348 if err.errno != errno.ESRCH:
350 if err.errno != errno.ESRCH:
349 raise
351 raise
350 fp.close()
352 fp.close()
351 os.unlink(DAEMON_PIDS)
353 os.unlink(DAEMON_PIDS)
352 except IOError:
354 except IOError:
353 pass
355 pass
354
356
355 def cleanup(options):
357 def cleanup(options):
356 if not options.keep_tmpdir:
358 if not options.keep_tmpdir:
357 vlog("# Cleaning up HGTMP", HGTMP)
359 vlog("# Cleaning up HGTMP", HGTMP)
358 shutil.rmtree(HGTMP, True)
360 shutil.rmtree(HGTMP, True)
359
361
360 def usecorrectpython():
362 def usecorrectpython():
361 # some tests run python interpreter. they must use same
363 # some tests run python interpreter. they must use same
362 # interpreter we use or bad things will happen.
364 # interpreter we use or bad things will happen.
363 exedir, exename = os.path.split(sys.executable)
365 exedir, exename = os.path.split(sys.executable)
364 if exename == 'python':
366 if exename == 'python':
365 path = findprogram('python')
367 path = findprogram('python')
366 if os.path.dirname(path) == exedir:
368 if os.path.dirname(path) == exedir:
367 return
369 return
368 vlog('# Making python executable in test path use correct Python')
370 vlog('# Making python executable in test path use correct Python')
369 mypython = os.path.join(BINDIR, 'python')
371 mypython = os.path.join(BINDIR, 'python')
370 try:
372 try:
371 os.symlink(sys.executable, mypython)
373 os.symlink(sys.executable, mypython)
372 except AttributeError:
374 except AttributeError:
373 # windows fallback
375 # windows fallback
374 shutil.copyfile(sys.executable, mypython)
376 shutil.copyfile(sys.executable, mypython)
375 shutil.copymode(sys.executable, mypython)
377 shutil.copymode(sys.executable, mypython)
376
378
377 def installhg(options):
379 def installhg(options):
378 vlog("# Performing temporary installation of HG")
380 vlog("# Performing temporary installation of HG")
379 installerrs = os.path.join("tests", "install.err")
381 installerrs = os.path.join("tests", "install.err")
380 pure = options.pure and "--pure" or ""
382 pure = options.pure and "--pure" or ""
381
383
382 # Run installer in hg root
384 # Run installer in hg root
383 script = os.path.realpath(sys.argv[0])
385 script = os.path.realpath(sys.argv[0])
384 hgroot = os.path.dirname(os.path.dirname(script))
386 hgroot = os.path.dirname(os.path.dirname(script))
385 os.chdir(hgroot)
387 os.chdir(hgroot)
386 nohome = '--home=""'
388 nohome = '--home=""'
387 if os.name == 'nt':
389 if os.name == 'nt':
388 # The --home="" trick works only on OS where os.sep == '/'
390 # The --home="" trick works only on OS where os.sep == '/'
389 # because of a distutils convert_path() fast-path. Avoid it at
391 # because of a distutils convert_path() fast-path. Avoid it at
390 # least on Windows for now, deal with .pydistutils.cfg bugs
392 # least on Windows for now, deal with .pydistutils.cfg bugs
391 # when they happen.
393 # when they happen.
392 nohome = ''
394 nohome = ''
393 cmd = ('%s setup.py %s clean --all'
395 cmd = ('%s setup.py %s clean --all'
394 ' build --build-base="%s"'
396 ' build --build-base="%s"'
395 ' install --force --prefix="%s" --install-lib="%s"'
397 ' install --force --prefix="%s" --install-lib="%s"'
396 ' --install-scripts="%s" %s >%s 2>&1'
398 ' --install-scripts="%s" %s >%s 2>&1'
397 % (sys.executable, pure, os.path.join(HGTMP, "build"),
399 % (sys.executable, pure, os.path.join(HGTMP, "build"),
398 INST, PYTHONDIR, BINDIR, nohome, installerrs))
400 INST, PYTHONDIR, BINDIR, nohome, installerrs))
399 vlog("# Running", cmd)
401 vlog("# Running", cmd)
400 if os.system(cmd) == 0:
402 if os.system(cmd) == 0:
401 if not options.verbose:
403 if not options.verbose:
402 os.remove(installerrs)
404 os.remove(installerrs)
403 else:
405 else:
404 f = open(installerrs)
406 f = open(installerrs)
405 for line in f:
407 for line in f:
406 print line,
408 print line,
407 f.close()
409 f.close()
408 sys.exit(1)
410 sys.exit(1)
409 os.chdir(TESTDIR)
411 os.chdir(TESTDIR)
410
412
411 usecorrectpython()
413 usecorrectpython()
412
414
413 vlog("# Installing dummy diffstat")
415 vlog("# Installing dummy diffstat")
414 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
416 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
415 f.write('#!' + sys.executable + '\n'
417 f.write('#!' + sys.executable + '\n'
416 'import sys\n'
418 'import sys\n'
417 'files = 0\n'
419 'files = 0\n'
418 'for line in sys.stdin:\n'
420 'for line in sys.stdin:\n'
419 ' if line.startswith("diff "):\n'
421 ' if line.startswith("diff "):\n'
420 ' files += 1\n'
422 ' files += 1\n'
421 'sys.stdout.write("files patched: %d\\n" % files)\n')
423 'sys.stdout.write("files patched: %d\\n" % files)\n')
422 f.close()
424 f.close()
423 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
425 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
424
426
425 if options.py3k_warnings and not options.anycoverage:
427 if options.py3k_warnings and not options.anycoverage:
426 vlog("# Updating hg command to enable Py3k Warnings switch")
428 vlog("# Updating hg command to enable Py3k Warnings switch")
427 f = open(os.path.join(BINDIR, 'hg'), 'r')
429 f = open(os.path.join(BINDIR, 'hg'), 'r')
428 lines = [line.rstrip() for line in f]
430 lines = [line.rstrip() for line in f]
429 lines[0] += ' -3'
431 lines[0] += ' -3'
430 f.close()
432 f.close()
431 f = open(os.path.join(BINDIR, 'hg'), 'w')
433 f = open(os.path.join(BINDIR, 'hg'), 'w')
432 for line in lines:
434 for line in lines:
433 f.write(line + '\n')
435 f.write(line + '\n')
434 f.close()
436 f.close()
435
437
436 if options.anycoverage:
438 if options.anycoverage:
437 custom = os.path.join(TESTDIR, 'sitecustomize.py')
439 custom = os.path.join(TESTDIR, 'sitecustomize.py')
438 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
440 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
439 vlog('# Installing coverage trigger to %s' % target)
441 vlog('# Installing coverage trigger to %s' % target)
440 shutil.copyfile(custom, target)
442 shutil.copyfile(custom, target)
441 rc = os.path.join(TESTDIR, '.coveragerc')
443 rc = os.path.join(TESTDIR, '.coveragerc')
442 vlog('# Installing coverage rc to %s' % rc)
444 vlog('# Installing coverage rc to %s' % rc)
443 os.environ['COVERAGE_PROCESS_START'] = rc
445 os.environ['COVERAGE_PROCESS_START'] = rc
444 fn = os.path.join(INST, '..', '.coverage')
446 fn = os.path.join(INST, '..', '.coverage')
445 os.environ['COVERAGE_FILE'] = fn
447 os.environ['COVERAGE_FILE'] = fn
446
448
447 def outputcoverage(options):
449 def outputcoverage(options):
448
450
449 vlog('# Producing coverage report')
451 vlog('# Producing coverage report')
450 os.chdir(PYTHONDIR)
452 os.chdir(PYTHONDIR)
451
453
452 def covrun(*args):
454 def covrun(*args):
453 cmd = 'coverage %s' % ' '.join(args)
455 cmd = 'coverage %s' % ' '.join(args)
454 vlog('# Running: %s' % cmd)
456 vlog('# Running: %s' % cmd)
455 os.system(cmd)
457 os.system(cmd)
456
458
457 if options.child:
459 if options.child:
458 return
460 return
459
461
460 covrun('-c')
462 covrun('-c')
461 omit = ','.join([BINDIR, TESTDIR])
463 omit = ','.join([BINDIR, TESTDIR])
462 covrun('-i', '-r', '"--omit=%s"' % omit) # report
464 covrun('-i', '-r', '"--omit=%s"' % omit) # report
463 if options.annotate:
465 if options.annotate:
464 adir = os.path.join(TESTDIR, 'annotated')
466 adir = os.path.join(TESTDIR, 'annotated')
465 if not os.path.isdir(adir):
467 if not os.path.isdir(adir):
466 os.mkdir(adir)
468 os.mkdir(adir)
467 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
469 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
468
470
469 def pytest(test, wd, options, replacements):
471 def pytest(test, wd, options, replacements):
470 py3kswitch = options.py3k_warnings and ' -3' or ''
472 py3kswitch = options.py3k_warnings and ' -3' or ''
471 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
473 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
472 vlog("# Running", cmd)
474 vlog("# Running", cmd)
473 return run(cmd, wd, options, replacements)
475 return run(cmd, wd, options, replacements)
474
476
475 def shtest(test, wd, options, replacements):
477 def shtest(test, wd, options, replacements):
476 cmd = '"%s"' % test
478 cmd = '"%s"' % test
477 vlog("# Running", cmd)
479 vlog("# Running", cmd)
478 return run(cmd, wd, options, replacements)
480 return run(cmd, wd, options, replacements)
479
481
480 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
482 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
481 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
483 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
482 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
484 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
483 escapemap.update({'\\': '\\\\', '\r': r'\r'})
485 escapemap.update({'\\': '\\\\', '\r': r'\r'})
484 def escapef(m):
486 def escapef(m):
485 return escapemap[m.group(0)]
487 return escapemap[m.group(0)]
486 def stringescape(s):
488 def stringescape(s):
487 return escapesub(escapef, s)
489 return escapesub(escapef, s)
488
490
489 def tsttest(test, wd, options, replacements):
491 def tsttest(test, wd, options, replacements):
490 t = open(test)
492 t = open(test)
491 out = []
493 out = []
492 script = []
494 script = []
493 salt = "SALT" + str(time.time())
495 salt = "SALT" + str(time.time())
494
496
495 pos = prepos = -1
497 pos = prepos = -1
496 after = {}
498 after = {}
497 expected = {}
499 expected = {}
498 for n, l in enumerate(t):
500 for n, l in enumerate(t):
499 if not l.endswith('\n'):
501 if not l.endswith('\n'):
500 l += '\n'
502 l += '\n'
501 if l.startswith(' $ '): # commands
503 if l.startswith(' $ '): # commands
502 after.setdefault(pos, []).append(l)
504 after.setdefault(pos, []).append(l)
503 prepos = pos
505 prepos = pos
504 pos = n
506 pos = n
505 script.append('echo %s %s $?\n' % (salt, n))
507 script.append('echo %s %s $?\n' % (salt, n))
506 script.append(l[4:])
508 script.append(l[4:])
507 elif l.startswith(' > '): # continuations
509 elif l.startswith(' > '): # continuations
508 after.setdefault(prepos, []).append(l)
510 after.setdefault(prepos, []).append(l)
509 script.append(l[4:])
511 script.append(l[4:])
510 elif l.startswith(' '): # results
512 elif l.startswith(' '): # results
511 # queue up a list of expected results
513 # queue up a list of expected results
512 expected.setdefault(pos, []).append(l[2:])
514 expected.setdefault(pos, []).append(l[2:])
513 else:
515 else:
514 # non-command/result - queue up for merged output
516 # non-command/result - queue up for merged output
515 after.setdefault(pos, []).append(l)
517 after.setdefault(pos, []).append(l)
516
518
517 t.close()
519 t.close()
518
520
519 script.append('echo %s %s $?\n' % (salt, n + 1))
521 script.append('echo %s %s $?\n' % (salt, n + 1))
520
522
521 fd, name = tempfile.mkstemp(suffix='hg-tst')
523 fd, name = tempfile.mkstemp(suffix='hg-tst')
522
524
523 try:
525 try:
524 for l in script:
526 for l in script:
525 os.write(fd, l)
527 os.write(fd, l)
526 os.close(fd)
528 os.close(fd)
527
529
528 cmd = '/bin/sh "%s"' % name
530 cmd = '/bin/sh "%s"' % name
529 vlog("# Running", cmd)
531 vlog("# Running", cmd)
530 exitcode, output = run(cmd, wd, options, replacements)
532 exitcode, output = run(cmd, wd, options, replacements)
531 # do not merge output if skipped, return hghave message instead
533 # do not merge output if skipped, return hghave message instead
532 # similarly, with --debug, output is None
534 # similarly, with --debug, output is None
533 if exitcode == SKIPPED_STATUS or output is None:
535 if exitcode == SKIPPED_STATUS or output is None:
534 return exitcode, output
536 return exitcode, output
535 finally:
537 finally:
536 os.remove(name)
538 os.remove(name)
537
539
538 def rematch(el, l):
540 def rematch(el, l):
539 try:
541 try:
540 # ensure that the regex matches to the end of the string
542 # ensure that the regex matches to the end of the string
541 return re.match(el + r'\Z', l)
543 return re.match(el + r'\Z', l)
542 except re.error:
544 except re.error:
543 # el is an invalid regex
545 # el is an invalid regex
544 return False
546 return False
545
547
546 def globmatch(el, l):
548 def globmatch(el, l):
547 # The only supported special characters are * and ?. Escaping is
549 # The only supported special characters are * and ?. Escaping is
548 # supported.
550 # supported.
549 i, n = 0, len(el)
551 i, n = 0, len(el)
550 res = ''
552 res = ''
551 while i < n:
553 while i < n:
552 c = el[i]
554 c = el[i]
553 i += 1
555 i += 1
554 if c == '\\' and el[i] in '*?\\':
556 if c == '\\' and el[i] in '*?\\':
555 res += el[i - 1:i + 1]
557 res += el[i - 1:i + 1]
556 i += 1
558 i += 1
557 elif c == '*':
559 elif c == '*':
558 res += '.*'
560 res += '.*'
559 elif c == '?':
561 elif c == '?':
560 res += '.'
562 res += '.'
561 else:
563 else:
562 res += re.escape(c)
564 res += re.escape(c)
563 return rematch(res, l)
565 return rematch(res, l)
564
566
565 pos = -1
567 pos = -1
566 postout = []
568 postout = []
567 ret = 0
569 ret = 0
568 for n, l in enumerate(output):
570 for n, l in enumerate(output):
569 lout, lcmd = l, None
571 lout, lcmd = l, None
570 if salt in l:
572 if salt in l:
571 lout, lcmd = l.split(salt, 1)
573 lout, lcmd = l.split(salt, 1)
572
574
573 if lout:
575 if lout:
574 if lcmd:
576 if lcmd:
575 lout += ' (no-eol)\n'
577 lout += ' (no-eol)\n'
576
578
577 el = None
579 el = None
578 if pos in expected and expected[pos]:
580 if pos in expected and expected[pos]:
579 el = expected[pos].pop(0)
581 el = expected[pos].pop(0)
580
582
581 if el == lout: # perfect match (fast)
583 if el == lout: # perfect match (fast)
582 postout.append(" " + lout)
584 postout.append(" " + lout)
583 elif (el and
585 elif (el and
584 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', lout) or
586 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', lout) or
585 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', lout)
587 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', lout)
586 or el.endswith(" (esc)\n") and
588 or el.endswith(" (esc)\n") and
587 el.decode('string-escape') == l)):
589 el.decode('string-escape') == l)):
588 postout.append(" " + el) # fallback regex/glob/esc match
590 postout.append(" " + el) # fallback regex/glob/esc match
589 else:
591 else:
590 if needescape(lout):
592 if needescape(lout):
591 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
593 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
592 postout.append(" " + lout) # let diff deal with it
594 postout.append(" " + lout) # let diff deal with it
593
595
594 if lcmd:
596 if lcmd:
595 # add on last return code
597 # add on last return code
596 ret = int(lcmd.split()[1])
598 ret = int(lcmd.split()[1])
597 if ret != 0:
599 if ret != 0:
598 postout.append(" [%s]\n" % ret)
600 postout.append(" [%s]\n" % ret)
599 if pos in after:
601 if pos in after:
600 postout += after.pop(pos)
602 postout += after.pop(pos)
601 pos = int(lcmd.split()[0])
603 pos = int(lcmd.split()[0])
602
604
603 if pos in after:
605 if pos in after:
604 postout += after.pop(pos)
606 postout += after.pop(pos)
605
607
606 return exitcode, postout
608 return exitcode, postout
607
609
608 wifexited = getattr(os, "WIFEXITED", lambda x: False)
610 wifexited = getattr(os, "WIFEXITED", lambda x: False)
609 def run(cmd, wd, options, replacements):
611 def run(cmd, wd, options, replacements):
610 """Run command in a sub-process, capturing the output (stdout and stderr).
612 """Run command in a sub-process, capturing the output (stdout and stderr).
611 Return a tuple (exitcode, output). output is None in debug mode."""
613 Return a tuple (exitcode, output). output is None in debug mode."""
612 # TODO: Use subprocess.Popen if we're running on Python 2.4
614 # TODO: Use subprocess.Popen if we're running on Python 2.4
613 if options.debug:
615 if options.debug:
614 proc = subprocess.Popen(cmd, shell=True)
616 proc = subprocess.Popen(cmd, shell=True)
615 ret = proc.wait()
617 ret = proc.wait()
616 return (ret, None)
618 return (ret, None)
617
619
618 if os.name == 'nt' or sys.platform.startswith('java'):
620 if os.name == 'nt' or sys.platform.startswith('java'):
619 tochild, fromchild = os.popen4(cmd)
621 tochild, fromchild = os.popen4(cmd)
620 tochild.close()
622 tochild.close()
621 output = fromchild.read()
623 output = fromchild.read()
622 ret = fromchild.close()
624 ret = fromchild.close()
623 if ret is None:
625 if ret is None:
624 ret = 0
626 ret = 0
625 else:
627 else:
626 proc = Popen4(cmd, wd, options.timeout)
628 proc = Popen4(cmd, wd, options.timeout)
627 def cleanup():
629 def cleanup():
628 try:
630 try:
629 proc.terminate()
631 proc.terminate()
630 except OSError:
632 except OSError:
631 pass
633 pass
632 ret = proc.wait()
634 ret = proc.wait()
633 if ret == 0:
635 if ret == 0:
634 ret = signal.SIGTERM << 8
636 ret = signal.SIGTERM << 8
635 killdaemons()
637 killdaemons()
636 return ret
638 return ret
637
639
638 output = ''
640 output = ''
639 proc.tochild.close()
641 proc.tochild.close()
640
642
641 try:
643 try:
642 output = proc.fromchild.read()
644 output = proc.fromchild.read()
643 except KeyboardInterrupt:
645 except KeyboardInterrupt:
644 vlog('# Handling keyboard interrupt')
646 vlog('# Handling keyboard interrupt')
645 cleanup()
647 cleanup()
646 raise
648 raise
647
649
648 ret = proc.wait()
650 ret = proc.wait()
649 if wifexited(ret):
651 if wifexited(ret):
650 ret = os.WEXITSTATUS(ret)
652 ret = os.WEXITSTATUS(ret)
651
653
652 if proc.timeout:
654 if proc.timeout:
653 ret = 'timeout'
655 ret = 'timeout'
654
656
655 if ret:
657 if ret:
656 killdaemons()
658 killdaemons()
657
659
658 for s, r in replacements:
660 for s, r in replacements:
659 output = re.sub(s, r, output)
661 output = re.sub(s, r, output)
660 return ret, splitnewlines(output)
662 return ret, splitnewlines(output)
661
663
662 def runone(options, test):
664 def runone(options, test):
663 '''tristate output:
665 '''tristate output:
664 None -> skipped
666 None -> skipped
665 True -> passed
667 True -> passed
666 False -> failed'''
668 False -> failed'''
667
669
668 global results, resultslock, iolock
670 global results, resultslock, iolock
669
671
670 testpath = os.path.join(TESTDIR, test)
672 testpath = os.path.join(TESTDIR, test)
671
673
672 def result(l, e):
674 def result(l, e):
673 resultslock.acquire()
675 resultslock.acquire()
674 results[l].append(e)
676 results[l].append(e)
675 resultslock.release()
677 resultslock.release()
676
678
677 def skip(msg):
679 def skip(msg):
678 if not options.verbose:
680 if not options.verbose:
679 result('s', (test, msg))
681 result('s', (test, msg))
680 else:
682 else:
681 iolock.acquire()
683 iolock.acquire()
682 print "\nSkipping %s: %s" % (testpath, msg)
684 print "\nSkipping %s: %s" % (testpath, msg)
683 iolock.release()
685 iolock.release()
684 return None
686 return None
685
687
686 def fail(msg, ret):
688 def fail(msg, ret):
687 if not options.nodiff:
689 if not options.nodiff:
688 iolock.acquire()
690 iolock.acquire()
689 print "\nERROR: %s %s" % (testpath, msg)
691 print "\nERROR: %s %s" % (testpath, msg)
690 iolock.release()
692 iolock.release()
691 if (not ret and options.interactive
693 if (not ret and options.interactive
692 and os.path.exists(testpath + ".err")):
694 and os.path.exists(testpath + ".err")):
693 iolock.acquire()
695 iolock.acquire()
694 print "Accept this change? [n] ",
696 print "Accept this change? [n] ",
695 answer = sys.stdin.readline().strip()
697 answer = sys.stdin.readline().strip()
696 iolock.release()
698 iolock.release()
697 if answer.lower() in "y yes".split():
699 if answer.lower() in "y yes".split():
698 if test.endswith(".t"):
700 if test.endswith(".t"):
699 rename(testpath + ".err", testpath)
701 rename(testpath + ".err", testpath)
700 else:
702 else:
701 rename(testpath + ".err", testpath + ".out")
703 rename(testpath + ".err", testpath + ".out")
702 return
704 return
703 result('f', (test, msg))
705 result('f', (test, msg))
704
706
705 def success():
707 def success():
706 result('p', test)
708 result('p', test)
707
709
708 def ignore(msg):
710 def ignore(msg):
709 result('i', (test, msg))
711 result('i', (test, msg))
710
712
711 if (test.startswith("test-") and '~' not in test and
713 if (test.startswith("test-") and '~' not in test and
712 ('.' not in test or test.endswith('.py') or
714 ('.' not in test or test.endswith('.py') or
713 test.endswith('.bat') or test.endswith('.t'))):
715 test.endswith('.bat') or test.endswith('.t'))):
714 if not os.path.exists(test):
716 if not os.path.exists(test):
715 skip("doesn't exist")
717 skip("doesn't exist")
716 return None
718 return None
717 else:
719 else:
718 return None # not a supported test, don't record
720 return None # not a supported test, don't record
719
721
720 if options.blacklist:
722 if options.blacklist:
721 filename = options.blacklist.get(test)
723 filename = options.blacklist.get(test)
722 if filename is not None:
724 if filename is not None:
723 skip("blacklisted")
725 skip("blacklisted")
724 return None
726 return None
725
727
726 if options.retest and not os.path.exists(test + ".err"):
728 if options.retest and not os.path.exists(test + ".err"):
727 ignore("not retesting")
729 ignore("not retesting")
728 return None
730 return None
729
731
730 if options.keywords:
732 if options.keywords:
731 fp = open(test)
733 fp = open(test)
732 t = fp.read().lower() + test.lower()
734 t = fp.read().lower() + test.lower()
733 fp.close()
735 fp.close()
734 for k in options.keywords.lower().split():
736 for k in options.keywords.lower().split():
735 if k in t:
737 if k in t:
736 break
738 break
737 else:
739 else:
738 ignore("doesn't match keyword")
740 ignore("doesn't match keyword")
739 return None
741 return None
740
742
741 vlog("# Test", test)
743 vlog("# Test", test)
742
744
743 # create a fresh hgrc
745 # create a fresh hgrc
744 hgrc = open(HGRCPATH, 'w+')
746 hgrc = open(HGRCPATH, 'w+')
745 hgrc.write('[ui]\n')
747 hgrc.write('[ui]\n')
746 hgrc.write('slash = True\n')
748 hgrc.write('slash = True\n')
747 hgrc.write('[defaults]\n')
749 hgrc.write('[defaults]\n')
748 hgrc.write('backout = -d "0 0"\n')
750 hgrc.write('backout = -d "0 0"\n')
749 hgrc.write('commit = -d "0 0"\n')
751 hgrc.write('commit = -d "0 0"\n')
750 hgrc.write('tag = -d "0 0"\n')
752 hgrc.write('tag = -d "0 0"\n')
751 if options.inotify:
753 if options.inotify:
752 hgrc.write('[extensions]\n')
754 hgrc.write('[extensions]\n')
753 hgrc.write('inotify=\n')
755 hgrc.write('inotify=\n')
754 hgrc.write('[inotify]\n')
756 hgrc.write('[inotify]\n')
755 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
757 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
756 hgrc.write('appendpid=True\n')
758 hgrc.write('appendpid=True\n')
759 if options.extra_config_opt:
760 for opt in options.extra_config_opt:
761 section, key = opt.split('.', 1)
762 assert '=' in key, ('extra config opt %s must '
763 'have an = for assignment' % opt)
764 hgrc.write('[%s]\n%s\n' % (section, key))
757 hgrc.close()
765 hgrc.close()
758
766
759 ref = os.path.join(TESTDIR, test+".out")
767 ref = os.path.join(TESTDIR, test+".out")
760 err = os.path.join(TESTDIR, test+".err")
768 err = os.path.join(TESTDIR, test+".err")
761 if os.path.exists(err):
769 if os.path.exists(err):
762 os.remove(err) # Remove any previous output files
770 os.remove(err) # Remove any previous output files
763 try:
771 try:
764 tf = open(testpath)
772 tf = open(testpath)
765 firstline = tf.readline().rstrip()
773 firstline = tf.readline().rstrip()
766 tf.close()
774 tf.close()
767 except:
775 except:
768 firstline = ''
776 firstline = ''
769 lctest = test.lower()
777 lctest = test.lower()
770
778
771 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
779 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
772 runner = pytest
780 runner = pytest
773 elif lctest.endswith('.t'):
781 elif lctest.endswith('.t'):
774 runner = tsttest
782 runner = tsttest
775 ref = testpath
783 ref = testpath
776 else:
784 else:
777 # do not try to run non-executable programs
785 # do not try to run non-executable programs
778 if not os.access(testpath, os.X_OK):
786 if not os.access(testpath, os.X_OK):
779 return skip("not executable")
787 return skip("not executable")
780 runner = shtest
788 runner = shtest
781
789
782 # Make a tmp subdirectory to work in
790 # Make a tmp subdirectory to work in
783 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
791 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
784 os.path.join(HGTMP, test)
792 os.path.join(HGTMP, test)
785
793
786 os.mkdir(testtmp)
794 os.mkdir(testtmp)
787 ret, out = runner(testpath, testtmp, options, [
795 ret, out = runner(testpath, testtmp, options, [
788 (re.escape(testtmp), '$TESTTMP'),
796 (re.escape(testtmp), '$TESTTMP'),
789 (r':%s\b' % options.port, ':$HGPORT'),
797 (r':%s\b' % options.port, ':$HGPORT'),
790 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
798 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
791 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
799 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
792 ])
800 ])
793 vlog("# Ret was:", ret)
801 vlog("# Ret was:", ret)
794
802
795 mark = '.'
803 mark = '.'
796
804
797 skipped = (ret == SKIPPED_STATUS)
805 skipped = (ret == SKIPPED_STATUS)
798
806
799 # If we're not in --debug mode and reference output file exists,
807 # If we're not in --debug mode and reference output file exists,
800 # check test output against it.
808 # check test output against it.
801 if options.debug:
809 if options.debug:
802 refout = None # to match "out is None"
810 refout = None # to match "out is None"
803 elif os.path.exists(ref):
811 elif os.path.exists(ref):
804 f = open(ref, "r")
812 f = open(ref, "r")
805 refout = splitnewlines(f.read())
813 refout = splitnewlines(f.read())
806 f.close()
814 f.close()
807 else:
815 else:
808 refout = []
816 refout = []
809
817
810 if (ret != 0 or out != refout) and not skipped and not options.debug:
818 if (ret != 0 or out != refout) and not skipped and not options.debug:
811 # Save errors to a file for diagnosis
819 # Save errors to a file for diagnosis
812 f = open(err, "wb")
820 f = open(err, "wb")
813 for line in out:
821 for line in out:
814 f.write(line)
822 f.write(line)
815 f.close()
823 f.close()
816
824
817 if skipped:
825 if skipped:
818 mark = 's'
826 mark = 's'
819 if out is None: # debug mode: nothing to parse
827 if out is None: # debug mode: nothing to parse
820 missing = ['unknown']
828 missing = ['unknown']
821 failed = None
829 failed = None
822 else:
830 else:
823 missing, failed = parsehghaveoutput(out)
831 missing, failed = parsehghaveoutput(out)
824 if not missing:
832 if not missing:
825 missing = ['irrelevant']
833 missing = ['irrelevant']
826 if failed:
834 if failed:
827 fail("hghave failed checking for %s" % failed[-1], ret)
835 fail("hghave failed checking for %s" % failed[-1], ret)
828 skipped = False
836 skipped = False
829 else:
837 else:
830 skip(missing[-1])
838 skip(missing[-1])
831 elif ret == 'timeout':
839 elif ret == 'timeout':
832 mark = 't'
840 mark = 't'
833 fail("timed out", ret)
841 fail("timed out", ret)
834 elif out != refout:
842 elif out != refout:
835 mark = '!'
843 mark = '!'
836 if not options.nodiff:
844 if not options.nodiff:
837 iolock.acquire()
845 iolock.acquire()
838 if options.view:
846 if options.view:
839 os.system("%s %s %s" % (options.view, ref, err))
847 os.system("%s %s %s" % (options.view, ref, err))
840 else:
848 else:
841 showdiff(refout, out, ref, err)
849 showdiff(refout, out, ref, err)
842 iolock.release()
850 iolock.release()
843 if ret:
851 if ret:
844 fail("output changed and returned error code %d" % ret, ret)
852 fail("output changed and returned error code %d" % ret, ret)
845 else:
853 else:
846 fail("output changed", ret)
854 fail("output changed", ret)
847 ret = 1
855 ret = 1
848 elif ret:
856 elif ret:
849 mark = '!'
857 mark = '!'
850 fail("returned error code %d" % ret, ret)
858 fail("returned error code %d" % ret, ret)
851 else:
859 else:
852 success()
860 success()
853
861
854 if not options.verbose:
862 if not options.verbose:
855 iolock.acquire()
863 iolock.acquire()
856 sys.stdout.write(mark)
864 sys.stdout.write(mark)
857 sys.stdout.flush()
865 sys.stdout.flush()
858 iolock.release()
866 iolock.release()
859
867
860 killdaemons()
868 killdaemons()
861
869
862 if not options.keep_tmpdir:
870 if not options.keep_tmpdir:
863 shutil.rmtree(testtmp, True)
871 shutil.rmtree(testtmp, True)
864 if skipped:
872 if skipped:
865 return None
873 return None
866 return ret == 0
874 return ret == 0
867
875
868 _hgpath = None
876 _hgpath = None
869
877
870 def _gethgpath():
878 def _gethgpath():
871 """Return the path to the mercurial package that is actually found by
879 """Return the path to the mercurial package that is actually found by
872 the current Python interpreter."""
880 the current Python interpreter."""
873 global _hgpath
881 global _hgpath
874 if _hgpath is not None:
882 if _hgpath is not None:
875 return _hgpath
883 return _hgpath
876
884
877 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
885 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
878 pipe = os.popen(cmd % PYTHON)
886 pipe = os.popen(cmd % PYTHON)
879 try:
887 try:
880 _hgpath = pipe.read().strip()
888 _hgpath = pipe.read().strip()
881 finally:
889 finally:
882 pipe.close()
890 pipe.close()
883 return _hgpath
891 return _hgpath
884
892
885 def _checkhglib(verb):
893 def _checkhglib(verb):
886 """Ensure that the 'mercurial' package imported by python is
894 """Ensure that the 'mercurial' package imported by python is
887 the one we expect it to be. If not, print a warning to stderr."""
895 the one we expect it to be. If not, print a warning to stderr."""
888 expecthg = os.path.join(PYTHONDIR, 'mercurial')
896 expecthg = os.path.join(PYTHONDIR, 'mercurial')
889 actualhg = _gethgpath()
897 actualhg = _gethgpath()
890 if actualhg != expecthg:
898 if actualhg != expecthg:
891 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
899 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
892 ' (expected %s)\n'
900 ' (expected %s)\n'
893 % (verb, actualhg, expecthg))
901 % (verb, actualhg, expecthg))
894
902
895 def runchildren(options, tests):
903 def runchildren(options, tests):
896 if INST:
904 if INST:
897 installhg(options)
905 installhg(options)
898 _checkhglib("Testing")
906 _checkhglib("Testing")
899
907
900 optcopy = dict(options.__dict__)
908 optcopy = dict(options.__dict__)
901 optcopy['jobs'] = 1
909 optcopy['jobs'] = 1
902 del optcopy['blacklist']
910 del optcopy['blacklist']
903 if optcopy['with_hg'] is None:
911 if optcopy['with_hg'] is None:
904 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
912 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
905 optcopy.pop('anycoverage', None)
913 optcopy.pop('anycoverage', None)
906
914
907 opts = []
915 opts = []
908 for opt, value in optcopy.iteritems():
916 for opt, value in optcopy.iteritems():
909 name = '--' + opt.replace('_', '-')
917 name = '--' + opt.replace('_', '-')
910 if value is True:
918 if value is True:
911 opts.append(name)
919 opts.append(name)
912 elif value is not None:
920 elif value is not None:
913 opts.append(name + '=' + str(value))
921 opts.append(name + '=' + str(value))
914
922
915 tests.reverse()
923 tests.reverse()
916 jobs = [[] for j in xrange(options.jobs)]
924 jobs = [[] for j in xrange(options.jobs)]
917 while tests:
925 while tests:
918 for job in jobs:
926 for job in jobs:
919 if not tests:
927 if not tests:
920 break
928 break
921 job.append(tests.pop())
929 job.append(tests.pop())
922 fps = {}
930 fps = {}
923
931
924 for j, job in enumerate(jobs):
932 for j, job in enumerate(jobs):
925 if not job:
933 if not job:
926 continue
934 continue
927 rfd, wfd = os.pipe()
935 rfd, wfd = os.pipe()
928 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
936 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
929 childtmp = os.path.join(HGTMP, 'child%d' % j)
937 childtmp = os.path.join(HGTMP, 'child%d' % j)
930 childopts += ['--tmpdir', childtmp]
938 childopts += ['--tmpdir', childtmp]
931 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
939 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
932 vlog(' '.join(cmdline))
940 vlog(' '.join(cmdline))
933 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
941 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
934 os.close(wfd)
942 os.close(wfd)
935 signal.signal(signal.SIGINT, signal.SIG_IGN)
943 signal.signal(signal.SIGINT, signal.SIG_IGN)
936 failures = 0
944 failures = 0
937 tested, skipped, failed = 0, 0, 0
945 tested, skipped, failed = 0, 0, 0
938 skips = []
946 skips = []
939 fails = []
947 fails = []
940 while fps:
948 while fps:
941 pid, status = os.wait()
949 pid, status = os.wait()
942 fp = fps.pop(pid)
950 fp = fps.pop(pid)
943 l = fp.read().splitlines()
951 l = fp.read().splitlines()
944 try:
952 try:
945 test, skip, fail = map(int, l[:3])
953 test, skip, fail = map(int, l[:3])
946 except ValueError:
954 except ValueError:
947 test, skip, fail = 0, 0, 0
955 test, skip, fail = 0, 0, 0
948 split = -fail or len(l)
956 split = -fail or len(l)
949 for s in l[3:split]:
957 for s in l[3:split]:
950 skips.append(s.split(" ", 1))
958 skips.append(s.split(" ", 1))
951 for s in l[split:]:
959 for s in l[split:]:
952 fails.append(s.split(" ", 1))
960 fails.append(s.split(" ", 1))
953 tested += test
961 tested += test
954 skipped += skip
962 skipped += skip
955 failed += fail
963 failed += fail
956 vlog('pid %d exited, status %d' % (pid, status))
964 vlog('pid %d exited, status %d' % (pid, status))
957 failures |= status
965 failures |= status
958 print
966 print
959 if not options.noskips:
967 if not options.noskips:
960 for s in skips:
968 for s in skips:
961 print "Skipped %s: %s" % (s[0], s[1])
969 print "Skipped %s: %s" % (s[0], s[1])
962 for s in fails:
970 for s in fails:
963 print "Failed %s: %s" % (s[0], s[1])
971 print "Failed %s: %s" % (s[0], s[1])
964
972
965 _checkhglib("Tested")
973 _checkhglib("Tested")
966 print "# Ran %d tests, %d skipped, %d failed." % (
974 print "# Ran %d tests, %d skipped, %d failed." % (
967 tested, skipped, failed)
975 tested, skipped, failed)
968
976
969 if options.anycoverage:
977 if options.anycoverage:
970 outputcoverage(options)
978 outputcoverage(options)
971 sys.exit(failures != 0)
979 sys.exit(failures != 0)
972
980
973 results = dict(p=[], f=[], s=[], i=[])
981 results = dict(p=[], f=[], s=[], i=[])
974 resultslock = threading.Lock()
982 resultslock = threading.Lock()
975 iolock = threading.Lock()
983 iolock = threading.Lock()
976
984
977 def runqueue(options, tests, results):
985 def runqueue(options, tests, results):
978 for test in tests:
986 for test in tests:
979 ret = runone(options, test)
987 ret = runone(options, test)
980 if options.first and ret is not None and not ret:
988 if options.first and ret is not None and not ret:
981 break
989 break
982
990
983 def runtests(options, tests):
991 def runtests(options, tests):
984 global DAEMON_PIDS, HGRCPATH
992 global DAEMON_PIDS, HGRCPATH
985 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
993 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
986 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
994 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
987
995
988 try:
996 try:
989 if INST:
997 if INST:
990 installhg(options)
998 installhg(options)
991 _checkhglib("Testing")
999 _checkhglib("Testing")
992
1000
993 if options.restart:
1001 if options.restart:
994 orig = list(tests)
1002 orig = list(tests)
995 while tests:
1003 while tests:
996 if os.path.exists(tests[0] + ".err"):
1004 if os.path.exists(tests[0] + ".err"):
997 break
1005 break
998 tests.pop(0)
1006 tests.pop(0)
999 if not tests:
1007 if not tests:
1000 print "running all tests"
1008 print "running all tests"
1001 tests = orig
1009 tests = orig
1002
1010
1003 runqueue(options, tests, results)
1011 runqueue(options, tests, results)
1004
1012
1005 failed = len(results['f'])
1013 failed = len(results['f'])
1006 tested = len(results['p']) + failed
1014 tested = len(results['p']) + failed
1007 skipped = len(results['s'])
1015 skipped = len(results['s'])
1008 ignored = len(results['i'])
1016 ignored = len(results['i'])
1009
1017
1010 if options.child:
1018 if options.child:
1011 fp = os.fdopen(options.child, 'w')
1019 fp = os.fdopen(options.child, 'w')
1012 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1020 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1013 for s in results['s']:
1021 for s in results['s']:
1014 fp.write("%s %s\n" % s)
1022 fp.write("%s %s\n" % s)
1015 for s in results['f']:
1023 for s in results['f']:
1016 fp.write("%s %s\n" % s)
1024 fp.write("%s %s\n" % s)
1017 fp.close()
1025 fp.close()
1018 else:
1026 else:
1019 print
1027 print
1020 for s in results['s']:
1028 for s in results['s']:
1021 print "Skipped %s: %s" % s
1029 print "Skipped %s: %s" % s
1022 for s in results['f']:
1030 for s in results['f']:
1023 print "Failed %s: %s" % s
1031 print "Failed %s: %s" % s
1024 _checkhglib("Tested")
1032 _checkhglib("Tested")
1025 print "# Ran %d tests, %d skipped, %d failed." % (
1033 print "# Ran %d tests, %d skipped, %d failed." % (
1026 tested, skipped + ignored, failed)
1034 tested, skipped + ignored, failed)
1027
1035
1028 if options.anycoverage:
1036 if options.anycoverage:
1029 outputcoverage(options)
1037 outputcoverage(options)
1030 except KeyboardInterrupt:
1038 except KeyboardInterrupt:
1031 failed = True
1039 failed = True
1032 print "\ninterrupted!"
1040 print "\ninterrupted!"
1033
1041
1034 if failed:
1042 if failed:
1035 sys.exit(1)
1043 sys.exit(1)
1036
1044
1037 def main():
1045 def main():
1038 (options, args) = parseargs()
1046 (options, args) = parseargs()
1039 if not options.child:
1047 if not options.child:
1040 os.umask(022)
1048 os.umask(022)
1041
1049
1042 checktools()
1050 checktools()
1043
1051
1044 if len(args) == 0:
1052 if len(args) == 0:
1045 args = os.listdir(".")
1053 args = os.listdir(".")
1046 args.sort()
1054 args.sort()
1047
1055
1048 tests = args
1056 tests = args
1049
1057
1050 # Reset some environment variables to well-known values so that
1058 # Reset some environment variables to well-known values so that
1051 # the tests produce repeatable output.
1059 # the tests produce repeatable output.
1052 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1060 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1053 os.environ['TZ'] = 'GMT'
1061 os.environ['TZ'] = 'GMT'
1054 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1062 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1055 os.environ['CDPATH'] = ''
1063 os.environ['CDPATH'] = ''
1056 os.environ['COLUMNS'] = '80'
1064 os.environ['COLUMNS'] = '80'
1057 os.environ['GREP_OPTIONS'] = ''
1065 os.environ['GREP_OPTIONS'] = ''
1058 os.environ['http_proxy'] = ''
1066 os.environ['http_proxy'] = ''
1059
1067
1060 # unset env related to hooks
1068 # unset env related to hooks
1061 for k in os.environ.keys():
1069 for k in os.environ.keys():
1062 if k.startswith('HG_'):
1070 if k.startswith('HG_'):
1063 # can't remove on solaris
1071 # can't remove on solaris
1064 os.environ[k] = ''
1072 os.environ[k] = ''
1065 del os.environ[k]
1073 del os.environ[k]
1066
1074
1067 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1075 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1068 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1076 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1069 if options.tmpdir:
1077 if options.tmpdir:
1070 options.keep_tmpdir = True
1078 options.keep_tmpdir = True
1071 tmpdir = options.tmpdir
1079 tmpdir = options.tmpdir
1072 if os.path.exists(tmpdir):
1080 if os.path.exists(tmpdir):
1073 # Meaning of tmpdir has changed since 1.3: we used to create
1081 # Meaning of tmpdir has changed since 1.3: we used to create
1074 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1082 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1075 # tmpdir already exists.
1083 # tmpdir already exists.
1076 sys.exit("error: temp dir %r already exists" % tmpdir)
1084 sys.exit("error: temp dir %r already exists" % tmpdir)
1077
1085
1078 # Automatically removing tmpdir sounds convenient, but could
1086 # Automatically removing tmpdir sounds convenient, but could
1079 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1087 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1080 # or "--tmpdir=$HOME".
1088 # or "--tmpdir=$HOME".
1081 #vlog("# Removing temp dir", tmpdir)
1089 #vlog("# Removing temp dir", tmpdir)
1082 #shutil.rmtree(tmpdir)
1090 #shutil.rmtree(tmpdir)
1083 os.makedirs(tmpdir)
1091 os.makedirs(tmpdir)
1084 else:
1092 else:
1085 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1093 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1086 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1094 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1087 DAEMON_PIDS = None
1095 DAEMON_PIDS = None
1088 HGRCPATH = None
1096 HGRCPATH = None
1089
1097
1090 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1098 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1091 os.environ["HGMERGE"] = "internal:merge"
1099 os.environ["HGMERGE"] = "internal:merge"
1092 os.environ["HGUSER"] = "test"
1100 os.environ["HGUSER"] = "test"
1093 os.environ["HGENCODING"] = "ascii"
1101 os.environ["HGENCODING"] = "ascii"
1094 os.environ["HGENCODINGMODE"] = "strict"
1102 os.environ["HGENCODINGMODE"] = "strict"
1095 os.environ["HGPORT"] = str(options.port)
1103 os.environ["HGPORT"] = str(options.port)
1096 os.environ["HGPORT1"] = str(options.port + 1)
1104 os.environ["HGPORT1"] = str(options.port + 1)
1097 os.environ["HGPORT2"] = str(options.port + 2)
1105 os.environ["HGPORT2"] = str(options.port + 2)
1098
1106
1099 if options.with_hg:
1107 if options.with_hg:
1100 INST = None
1108 INST = None
1101 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1109 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1102
1110
1103 # This looks redundant with how Python initializes sys.path from
1111 # This looks redundant with how Python initializes sys.path from
1104 # the location of the script being executed. Needed because the
1112 # the location of the script being executed. Needed because the
1105 # "hg" specified by --with-hg is not the only Python script
1113 # "hg" specified by --with-hg is not the only Python script
1106 # executed in the test suite that needs to import 'mercurial'
1114 # executed in the test suite that needs to import 'mercurial'
1107 # ... which means it's not really redundant at all.
1115 # ... which means it's not really redundant at all.
1108 PYTHONDIR = BINDIR
1116 PYTHONDIR = BINDIR
1109 else:
1117 else:
1110 INST = os.path.join(HGTMP, "install")
1118 INST = os.path.join(HGTMP, "install")
1111 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1119 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1112 PYTHONDIR = os.path.join(INST, "lib", "python")
1120 PYTHONDIR = os.path.join(INST, "lib", "python")
1113
1121
1114 os.environ["BINDIR"] = BINDIR
1122 os.environ["BINDIR"] = BINDIR
1115 os.environ["PYTHON"] = PYTHON
1123 os.environ["PYTHON"] = PYTHON
1116
1124
1117 if not options.child:
1125 if not options.child:
1118 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1126 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1119 os.environ["PATH"] = os.pathsep.join(path)
1127 os.environ["PATH"] = os.pathsep.join(path)
1120
1128
1121 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1129 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1122 # can run .../tests/run-tests.py test-foo where test-foo
1130 # can run .../tests/run-tests.py test-foo where test-foo
1123 # adds an extension to HGRC
1131 # adds an extension to HGRC
1124 pypath = [PYTHONDIR, TESTDIR]
1132 pypath = [PYTHONDIR, TESTDIR]
1125 # We have to augment PYTHONPATH, rather than simply replacing
1133 # We have to augment PYTHONPATH, rather than simply replacing
1126 # it, in case external libraries are only available via current
1134 # it, in case external libraries are only available via current
1127 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1135 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1128 # are in /opt/subversion.)
1136 # are in /opt/subversion.)
1129 oldpypath = os.environ.get(IMPL_PATH)
1137 oldpypath = os.environ.get(IMPL_PATH)
1130 if oldpypath:
1138 if oldpypath:
1131 pypath.append(oldpypath)
1139 pypath.append(oldpypath)
1132 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1140 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1133
1141
1134 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1142 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1135
1143
1136 vlog("# Using TESTDIR", TESTDIR)
1144 vlog("# Using TESTDIR", TESTDIR)
1137 vlog("# Using HGTMP", HGTMP)
1145 vlog("# Using HGTMP", HGTMP)
1138 vlog("# Using PATH", os.environ["PATH"])
1146 vlog("# Using PATH", os.environ["PATH"])
1139 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1147 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1140
1148
1141 try:
1149 try:
1142 if len(tests) > 1 and options.jobs > 1:
1150 if len(tests) > 1 and options.jobs > 1:
1143 runchildren(options, tests)
1151 runchildren(options, tests)
1144 else:
1152 else:
1145 runtests(options, tests)
1153 runtests(options, tests)
1146 finally:
1154 finally:
1147 time.sleep(1)
1155 time.sleep(1)
1148 cleanup(options)
1156 cleanup(options)
1149
1157
1150 if __name__ == '__main__':
1158 if __name__ == '__main__':
1151 main()
1159 main()
General Comments 0
You need to be logged in to leave comments. Login now