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