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