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