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