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