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