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