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