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