Mercurial > hg > mercurial-crew-with-dirclash
comparison tests/run-tests.py @ 2570:83cfd95eafb5
tests: add timeouts, make run-tests.py clean up dead daemon processes
test timeout feature is needed for test with python 2.5 beta. if test
does not complete in time (30 seconds is default), it is killed.
some times daemon process used in test can be alive after the test
is killed by user or by timeout. tests now record daemon pids into
$DAEMON_PIDS and run-tests.py kills all living daemons after every test.
final little change is to add newline to end of pid file printed by
"hg serve", else "cat hg.pid >> $DAEMON_FILES" gives garbage.
author | Vadim Gelfer <vadim.gelfer@gmail.com> |
---|---|
date | Thu, 06 Jul 2006 11:45:34 -0700 |
parents | 2264b2b077a1 |
children | 6a961a54f953 |
comparison
equal
deleted
inserted
replaced
2569:2264b2b077a1 | 2570:83cfd95eafb5 |
---|---|
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 | 7 # This software may be used and distributed according to the terms |
8 # of the GNU General Public License, incorporated herein by reference. | 8 # of the GNU General Public License, incorporated herein by reference. |
9 | 9 |
10 import os, sys, shutil, re | 10 import difflib |
11 import errno | |
12 import optparse | |
13 import os | |
14 import popen2 | |
15 import re | |
16 import shutil | |
17 import signal | |
18 import sys | |
11 import tempfile | 19 import tempfile |
12 import difflib | 20 import time |
13 import popen2 | |
14 from optparse import OptionParser | |
15 | 21 |
16 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] | 22 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] |
17 | 23 |
18 parser = OptionParser("%prog [options] [tests]") | 24 parser = optparse.OptionParser("%prog [options] [tests]") |
19 parser.add_option("-v", "--verbose", action="store_true", | 25 parser.add_option("-v", "--verbose", action="store_true", |
26 help="output verbose messages") | |
27 parser.add_option("-t", "--timeout", type="int", | |
20 help="output verbose messages") | 28 help="output verbose messages") |
21 parser.add_option("-c", "--cover", action="store_true", | 29 parser.add_option("-c", "--cover", action="store_true", |
22 help="print a test coverage report") | 30 help="print a test coverage report") |
23 parser.add_option("-s", "--cover_stdlib", action="store_true", | 31 parser.add_option("-s", "--cover_stdlib", action="store_true", |
24 help="print a test coverage report inc. standard libraries") | 32 help="print a test coverage report inc. standard libraries") |
25 parser.add_option("-C", "--annotate", action="store_true", | 33 parser.add_option("-C", "--annotate", action="store_true", |
26 help="output files annotated with coverage") | 34 help="output files annotated with coverage") |
35 parser.set_defaults(timeout=30) | |
27 (options, args) = parser.parse_args() | 36 (options, args) = parser.parse_args() |
28 verbose = options.verbose | 37 verbose = options.verbose |
29 coverage = options.cover or options.cover_stdlib or options.annotate | 38 coverage = options.cover or options.cover_stdlib or options.annotate |
30 | 39 |
31 def vlog(*msg): | 40 def vlog(*msg): |
157 sys.executable, os.path.join(TESTDIR, 'coverage.py'), | 166 sys.executable, os.path.join(TESTDIR, 'coverage.py'), |
158 adir, omit) | 167 adir, omit) |
159 vlog("# Running: "+cmd) | 168 vlog("# Running: "+cmd) |
160 os.system(cmd) | 169 os.system(cmd) |
161 | 170 |
171 class Timeout(Exception): | |
172 pass | |
173 | |
174 def alarmed(signum, frame): | |
175 raise Timeout | |
176 | |
162 def run(cmd): | 177 def run(cmd): |
163 """Run command in a sub-process, capturing the output (stdout and stderr). | 178 """Run command in a sub-process, capturing the output (stdout and stderr). |
164 Return the exist code, and output.""" | 179 Return the exist code, and output.""" |
165 # TODO: Use subprocess.Popen if we're running on Python 2.4 | 180 # TODO: Use subprocess.Popen if we're running on Python 2.4 |
166 if os.name == 'nt': | 181 if os.name == 'nt': |
170 ret = fromchild.close() | 185 ret = fromchild.close() |
171 if ret == None: | 186 if ret == None: |
172 ret = 0 | 187 ret = 0 |
173 else: | 188 else: |
174 proc = popen2.Popen4(cmd) | 189 proc = popen2.Popen4(cmd) |
175 proc.tochild.close() | 190 try: |
176 output = proc.fromchild.read() | 191 output = '' |
177 ret = proc.wait() | 192 proc.tochild.close() |
193 output = proc.fromchild.read() | |
194 ret = proc.wait() | |
195 except Timeout: | |
196 vlog('# Process %d timed out - killing it' % proc.pid) | |
197 os.kill(proc.pid, signal.SIGTERM) | |
198 ret = proc.wait() | |
199 if ret == 0: | |
200 ret = signal.SIGTERM << 8 | |
178 return ret, splitnewlines(output) | 201 return ret, splitnewlines(output) |
179 | 202 |
180 def run_one(test): | 203 def run_one(test): |
181 vlog("# Test", test) | 204 vlog("# Test", test) |
182 if not verbose: | 205 if not verbose: |
202 # To reliably get the error code from batch files on WinXP, | 225 # To reliably get the error code from batch files on WinXP, |
203 # the "cmd /c call" prefix is needed. Grrr | 226 # the "cmd /c call" prefix is needed. Grrr |
204 if os.name == 'nt' and test.endswith(".bat"): | 227 if os.name == 'nt' and test.endswith(".bat"): |
205 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test)) | 228 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test)) |
206 | 229 |
230 if options.timeout > 0: | |
231 signal.alarm(options.timeout) | |
232 | |
207 vlog("# Running", cmd) | 233 vlog("# Running", cmd) |
208 ret, out = run(cmd) | 234 ret, out = run(cmd) |
209 vlog("# Ret was:", ret) | 235 vlog("# Ret was:", ret) |
236 | |
237 if options.timeout > 0: | |
238 signal.alarm(0) | |
210 | 239 |
211 diffret = 0 | 240 diffret = 0 |
212 # If reference output file exists, check test output against it | 241 # If reference output file exists, check test output against it |
213 if os.path.exists(ref): | 242 if os.path.exists(ref): |
214 f = open(ref, "r") | 243 f = open(ref, "r") |
229 f = open(err, "wb") | 258 f = open(err, "wb") |
230 for line in out: | 259 for line in out: |
231 f.write(line) | 260 f.write(line) |
232 f.close() | 261 f.close() |
233 | 262 |
263 # Kill off any leftover daemon processes | |
264 try: | |
265 fp = file(DAEMON_PIDS) | |
266 for line in fp: | |
267 try: | |
268 pid = int(line) | |
269 except ValueError: | |
270 continue | |
271 try: | |
272 os.kill(pid, 0) | |
273 vlog('# Killing daemon process %d' % pid) | |
274 os.kill(pid, signal.SIGTERM) | |
275 time.sleep(0.25) | |
276 os.kill(pid, 0) | |
277 vlog('# Daemon process %d is stuck - really killing it' % pid) | |
278 os.kill(pid, signal.SIGKILL) | |
279 except OSError, err: | |
280 if err.errno != errno.ESRCH: | |
281 raise | |
282 fp.close() | |
283 os.unlink(DAEMON_PIDS) | |
284 except IOError: | |
285 pass | |
286 | |
234 os.chdir(TESTDIR) | 287 os.chdir(TESTDIR) |
235 shutil.rmtree(tmpd, True) | 288 shutil.rmtree(tmpd, True) |
236 return ret == 0 | 289 return ret == 0 |
237 | 290 |
238 | 291 |
250 os.environ["HGUSER"] = "test" | 303 os.environ["HGUSER"] = "test" |
251 os.environ["HGRCPATH"] = "" | 304 os.environ["HGRCPATH"] = "" |
252 | 305 |
253 TESTDIR = os.environ["TESTDIR"] = os.getcwd() | 306 TESTDIR = os.environ["TESTDIR"] = os.getcwd() |
254 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.") | 307 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.") |
308 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') | |
309 | |
255 vlog("# Using TESTDIR", TESTDIR) | 310 vlog("# Using TESTDIR", TESTDIR) |
256 vlog("# Using HGTMP", HGTMP) | 311 vlog("# Using HGTMP", HGTMP) |
257 | 312 |
258 INST = os.path.join(HGTMP, "install") | 313 INST = os.path.join(HGTMP, "install") |
259 BINDIR = os.path.join(INST, "bin") | 314 BINDIR = os.path.join(INST, "bin") |
261 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") | 316 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") |
262 | 317 |
263 try: | 318 try: |
264 try: | 319 try: |
265 install_hg() | 320 install_hg() |
321 | |
322 if options.timeout > 0: | |
323 try: | |
324 signal.signal(signal.SIGALRM, alarmed) | |
325 vlog('# Running tests with %d-second timeout' % | |
326 options.timeout) | |
327 except AttributeError: | |
328 print 'WARNING: cannot run tests with timeouts' | |
329 options.timeout = 0 | |
266 | 330 |
267 tests = 0 | 331 tests = 0 |
268 failed = 0 | 332 failed = 0 |
269 | 333 |
270 if len(args) == 0: | 334 if len(args) == 0: |