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