##// END OF EJS Templates
tests: replace test tmp directory with $TESTTMP in test output...
Mads Kiilerich -
r12639:236058a6 default
parent child Browse files
Show More
@@ -1,1093 +1,1095 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):
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)
450 return run(cmd, options, replacements)
451
451
452 def shtest(test, options):
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)
455 return run(cmd, options, replacements)
456
456
457 def battest(test, options):
457 def battest(test, options, replacements):
458 # To reliably get the error code from batch files on WinXP,
458 # To reliably get the error code from batch files on WinXP,
459 # the "cmd /c call" prefix is needed. Grrr
459 # the "cmd /c call" prefix is needed. Grrr
460 cmd = 'cmd /c call "%s"' % testpath
460 cmd = 'cmd /c call "%s"' % testpath
461 vlog("# Running", cmd)
461 vlog("# Running", cmd)
462 return run(cmd, options)
462 return run(cmd, options, replacements)
463
463
464 def tsttest(test, options):
464 def tsttest(test, options, replacements):
465 t = open(test)
465 t = open(test)
466 out = []
466 out = []
467 script = []
467 script = []
468 salt = "SALT" + str(time.time())
468 salt = "SALT" + str(time.time())
469
469
470 pos = prepos = -1
470 pos = prepos = -1
471 after = {}
471 after = {}
472 expected = {}
472 expected = {}
473 for n, l in enumerate(t):
473 for n, l in enumerate(t):
474 if l.startswith(' $ '): # commands
474 if l.startswith(' $ '): # commands
475 after.setdefault(pos, []).append(l)
475 after.setdefault(pos, []).append(l)
476 prepos = pos
476 prepos = pos
477 pos = n
477 pos = n
478 script.append('echo %s %s $?\n' % (salt, n))
478 script.append('echo %s %s $?\n' % (salt, n))
479 script.append(l[4:])
479 script.append(l[4:])
480 elif l.startswith(' > '): # continuations
480 elif l.startswith(' > '): # continuations
481 after.setdefault(prepos, []).append(l)
481 after.setdefault(prepos, []).append(l)
482 script.append(l[4:])
482 script.append(l[4:])
483 elif l.startswith(' '): # results
483 elif l.startswith(' '): # results
484 # queue up a list of expected results
484 # queue up a list of expected results
485 expected.setdefault(pos, []).append(l[2:])
485 expected.setdefault(pos, []).append(l[2:])
486 else:
486 else:
487 # non-command/result - queue up for merged output
487 # non-command/result - queue up for merged output
488 after.setdefault(pos, []).append(l)
488 after.setdefault(pos, []).append(l)
489
489
490 if script and not script[-1].endswith('\n'):
490 if script and not script[-1].endswith('\n'):
491 script[-1] = script[-1] + '\n'
491 script[-1] = script[-1] + '\n'
492 script.append('echo %s %s $?\n' % (salt, n + 1))
492 script.append('echo %s %s $?\n' % (salt, n + 1))
493
493
494 fd, name = tempfile.mkstemp(suffix='hg-tst')
494 fd, name = tempfile.mkstemp(suffix='hg-tst')
495
495
496 try:
496 try:
497 for l in script:
497 for l in script:
498 os.write(fd, l)
498 os.write(fd, l)
499 os.close(fd)
499 os.close(fd)
500
500
501 cmd = '/bin/sh "%s"' % name
501 cmd = '/bin/sh "%s"' % name
502 vlog("# Running", cmd)
502 vlog("# Running", cmd)
503 exitcode, output = run(cmd, options)
503 exitcode, output = run(cmd, options, replacements)
504 # do not merge output if skipped, return hghave message instead
504 # do not merge output if skipped, return hghave message instead
505 if exitcode == SKIPPED_STATUS:
505 if exitcode == SKIPPED_STATUS:
506 return exitcode, output
506 return exitcode, output
507 finally:
507 finally:
508 os.remove(name)
508 os.remove(name)
509
509
510 def rematch(el, l):
510 def rematch(el, l):
511 try:
511 try:
512 # ensure that the regex matches to the end of the string
512 # ensure that the regex matches to the end of the string
513 return re.match(el + r'\Z', l)
513 return re.match(el + r'\Z', l)
514 except re.error:
514 except re.error:
515 # el is an invalid regex
515 # el is an invalid regex
516 return False
516 return False
517
517
518 def globmatch(el, l):
518 def globmatch(el, l):
519 # The only supported special characters are * and ?. Escaping is
519 # The only supported special characters are * and ?. Escaping is
520 # supported.
520 # supported.
521 i, n = 0, len(el)
521 i, n = 0, len(el)
522 res = ''
522 res = ''
523 while i < n:
523 while i < n:
524 c = el[i]
524 c = el[i]
525 i += 1
525 i += 1
526 if c == '\\' and el[i] in '*?\\':
526 if c == '\\' and el[i] in '*?\\':
527 res += el[i - 1:i + 1]
527 res += el[i - 1:i + 1]
528 i += 1
528 i += 1
529 elif c == '*':
529 elif c == '*':
530 res += '.*'
530 res += '.*'
531 elif c == '?':
531 elif c == '?':
532 res += '.'
532 res += '.'
533 else:
533 else:
534 res += re.escape(c)
534 res += re.escape(c)
535 return rematch(res, l)
535 return rematch(res, l)
536
536
537 pos = -1
537 pos = -1
538 postout = []
538 postout = []
539 ret = 0
539 ret = 0
540 for n, l in enumerate(output):
540 for n, l in enumerate(output):
541 if l.startswith(salt):
541 if l.startswith(salt):
542 # add on last return code
542 # add on last return code
543 ret = int(l.split()[2])
543 ret = int(l.split()[2])
544 if ret != 0:
544 if ret != 0:
545 postout.append(" [%s]\n" % ret)
545 postout.append(" [%s]\n" % ret)
546 if pos in after:
546 if pos in after:
547 postout += after.pop(pos)
547 postout += after.pop(pos)
548 pos = int(l.split()[1])
548 pos = int(l.split()[1])
549 else:
549 else:
550 el = None
550 el = None
551 if pos in expected and expected[pos]:
551 if pos in expected and expected[pos]:
552 el = expected[pos].pop(0)
552 el = expected[pos].pop(0)
553
553
554 if el == l: # perfect match (fast)
554 if el == l: # perfect match (fast)
555 postout.append(" " + l)
555 postout.append(" " + l)
556 elif (el and
556 elif (el and
557 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', l) or
557 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', l) or
558 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', l))):
558 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', l))):
559 postout.append(" " + el) # fallback regex/glob match
559 postout.append(" " + el) # fallback regex/glob match
560 else:
560 else:
561 postout.append(" " + l) # let diff deal with it
561 postout.append(" " + l) # let diff deal with it
562
562
563 if pos in after:
563 if pos in after:
564 postout += after.pop(pos)
564 postout += after.pop(pos)
565
565
566 return exitcode, postout
566 return exitcode, postout
567
567
568 def run(cmd, options):
568 def run(cmd, options, replacements):
569 """Run command in a sub-process, capturing the output (stdout and stderr).
569 """Run command in a sub-process, capturing the output (stdout and stderr).
570 Return a tuple (exitcode, output). output is None in debug mode."""
570 Return a tuple (exitcode, output). output is None in debug mode."""
571 # TODO: Use subprocess.Popen if we're running on Python 2.4
571 # TODO: Use subprocess.Popen if we're running on Python 2.4
572 if options.debug:
572 if options.debug:
573 proc = subprocess.Popen(cmd, shell=True)
573 proc = subprocess.Popen(cmd, shell=True)
574 ret = proc.wait()
574 ret = proc.wait()
575 return (ret, None)
575 return (ret, None)
576
576
577 if os.name == 'nt' or sys.platform.startswith('java'):
577 if os.name == 'nt' or sys.platform.startswith('java'):
578 tochild, fromchild = os.popen4(cmd)
578 tochild, fromchild = os.popen4(cmd)
579 tochild.close()
579 tochild.close()
580 output = fromchild.read()
580 output = fromchild.read()
581 ret = fromchild.close()
581 ret = fromchild.close()
582 if ret == None:
582 if ret == None:
583 ret = 0
583 ret = 0
584 else:
584 else:
585 proc = Popen4(cmd)
585 proc = Popen4(cmd)
586 def cleanup():
586 def cleanup():
587 os.kill(proc.pid, signal.SIGTERM)
587 os.kill(proc.pid, signal.SIGTERM)
588 ret = proc.wait()
588 ret = proc.wait()
589 if ret == 0:
589 if ret == 0:
590 ret = signal.SIGTERM << 8
590 ret = signal.SIGTERM << 8
591 killdaemons()
591 killdaemons()
592 return ret
592 return ret
593
593
594 try:
594 try:
595 output = ''
595 output = ''
596 proc.tochild.close()
596 proc.tochild.close()
597 output = proc.fromchild.read()
597 output = proc.fromchild.read()
598 ret = proc.wait()
598 ret = proc.wait()
599 if os.WIFEXITED(ret):
599 if os.WIFEXITED(ret):
600 ret = os.WEXITSTATUS(ret)
600 ret = os.WEXITSTATUS(ret)
601 except Timeout:
601 except Timeout:
602 vlog('# Process %d timed out - killing it' % proc.pid)
602 vlog('# Process %d timed out - killing it' % proc.pid)
603 ret = cleanup()
603 ret = cleanup()
604 output += ("\n### Abort: timeout after %d seconds.\n"
604 output += ("\n### Abort: timeout after %d seconds.\n"
605 % options.timeout)
605 % options.timeout)
606 except KeyboardInterrupt:
606 except KeyboardInterrupt:
607 vlog('# Handling keyboard interrupt')
607 vlog('# Handling keyboard interrupt')
608 cleanup()
608 cleanup()
609 raise
609 raise
610
610
611 for s, r in replacements:
612 output = output.replace(s, r)
611 return ret, splitnewlines(output)
613 return ret, splitnewlines(output)
612
614
613 def runone(options, test, skips, fails):
615 def runone(options, test, skips, fails):
614 '''tristate output:
616 '''tristate output:
615 None -> skipped
617 None -> skipped
616 True -> passed
618 True -> passed
617 False -> failed'''
619 False -> failed'''
618
620
619 def skip(msg):
621 def skip(msg):
620 if not options.verbose:
622 if not options.verbose:
621 skips.append((test, msg))
623 skips.append((test, msg))
622 else:
624 else:
623 print "\nSkipping %s: %s" % (testpath, msg)
625 print "\nSkipping %s: %s" % (testpath, msg)
624 return None
626 return None
625
627
626 def fail(msg):
628 def fail(msg):
627 fails.append((test, msg))
629 fails.append((test, msg))
628 if not options.nodiff:
630 if not options.nodiff:
629 print "\nERROR: %s %s" % (testpath, msg)
631 print "\nERROR: %s %s" % (testpath, msg)
630 return None
632 return None
631
633
632 vlog("# Test", test)
634 vlog("# Test", test)
633
635
634 # create a fresh hgrc
636 # create a fresh hgrc
635 hgrc = open(HGRCPATH, 'w+')
637 hgrc = open(HGRCPATH, 'w+')
636 hgrc.write('[ui]\n')
638 hgrc.write('[ui]\n')
637 hgrc.write('slash = True\n')
639 hgrc.write('slash = True\n')
638 hgrc.write('[defaults]\n')
640 hgrc.write('[defaults]\n')
639 hgrc.write('backout = -d "0 0"\n')
641 hgrc.write('backout = -d "0 0"\n')
640 hgrc.write('commit = -d "0 0"\n')
642 hgrc.write('commit = -d "0 0"\n')
641 hgrc.write('tag = -d "0 0"\n')
643 hgrc.write('tag = -d "0 0"\n')
642 if options.inotify:
644 if options.inotify:
643 hgrc.write('[extensions]\n')
645 hgrc.write('[extensions]\n')
644 hgrc.write('inotify=\n')
646 hgrc.write('inotify=\n')
645 hgrc.write('[inotify]\n')
647 hgrc.write('[inotify]\n')
646 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
648 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
647 hgrc.write('appendpid=True\n')
649 hgrc.write('appendpid=True\n')
648 hgrc.close()
650 hgrc.close()
649
651
650 testpath = os.path.join(TESTDIR, test)
652 testpath = os.path.join(TESTDIR, test)
651 ref = os.path.join(TESTDIR, test+".out")
653 ref = os.path.join(TESTDIR, test+".out")
652 err = os.path.join(TESTDIR, test+".err")
654 err = os.path.join(TESTDIR, test+".err")
653 if os.path.exists(err):
655 if os.path.exists(err):
654 os.remove(err) # Remove any previous output files
656 os.remove(err) # Remove any previous output files
655 try:
657 try:
656 tf = open(testpath)
658 tf = open(testpath)
657 firstline = tf.readline().rstrip()
659 firstline = tf.readline().rstrip()
658 tf.close()
660 tf.close()
659 except:
661 except:
660 firstline = ''
662 firstline = ''
661 lctest = test.lower()
663 lctest = test.lower()
662
664
663 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
665 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
664 runner = pytest
666 runner = pytest
665 elif lctest.endswith('.bat'):
667 elif lctest.endswith('.bat'):
666 # do not run batch scripts on non-windows
668 # do not run batch scripts on non-windows
667 if os.name != 'nt':
669 if os.name != 'nt':
668 return skip("batch script")
670 return skip("batch script")
669 runner = battest
671 runner = battest
670 elif lctest.endswith('.t'):
672 elif lctest.endswith('.t'):
671 runner = tsttest
673 runner = tsttest
672 ref = testpath
674 ref = testpath
673 else:
675 else:
674 # do not run shell scripts on windows
676 # do not run shell scripts on windows
675 if os.name == 'nt':
677 if os.name == 'nt':
676 return skip("shell script")
678 return skip("shell script")
677 # do not try to run non-executable programs
679 # do not try to run non-executable programs
678 if not os.path.exists(testpath):
680 if not os.path.exists(testpath):
679 return fail("does not exist")
681 return fail("does not exist")
680 elif not os.access(testpath, os.X_OK):
682 elif not os.access(testpath, os.X_OK):
681 return skip("not executable")
683 return skip("not executable")
682 runner = shtest
684 runner = shtest
683
685
684 # Make a tmp subdirectory to work in
686 # Make a tmp subdirectory to work in
685 tmpd = os.path.join(HGTMP, test)
687 testtmp = os.environ["TESTTMP"] = os.path.join(HGTMP, test)
686 os.mkdir(tmpd)
688 os.mkdir(testtmp)
687 os.chdir(tmpd)
689 os.chdir(testtmp)
688
690
689 if options.timeout > 0:
691 if options.timeout > 0:
690 signal.alarm(options.timeout)
692 signal.alarm(options.timeout)
691
693
692 ret, out = runner(testpath, options)
694 ret, out = runner(testpath, options, [(testtmp, '$TESTTMP')])
693 vlog("# Ret was:", ret)
695 vlog("# Ret was:", ret)
694
696
695 if options.timeout > 0:
697 if options.timeout > 0:
696 signal.alarm(0)
698 signal.alarm(0)
697
699
698 mark = '.'
700 mark = '.'
699
701
700 skipped = (ret == SKIPPED_STATUS)
702 skipped = (ret == SKIPPED_STATUS)
701
703
702 # If we're not in --debug mode and reference output file exists,
704 # If we're not in --debug mode and reference output file exists,
703 # check test output against it.
705 # check test output against it.
704 if options.debug:
706 if options.debug:
705 refout = None # to match out == None
707 refout = None # to match out == None
706 elif os.path.exists(ref):
708 elif os.path.exists(ref):
707 f = open(ref, "r")
709 f = open(ref, "r")
708 refout = splitnewlines(f.read())
710 refout = splitnewlines(f.read())
709 f.close()
711 f.close()
710 else:
712 else:
711 refout = []
713 refout = []
712
714
713 if (ret != 0 or out != refout) and not skipped and not options.debug:
715 if (ret != 0 or out != refout) and not skipped and not options.debug:
714 # Save errors to a file for diagnosis
716 # Save errors to a file for diagnosis
715 f = open(err, "wb")
717 f = open(err, "wb")
716 for line in out:
718 for line in out:
717 f.write(line)
719 f.write(line)
718 f.close()
720 f.close()
719
721
720 if skipped:
722 if skipped:
721 mark = 's'
723 mark = 's'
722 if out is None: # debug mode: nothing to parse
724 if out is None: # debug mode: nothing to parse
723 missing = ['unknown']
725 missing = ['unknown']
724 failed = None
726 failed = None
725 else:
727 else:
726 missing, failed = parsehghaveoutput(out)
728 missing, failed = parsehghaveoutput(out)
727 if not missing:
729 if not missing:
728 missing = ['irrelevant']
730 missing = ['irrelevant']
729 if failed:
731 if failed:
730 fail("hghave failed checking for %s" % failed[-1])
732 fail("hghave failed checking for %s" % failed[-1])
731 skipped = False
733 skipped = False
732 else:
734 else:
733 skip(missing[-1])
735 skip(missing[-1])
734 elif out != refout:
736 elif out != refout:
735 mark = '!'
737 mark = '!'
736 if ret:
738 if ret:
737 fail("output changed and returned error code %d" % ret)
739 fail("output changed and returned error code %d" % ret)
738 else:
740 else:
739 fail("output changed")
741 fail("output changed")
740 if not options.nodiff:
742 if not options.nodiff:
741 if options.view:
743 if options.view:
742 os.system("%s %s %s" % (options.view, ref, err))
744 os.system("%s %s %s" % (options.view, ref, err))
743 else:
745 else:
744 showdiff(refout, out, ref, err)
746 showdiff(refout, out, ref, err)
745 ret = 1
747 ret = 1
746 elif ret:
748 elif ret:
747 mark = '!'
749 mark = '!'
748 fail("returned error code %d" % ret)
750 fail("returned error code %d" % ret)
749
751
750 if not options.verbose:
752 if not options.verbose:
751 sys.stdout.write(mark)
753 sys.stdout.write(mark)
752 sys.stdout.flush()
754 sys.stdout.flush()
753
755
754 killdaemons()
756 killdaemons()
755
757
756 os.chdir(TESTDIR)
758 os.chdir(TESTDIR)
757 if not options.keep_tmpdir:
759 if not options.keep_tmpdir:
758 shutil.rmtree(tmpd, True)
760 shutil.rmtree(testtmp, True)
759 if skipped:
761 if skipped:
760 return None
762 return None
761 return ret == 0
763 return ret == 0
762
764
763 _hgpath = None
765 _hgpath = None
764
766
765 def _gethgpath():
767 def _gethgpath():
766 """Return the path to the mercurial package that is actually found by
768 """Return the path to the mercurial package that is actually found by
767 the current Python interpreter."""
769 the current Python interpreter."""
768 global _hgpath
770 global _hgpath
769 if _hgpath is not None:
771 if _hgpath is not None:
770 return _hgpath
772 return _hgpath
771
773
772 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
774 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
773 pipe = os.popen(cmd % PYTHON)
775 pipe = os.popen(cmd % PYTHON)
774 try:
776 try:
775 _hgpath = pipe.read().strip()
777 _hgpath = pipe.read().strip()
776 finally:
778 finally:
777 pipe.close()
779 pipe.close()
778 return _hgpath
780 return _hgpath
779
781
780 def _checkhglib(verb):
782 def _checkhglib(verb):
781 """Ensure that the 'mercurial' package imported by python is
783 """Ensure that the 'mercurial' package imported by python is
782 the one we expect it to be. If not, print a warning to stderr."""
784 the one we expect it to be. If not, print a warning to stderr."""
783 expecthg = os.path.join(PYTHONDIR, 'mercurial')
785 expecthg = os.path.join(PYTHONDIR, 'mercurial')
784 actualhg = _gethgpath()
786 actualhg = _gethgpath()
785 if actualhg != expecthg:
787 if actualhg != expecthg:
786 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
788 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
787 ' (expected %s)\n'
789 ' (expected %s)\n'
788 % (verb, actualhg, expecthg))
790 % (verb, actualhg, expecthg))
789
791
790 def runchildren(options, tests):
792 def runchildren(options, tests):
791 if INST:
793 if INST:
792 installhg(options)
794 installhg(options)
793 _checkhglib("Testing")
795 _checkhglib("Testing")
794
796
795 optcopy = dict(options.__dict__)
797 optcopy = dict(options.__dict__)
796 optcopy['jobs'] = 1
798 optcopy['jobs'] = 1
797 del optcopy['blacklist']
799 del optcopy['blacklist']
798 if optcopy['with_hg'] is None:
800 if optcopy['with_hg'] is None:
799 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
801 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
800 optcopy.pop('anycoverage', None)
802 optcopy.pop('anycoverage', None)
801
803
802 opts = []
804 opts = []
803 for opt, value in optcopy.iteritems():
805 for opt, value in optcopy.iteritems():
804 name = '--' + opt.replace('_', '-')
806 name = '--' + opt.replace('_', '-')
805 if value is True:
807 if value is True:
806 opts.append(name)
808 opts.append(name)
807 elif value is not None:
809 elif value is not None:
808 opts.append(name + '=' + str(value))
810 opts.append(name + '=' + str(value))
809
811
810 tests.reverse()
812 tests.reverse()
811 jobs = [[] for j in xrange(options.jobs)]
813 jobs = [[] for j in xrange(options.jobs)]
812 while tests:
814 while tests:
813 for job in jobs:
815 for job in jobs:
814 if not tests:
816 if not tests:
815 break
817 break
816 job.append(tests.pop())
818 job.append(tests.pop())
817 fps = {}
819 fps = {}
818
820
819 for j, job in enumerate(jobs):
821 for j, job in enumerate(jobs):
820 if not job:
822 if not job:
821 continue
823 continue
822 rfd, wfd = os.pipe()
824 rfd, wfd = os.pipe()
823 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
825 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
824 childtmp = os.path.join(HGTMP, 'child%d' % j)
826 childtmp = os.path.join(HGTMP, 'child%d' % j)
825 childopts += ['--tmpdir', childtmp]
827 childopts += ['--tmpdir', childtmp]
826 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
828 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
827 vlog(' '.join(cmdline))
829 vlog(' '.join(cmdline))
828 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
830 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
829 os.close(wfd)
831 os.close(wfd)
830 signal.signal(signal.SIGINT, signal.SIG_IGN)
832 signal.signal(signal.SIGINT, signal.SIG_IGN)
831 failures = 0
833 failures = 0
832 tested, skipped, failed = 0, 0, 0
834 tested, skipped, failed = 0, 0, 0
833 skips = []
835 skips = []
834 fails = []
836 fails = []
835 while fps:
837 while fps:
836 pid, status = os.wait()
838 pid, status = os.wait()
837 fp = fps.pop(pid)
839 fp = fps.pop(pid)
838 l = fp.read().splitlines()
840 l = fp.read().splitlines()
839 try:
841 try:
840 test, skip, fail = map(int, l[:3])
842 test, skip, fail = map(int, l[:3])
841 except ValueError:
843 except ValueError:
842 test, skip, fail = 0, 0, 0
844 test, skip, fail = 0, 0, 0
843 split = -fail or len(l)
845 split = -fail or len(l)
844 for s in l[3:split]:
846 for s in l[3:split]:
845 skips.append(s.split(" ", 1))
847 skips.append(s.split(" ", 1))
846 for s in l[split:]:
848 for s in l[split:]:
847 fails.append(s.split(" ", 1))
849 fails.append(s.split(" ", 1))
848 tested += test
850 tested += test
849 skipped += skip
851 skipped += skip
850 failed += fail
852 failed += fail
851 vlog('pid %d exited, status %d' % (pid, status))
853 vlog('pid %d exited, status %d' % (pid, status))
852 failures |= status
854 failures |= status
853 print
855 print
854 if not options.noskips:
856 if not options.noskips:
855 for s in skips:
857 for s in skips:
856 print "Skipped %s: %s" % (s[0], s[1])
858 print "Skipped %s: %s" % (s[0], s[1])
857 for s in fails:
859 for s in fails:
858 print "Failed %s: %s" % (s[0], s[1])
860 print "Failed %s: %s" % (s[0], s[1])
859
861
860 _checkhglib("Tested")
862 _checkhglib("Tested")
861 print "# Ran %d tests, %d skipped, %d failed." % (
863 print "# Ran %d tests, %d skipped, %d failed." % (
862 tested, skipped, failed)
864 tested, skipped, failed)
863
865
864 if options.anycoverage:
866 if options.anycoverage:
865 outputcoverage(options)
867 outputcoverage(options)
866 sys.exit(failures != 0)
868 sys.exit(failures != 0)
867
869
868 def runtests(options, tests):
870 def runtests(options, tests):
869 global DAEMON_PIDS, HGRCPATH
871 global DAEMON_PIDS, HGRCPATH
870 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
872 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
871 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
873 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
872
874
873 try:
875 try:
874 if INST:
876 if INST:
875 installhg(options)
877 installhg(options)
876 _checkhglib("Testing")
878 _checkhglib("Testing")
877
879
878 if options.timeout > 0:
880 if options.timeout > 0:
879 try:
881 try:
880 signal.signal(signal.SIGALRM, alarmed)
882 signal.signal(signal.SIGALRM, alarmed)
881 vlog('# Running each test with %d second timeout' %
883 vlog('# Running each test with %d second timeout' %
882 options.timeout)
884 options.timeout)
883 except AttributeError:
885 except AttributeError:
884 print 'WARNING: cannot run tests with timeouts'
886 print 'WARNING: cannot run tests with timeouts'
885 options.timeout = 0
887 options.timeout = 0
886
888
887 tested = 0
889 tested = 0
888 failed = 0
890 failed = 0
889 skipped = 0
891 skipped = 0
890
892
891 if options.restart:
893 if options.restart:
892 orig = list(tests)
894 orig = list(tests)
893 while tests:
895 while tests:
894 if os.path.exists(tests[0] + ".err"):
896 if os.path.exists(tests[0] + ".err"):
895 break
897 break
896 tests.pop(0)
898 tests.pop(0)
897 if not tests:
899 if not tests:
898 print "running all tests"
900 print "running all tests"
899 tests = orig
901 tests = orig
900
902
901 skips = []
903 skips = []
902 fails = []
904 fails = []
903
905
904 for test in tests:
906 for test in tests:
905 if options.blacklist:
907 if options.blacklist:
906 filename = options.blacklist.get(test)
908 filename = options.blacklist.get(test)
907 if filename is not None:
909 if filename is not None:
908 skips.append((test, "blacklisted (%s)" % filename))
910 skips.append((test, "blacklisted (%s)" % filename))
909 skipped += 1
911 skipped += 1
910 continue
912 continue
911
913
912 if options.retest and not os.path.exists(test + ".err"):
914 if options.retest and not os.path.exists(test + ".err"):
913 skipped += 1
915 skipped += 1
914 continue
916 continue
915
917
916 if options.keywords:
918 if options.keywords:
917 t = open(test).read().lower() + test.lower()
919 t = open(test).read().lower() + test.lower()
918 for k in options.keywords.lower().split():
920 for k in options.keywords.lower().split():
919 if k in t:
921 if k in t:
920 break
922 break
921 else:
923 else:
922 skipped += 1
924 skipped += 1
923 continue
925 continue
924
926
925 ret = runone(options, test, skips, fails)
927 ret = runone(options, test, skips, fails)
926 if ret is None:
928 if ret is None:
927 skipped += 1
929 skipped += 1
928 elif not ret:
930 elif not ret:
929 if options.interactive:
931 if options.interactive:
930 print "Accept this change? [n] ",
932 print "Accept this change? [n] ",
931 answer = sys.stdin.readline().strip()
933 answer = sys.stdin.readline().strip()
932 if answer.lower() in "y yes".split():
934 if answer.lower() in "y yes".split():
933 if test.endswith(".t"):
935 if test.endswith(".t"):
934 rename(test + ".err", test)
936 rename(test + ".err", test)
935 else:
937 else:
936 rename(test + ".err", test + ".out")
938 rename(test + ".err", test + ".out")
937 tested += 1
939 tested += 1
938 fails.pop()
940 fails.pop()
939 continue
941 continue
940 failed += 1
942 failed += 1
941 if options.first:
943 if options.first:
942 break
944 break
943 tested += 1
945 tested += 1
944
946
945 if options.child:
947 if options.child:
946 fp = os.fdopen(options.child, 'w')
948 fp = os.fdopen(options.child, 'w')
947 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
949 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
948 for s in skips:
950 for s in skips:
949 fp.write("%s %s\n" % s)
951 fp.write("%s %s\n" % s)
950 for s in fails:
952 for s in fails:
951 fp.write("%s %s\n" % s)
953 fp.write("%s %s\n" % s)
952 fp.close()
954 fp.close()
953 else:
955 else:
954 print
956 print
955 for s in skips:
957 for s in skips:
956 print "Skipped %s: %s" % s
958 print "Skipped %s: %s" % s
957 for s in fails:
959 for s in fails:
958 print "Failed %s: %s" % s
960 print "Failed %s: %s" % s
959 _checkhglib("Tested")
961 _checkhglib("Tested")
960 print "# Ran %d tests, %d skipped, %d failed." % (
962 print "# Ran %d tests, %d skipped, %d failed." % (
961 tested, skipped, failed)
963 tested, skipped, failed)
962
964
963 if options.anycoverage:
965 if options.anycoverage:
964 outputcoverage(options)
966 outputcoverage(options)
965 except KeyboardInterrupt:
967 except KeyboardInterrupt:
966 failed = True
968 failed = True
967 print "\ninterrupted!"
969 print "\ninterrupted!"
968
970
969 if failed:
971 if failed:
970 sys.exit(1)
972 sys.exit(1)
971
973
972 def main():
974 def main():
973 (options, args) = parseargs()
975 (options, args) = parseargs()
974 if not options.child:
976 if not options.child:
975 os.umask(022)
977 os.umask(022)
976
978
977 checktools()
979 checktools()
978
980
979 # Reset some environment variables to well-known values so that
981 # Reset some environment variables to well-known values so that
980 # the tests produce repeatable output.
982 # the tests produce repeatable output.
981 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
983 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
982 os.environ['TZ'] = 'GMT'
984 os.environ['TZ'] = 'GMT'
983 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
985 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
984 os.environ['CDPATH'] = ''
986 os.environ['CDPATH'] = ''
985 os.environ['COLUMNS'] = '80'
987 os.environ['COLUMNS'] = '80'
986 os.environ['GREP_OPTIONS'] = ''
988 os.environ['GREP_OPTIONS'] = ''
987 os.environ['http_proxy'] = ''
989 os.environ['http_proxy'] = ''
988
990
989 # unset env related to hooks
991 # unset env related to hooks
990 for k in os.environ.keys():
992 for k in os.environ.keys():
991 if k.startswith('HG_'):
993 if k.startswith('HG_'):
992 # can't remove on solaris
994 # can't remove on solaris
993 os.environ[k] = ''
995 os.environ[k] = ''
994 del os.environ[k]
996 del os.environ[k]
995
997
996 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
998 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
997 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
999 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
998 if options.tmpdir:
1000 if options.tmpdir:
999 options.keep_tmpdir = True
1001 options.keep_tmpdir = True
1000 tmpdir = options.tmpdir
1002 tmpdir = options.tmpdir
1001 if os.path.exists(tmpdir):
1003 if os.path.exists(tmpdir):
1002 # Meaning of tmpdir has changed since 1.3: we used to create
1004 # Meaning of tmpdir has changed since 1.3: we used to create
1003 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1005 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1004 # tmpdir already exists.
1006 # tmpdir already exists.
1005 sys.exit("error: temp dir %r already exists" % tmpdir)
1007 sys.exit("error: temp dir %r already exists" % tmpdir)
1006
1008
1007 # Automatically removing tmpdir sounds convenient, but could
1009 # Automatically removing tmpdir sounds convenient, but could
1008 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1010 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1009 # or "--tmpdir=$HOME".
1011 # or "--tmpdir=$HOME".
1010 #vlog("# Removing temp dir", tmpdir)
1012 #vlog("# Removing temp dir", tmpdir)
1011 #shutil.rmtree(tmpdir)
1013 #shutil.rmtree(tmpdir)
1012 os.makedirs(tmpdir)
1014 os.makedirs(tmpdir)
1013 else:
1015 else:
1014 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1016 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1015 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1017 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1016 DAEMON_PIDS = None
1018 DAEMON_PIDS = None
1017 HGRCPATH = None
1019 HGRCPATH = None
1018
1020
1019 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1021 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1020 os.environ["HGMERGE"] = "internal:merge"
1022 os.environ["HGMERGE"] = "internal:merge"
1021 os.environ["HGUSER"] = "test"
1023 os.environ["HGUSER"] = "test"
1022 os.environ["HGENCODING"] = "ascii"
1024 os.environ["HGENCODING"] = "ascii"
1023 os.environ["HGENCODINGMODE"] = "strict"
1025 os.environ["HGENCODINGMODE"] = "strict"
1024 os.environ["HGPORT"] = str(options.port)
1026 os.environ["HGPORT"] = str(options.port)
1025 os.environ["HGPORT1"] = str(options.port + 1)
1027 os.environ["HGPORT1"] = str(options.port + 1)
1026 os.environ["HGPORT2"] = str(options.port + 2)
1028 os.environ["HGPORT2"] = str(options.port + 2)
1027
1029
1028 if options.with_hg:
1030 if options.with_hg:
1029 INST = None
1031 INST = None
1030 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1032 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1031
1033
1032 # This looks redundant with how Python initializes sys.path from
1034 # This looks redundant with how Python initializes sys.path from
1033 # the location of the script being executed. Needed because the
1035 # the location of the script being executed. Needed because the
1034 # "hg" specified by --with-hg is not the only Python script
1036 # "hg" specified by --with-hg is not the only Python script
1035 # executed in the test suite that needs to import 'mercurial'
1037 # executed in the test suite that needs to import 'mercurial'
1036 # ... which means it's not really redundant at all.
1038 # ... which means it's not really redundant at all.
1037 PYTHONDIR = BINDIR
1039 PYTHONDIR = BINDIR
1038 else:
1040 else:
1039 INST = os.path.join(HGTMP, "install")
1041 INST = os.path.join(HGTMP, "install")
1040 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1042 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1041 PYTHONDIR = os.path.join(INST, "lib", "python")
1043 PYTHONDIR = os.path.join(INST, "lib", "python")
1042
1044
1043 os.environ["BINDIR"] = BINDIR
1045 os.environ["BINDIR"] = BINDIR
1044 os.environ["PYTHON"] = PYTHON
1046 os.environ["PYTHON"] = PYTHON
1045
1047
1046 if not options.child:
1048 if not options.child:
1047 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1049 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1048 os.environ["PATH"] = os.pathsep.join(path)
1050 os.environ["PATH"] = os.pathsep.join(path)
1049
1051
1050 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1052 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1051 # can run .../tests/run-tests.py test-foo where test-foo
1053 # can run .../tests/run-tests.py test-foo where test-foo
1052 # adds an extension to HGRC
1054 # adds an extension to HGRC
1053 pypath = [PYTHONDIR, TESTDIR]
1055 pypath = [PYTHONDIR, TESTDIR]
1054 # We have to augment PYTHONPATH, rather than simply replacing
1056 # We have to augment PYTHONPATH, rather than simply replacing
1055 # it, in case external libraries are only available via current
1057 # it, in case external libraries are only available via current
1056 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1058 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1057 # are in /opt/subversion.)
1059 # are in /opt/subversion.)
1058 oldpypath = os.environ.get(IMPL_PATH)
1060 oldpypath = os.environ.get(IMPL_PATH)
1059 if oldpypath:
1061 if oldpypath:
1060 pypath.append(oldpypath)
1062 pypath.append(oldpypath)
1061 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1063 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1062
1064
1063 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1065 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1064
1066
1065 if len(args) == 0:
1067 if len(args) == 0:
1066 args = os.listdir(".")
1068 args = os.listdir(".")
1067 args.sort()
1069 args.sort()
1068
1070
1069 tests = []
1071 tests = []
1070 for test in args:
1072 for test in args:
1071 if (test.startswith("test-") and '~' not in test and
1073 if (test.startswith("test-") and '~' not in test and
1072 ('.' not in test or test.endswith('.py') or
1074 ('.' not in test or test.endswith('.py') or
1073 test.endswith('.bat') or test.endswith('.t'))):
1075 test.endswith('.bat') or test.endswith('.t'))):
1074 tests.append(test)
1076 tests.append(test)
1075 if not tests:
1077 if not tests:
1076 print "# Ran 0 tests, 0 skipped, 0 failed."
1078 print "# Ran 0 tests, 0 skipped, 0 failed."
1077 return
1079 return
1078
1080
1079 vlog("# Using TESTDIR", TESTDIR)
1081 vlog("# Using TESTDIR", TESTDIR)
1080 vlog("# Using HGTMP", HGTMP)
1082 vlog("# Using HGTMP", HGTMP)
1081 vlog("# Using PATH", os.environ["PATH"])
1083 vlog("# Using PATH", os.environ["PATH"])
1082 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1084 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1083
1085
1084 try:
1086 try:
1085 if len(tests) > 1 and options.jobs > 1:
1087 if len(tests) > 1 and options.jobs > 1:
1086 runchildren(options, tests)
1088 runchildren(options, tests)
1087 else:
1089 else:
1088 runtests(options, tests)
1090 runtests(options, tests)
1089 finally:
1091 finally:
1090 time.sleep(1)
1092 time.sleep(1)
1091 cleanup(options)
1093 cleanup(options)
1092
1094
1093 main()
1095 main()
General Comments 0
You need to be logged in to leave comments. Login now