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