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