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