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