##// END OF EJS Templates
run-tests: factor out parse_args(). Clarify use of globals a bit.
Greg Ward -
r8091:e85cc856 default
parent child Browse files
Show More
@@ -1,697 +1,709 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms
8 8 # of the GNU General Public License, incorporated herein by reference.
9 9
10 10 import difflib
11 11 import errno
12 12 import optparse
13 13 import os
14 14 try:
15 15 import subprocess
16 16 subprocess.Popen # trigger ImportError early
17 17 closefds = os.name == 'posix'
18 18 def Popen4(cmd, bufsize=-1):
19 19 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
20 20 close_fds=closefds,
21 21 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
22 22 stderr=subprocess.STDOUT)
23 23 p.fromchild = p.stdout
24 24 p.tochild = p.stdin
25 25 p.childerr = p.stderr
26 26 return p
27 27 except ImportError:
28 28 subprocess = None
29 29 from popen2 import Popen4
30 30 import shutil
31 31 import signal
32 32 import sys
33 33 import tempfile
34 34 import time
35 35
36 36 # reserved exit code to skip test (used by hghave)
37 37 SKIPPED_STATUS = 80
38 38 SKIPPED_PREFIX = 'skipped: '
39 39 FAILED_PREFIX = 'hghave check failed: '
40 40
41 41 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
42 42
43 43 defaults = {
44 44 'jobs': ('HGTEST_JOBS', 1),
45 45 'timeout': ('HGTEST_TIMEOUT', 180),
46 46 'port': ('HGTEST_PORT', 20059),
47 47 }
48 48
49 # globals set by parse_args() (ugh)
50 verbose = False
51 nodiff = False
52 coverage = False
53 python = None
54
55 def parse_args():
49 56 parser = optparse.OptionParser("%prog [options] [tests]")
50 57 parser.add_option("-C", "--annotate", action="store_true",
51 58 help="output files annotated with coverage")
52 59 parser.add_option("--child", type="int",
53 60 help="run as child process, summary to given fd")
54 61 parser.add_option("-c", "--cover", action="store_true",
55 62 help="print a test coverage report")
56 63 parser.add_option("-f", "--first", action="store_true",
57 64 help="exit on the first test failure")
58 65 parser.add_option("-i", "--interactive", action="store_true",
59 66 help="prompt to accept changed output")
60 67 parser.add_option("-j", "--jobs", type="int",
61 68 help="number of jobs to run in parallel"
62 69 " (default: $%s or %d)" % defaults['jobs'])
63 70 parser.add_option("--keep-tmpdir", action="store_true",
64 71 help="keep temporary directory after running tests"
65 72 " (best used with --tmpdir)")
66 73 parser.add_option("-R", "--restart", action="store_true",
67 74 help="restart at last error")
68 75 parser.add_option("-p", "--port", type="int",
69 76 help="port on which servers should listen"
70 77 " (default: $%s or %d)" % defaults['port'])
71 78 parser.add_option("-r", "--retest", action="store_true",
72 79 help="retest failed tests")
73 80 parser.add_option("-s", "--cover_stdlib", action="store_true",
74 81 help="print a test coverage report inc. standard libraries")
75 82 parser.add_option("-t", "--timeout", type="int",
76 83 help="kill errant tests after TIMEOUT seconds"
77 84 " (default: $%s or %d)" % defaults['timeout'])
78 85 parser.add_option("--tmpdir", type="string",
79 86 help="run tests in the given temporary directory")
80 87 parser.add_option("-v", "--verbose", action="store_true",
81 88 help="output verbose messages")
82 89 parser.add_option("-n", "--nodiff", action="store_true",
83 90 help="skip showing test changes")
84 91 parser.add_option("--with-hg", type="string",
85 92 help="test existing install at given location")
86 93 parser.add_option("--pure", action="store_true",
87 94 help="use pure Python code instead of C extensions")
88 95
89 96 for option, default in defaults.items():
90 97 defaults[option] = int(os.environ.get(*default))
91 98 parser.set_defaults(**defaults)
92 99 (options, args) = parser.parse_args()
100
101 global verbose, nodiff, coverage, python
93 102 verbose = options.verbose
94 103 nodiff = options.nodiff
95 104 coverage = options.cover or options.cover_stdlib or options.annotate
96 105 python = sys.executable
97 106
98 107 if options.jobs < 1:
99 108 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
100 109 sys.exit(1)
101 110 if options.interactive and options.jobs > 1:
102 111 print '(--interactive overrides --jobs)'
103 112 options.jobs = 1
104 113
114 return (options, args)
115
105 116 def rename(src, dst):
106 117 """Like os.rename(), trade atomicity and opened files friendliness
107 118 for existing destination support.
108 119 """
109 120 shutil.copy(src, dst)
110 121 os.remove(src)
111 122
112 123 def vlog(*msg):
113 124 if verbose:
114 125 for m in msg:
115 126 print m,
116 127 print
117 128
118 129 def splitnewlines(text):
119 130 '''like str.splitlines, but only split on newlines.
120 131 keep line endings.'''
121 132 i = 0
122 133 lines = []
123 134 while True:
124 135 n = text.find('\n', i)
125 136 if n == -1:
126 137 last = text[i:]
127 138 if last:
128 139 lines.append(last)
129 140 return lines
130 141 lines.append(text[i:n+1])
131 142 i = n + 1
132 143
133 144 def parse_hghave_output(lines):
134 145 '''Parse hghave log lines.
135 146 Return tuple of lists (missing, failed):
136 147 * the missing/unknown features
137 148 * the features for which existence check failed'''
138 149 missing = []
139 150 failed = []
140 151 for line in lines:
141 152 if line.startswith(SKIPPED_PREFIX):
142 153 line = line.splitlines()[0]
143 154 missing.append(line[len(SKIPPED_PREFIX):])
144 155 elif line.startswith(FAILED_PREFIX):
145 156 line = line.splitlines()[0]
146 157 failed.append(line[len(FAILED_PREFIX):])
147 158
148 159 return missing, failed
149 160
150 161 def show_diff(expected, output):
151 162 for line in difflib.unified_diff(expected, output,
152 163 "Expected output", "Test output"):
153 164 sys.stdout.write(line)
154 165
155 166 def find_program(program):
156 167 """Search PATH for a executable program"""
157 168 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
158 169 name = os.path.join(p, program)
159 170 if os.access(name, os.X_OK):
160 171 return name
161 172 return None
162 173
163 174 def check_required_tools():
164 175 # Before we go any further, check for pre-requisite tools
165 176 # stuff from coreutils (cat, rm, etc) are not tested
166 177 for p in required_tools:
167 178 if os.name == 'nt':
168 179 p += '.exe'
169 180 found = find_program(p)
170 181 if found:
171 182 vlog("# Found prerequisite", p, "at", found)
172 183 else:
173 184 print "WARNING: Did not find prerequisite tool: "+p
174 185
175 186 def cleanup_exit():
176 187 if not options.keep_tmpdir:
177 188 if verbose:
178 189 print "# Cleaning up HGTMP", HGTMP
179 190 shutil.rmtree(HGTMP, True)
180 191
181 192 def use_correct_python():
182 193 # some tests run python interpreter. they must use same
183 194 # interpreter we use or bad things will happen.
184 195 exedir, exename = os.path.split(sys.executable)
185 196 if exename == 'python':
186 197 path = find_program('python')
187 198 if os.path.dirname(path) == exedir:
188 199 return
189 200 vlog('# Making python executable in test path use correct Python')
190 201 my_python = os.path.join(BINDIR, 'python')
191 202 try:
192 203 os.symlink(sys.executable, my_python)
193 204 except AttributeError:
194 205 # windows fallback
195 206 shutil.copyfile(sys.executable, my_python)
196 207 shutil.copymode(sys.executable, my_python)
197 208
198 209 def install_hg():
199 210 global python
200 211 vlog("# Performing temporary installation of HG")
201 212 installerrs = os.path.join("tests", "install.err")
202 213 pure = options.pure and "--pure" or ""
203 214
204 215 # Run installer in hg root
205 216 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
206 217 cmd = ('%s setup.py %s clean --all'
207 218 ' install --force --prefix="%s" --install-lib="%s"'
208 219 ' --install-scripts="%s" >%s 2>&1'
209 220 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
210 221 vlog("# Running", cmd)
211 222 if os.system(cmd) == 0:
212 223 if not verbose:
213 224 os.remove(installerrs)
214 225 else:
215 226 f = open(installerrs)
216 227 for line in f:
217 228 print line,
218 229 f.close()
219 230 sys.exit(1)
220 231 os.chdir(TESTDIR)
221 232
222 233 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
223 234
224 235 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
225 236 pythonpath = os.environ.get("PYTHONPATH")
226 237 if pythonpath:
227 238 pythonpath = pydir + os.pathsep + pythonpath
228 239 else:
229 240 pythonpath = pydir
230 241 os.environ["PYTHONPATH"] = pythonpath
231 242
232 243 use_correct_python()
233 244 global hgpkg
234 245 hgpkg = _hgpath()
235 246
236 247 vlog("# Installing dummy diffstat")
237 248 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
238 249 f.write('#!' + sys.executable + '\n'
239 250 'import sys\n'
240 251 'files = 0\n'
241 252 'for line in sys.stdin:\n'
242 253 ' if line.startswith("diff "):\n'
243 254 ' files += 1\n'
244 255 'sys.stdout.write("files patched: %d\\n" % files)\n')
245 256 f.close()
246 257 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
247 258
248 259 if coverage:
249 260 vlog("# Installing coverage wrapper")
250 261 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
251 262 if os.path.exists(COVERAGE_FILE):
252 263 os.unlink(COVERAGE_FILE)
253 264 # Create a wrapper script to invoke hg via coverage.py
254 265 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
255 266 f = open(os.path.join(BINDIR, 'hg'), 'w')
256 267 f.write('#!' + sys.executable + '\n')
257 268 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
258 269 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
259 270 (os.path.join(TESTDIR, 'coverage.py'),
260 271 os.path.join(BINDIR, '_hg.py')))
261 272 f.close()
262 273 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
263 274 python = '"%s" "%s" -x' % (sys.executable,
264 275 os.path.join(TESTDIR,'coverage.py'))
265 276
266 277 def output_coverage():
267 278 vlog("# Producing coverage report")
268 279 omit = [BINDIR, TESTDIR, PYTHONDIR]
269 280 if not options.cover_stdlib:
270 281 # Exclude as system paths (ignoring empty strings seen on win)
271 282 omit += [x for x in sys.path if x != '']
272 283 omit = ','.join(omit)
273 284 os.chdir(PYTHONDIR)
274 285 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
275 286 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
276 287 vlog("# Running: "+cmd)
277 288 os.system(cmd)
278 289 if options.annotate:
279 290 adir = os.path.join(TESTDIR, 'annotated')
280 291 if not os.path.isdir(adir):
281 292 os.mkdir(adir)
282 293 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
283 294 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
284 295 adir, omit)
285 296 vlog("# Running: "+cmd)
286 297 os.system(cmd)
287 298
288 299 class Timeout(Exception):
289 300 pass
290 301
291 302 def alarmed(signum, frame):
292 303 raise Timeout
293 304
294 305 def run(cmd):
295 306 """Run command in a sub-process, capturing the output (stdout and stderr).
296 307 Return the exist code, and output."""
297 308 # TODO: Use subprocess.Popen if we're running on Python 2.4
298 309 if os.name == 'nt' or sys.platform.startswith('java'):
299 310 tochild, fromchild = os.popen4(cmd)
300 311 tochild.close()
301 312 output = fromchild.read()
302 313 ret = fromchild.close()
303 314 if ret == None:
304 315 ret = 0
305 316 else:
306 317 proc = Popen4(cmd)
307 318 try:
308 319 output = ''
309 320 proc.tochild.close()
310 321 output = proc.fromchild.read()
311 322 ret = proc.wait()
312 323 if os.WIFEXITED(ret):
313 324 ret = os.WEXITSTATUS(ret)
314 325 except Timeout:
315 326 vlog('# Process %d timed out - killing it' % proc.pid)
316 327 os.kill(proc.pid, signal.SIGTERM)
317 328 ret = proc.wait()
318 329 if ret == 0:
319 330 ret = signal.SIGTERM << 8
320 331 output += ("\n### Abort: timeout after %d seconds.\n"
321 332 % options.timeout)
322 333 return ret, splitnewlines(output)
323 334
324 335 def run_one(test, skips, fails):
325 336 '''tristate output:
326 337 None -> skipped
327 338 True -> passed
328 339 False -> failed'''
329 340
330 341 def skip(msg):
331 342 if not verbose:
332 343 skips.append((test, msg))
333 344 else:
334 345 print "\nSkipping %s: %s" % (test, msg)
335 346 return None
336 347
337 348 def fail(msg):
338 349 fails.append((test, msg))
339 350 if not nodiff:
340 351 print "\nERROR: %s %s" % (test, msg)
341 352 return None
342 353
343 354 vlog("# Test", test)
344 355
345 356 # create a fresh hgrc
346 357 hgrc = file(HGRCPATH, 'w+')
347 358 hgrc.write('[ui]\n')
348 359 hgrc.write('slash = True\n')
349 360 hgrc.write('[defaults]\n')
350 361 hgrc.write('backout = -d "0 0"\n')
351 362 hgrc.write('commit = -d "0 0"\n')
352 363 hgrc.write('debugrawcommit = -d "0 0"\n')
353 364 hgrc.write('tag = -d "0 0"\n')
354 365 hgrc.close()
355 366
356 367 err = os.path.join(TESTDIR, test+".err")
357 368 ref = os.path.join(TESTDIR, test+".out")
358 369 testpath = os.path.join(TESTDIR, test)
359 370
360 371 if os.path.exists(err):
361 372 os.remove(err) # Remove any previous output files
362 373
363 374 # Make a tmp subdirectory to work in
364 375 tmpd = os.path.join(HGTMP, test)
365 376 os.mkdir(tmpd)
366 377 os.chdir(tmpd)
367 378
368 379 try:
369 380 tf = open(testpath)
370 381 firstline = tf.readline().rstrip()
371 382 tf.close()
372 383 except:
373 384 firstline = ''
374 385 lctest = test.lower()
375 386
376 387 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
377 388 cmd = '%s "%s"' % (python, testpath)
378 389 elif lctest.endswith('.bat'):
379 390 # do not run batch scripts on non-windows
380 391 if os.name != 'nt':
381 392 return skip("batch script")
382 393 # To reliably get the error code from batch files on WinXP,
383 394 # the "cmd /c call" prefix is needed. Grrr
384 395 cmd = 'cmd /c call "%s"' % testpath
385 396 else:
386 397 # do not run shell scripts on windows
387 398 if os.name == 'nt':
388 399 return skip("shell script")
389 400 # do not try to run non-executable programs
390 401 if not os.path.exists(testpath):
391 402 return fail("does not exist")
392 403 elif not os.access(testpath, os.X_OK):
393 404 return skip("not executable")
394 405 cmd = '"%s"' % testpath
395 406
396 407 if options.timeout > 0:
397 408 signal.alarm(options.timeout)
398 409
399 410 vlog("# Running", cmd)
400 411 ret, out = run(cmd)
401 412 vlog("# Ret was:", ret)
402 413
403 414 if options.timeout > 0:
404 415 signal.alarm(0)
405 416
406 417 mark = '.'
407 418
408 419 skipped = (ret == SKIPPED_STATUS)
409 420 # If reference output file exists, check test output against it
410 421 if os.path.exists(ref):
411 422 f = open(ref, "r")
412 423 ref_out = splitnewlines(f.read())
413 424 f.close()
414 425 else:
415 426 ref_out = []
416 427 if skipped:
417 428 mark = 's'
418 429 missing, failed = parse_hghave_output(out)
419 430 if not missing:
420 431 missing = ['irrelevant']
421 432 if failed:
422 433 fail("hghave failed checking for %s" % failed[-1])
423 434 skipped = False
424 435 else:
425 436 skip(missing[-1])
426 437 elif out != ref_out:
427 438 mark = '!'
428 439 if ret:
429 440 fail("output changed and returned error code %d" % ret)
430 441 else:
431 442 fail("output changed")
432 443 if not nodiff:
433 444 show_diff(ref_out, out)
434 445 ret = 1
435 446 elif ret:
436 447 mark = '!'
437 448 fail("returned error code %d" % ret)
438 449
439 450 if not verbose:
440 451 sys.stdout.write(mark)
441 452 sys.stdout.flush()
442 453
443 454 if ret != 0 and not skipped:
444 455 # Save errors to a file for diagnosis
445 456 f = open(err, "wb")
446 457 for line in out:
447 458 f.write(line)
448 459 f.close()
449 460
450 461 # Kill off any leftover daemon processes
451 462 try:
452 463 fp = file(DAEMON_PIDS)
453 464 for line in fp:
454 465 try:
455 466 pid = int(line)
456 467 except ValueError:
457 468 continue
458 469 try:
459 470 os.kill(pid, 0)
460 471 vlog('# Killing daemon process %d' % pid)
461 472 os.kill(pid, signal.SIGTERM)
462 473 time.sleep(0.25)
463 474 os.kill(pid, 0)
464 475 vlog('# Daemon process %d is stuck - really killing it' % pid)
465 476 os.kill(pid, signal.SIGKILL)
466 477 except OSError, err:
467 478 if err.errno != errno.ESRCH:
468 479 raise
469 480 fp.close()
470 481 os.unlink(DAEMON_PIDS)
471 482 except IOError:
472 483 pass
473 484
474 485 os.chdir(TESTDIR)
475 486 if not options.keep_tmpdir:
476 487 shutil.rmtree(tmpd, True)
477 488 if skipped:
478 489 return None
479 490 return ret == 0
480 491
492 (options, args) = parse_args()
481 493 if not options.child:
482 494 os.umask(022)
483 495
484 496 check_required_tools()
485 497
486 498 # Reset some environment variables to well-known values so that
487 499 # the tests produce repeatable output.
488 500 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
489 501 os.environ['TZ'] = 'GMT'
490 502 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
491 503 os.environ['CDPATH'] = ''
492 504
493 505 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
494 506 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
495 507 options.tmpdir))
496 508 DAEMON_PIDS = None
497 509 HGRCPATH = None
498 510
499 511 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
500 512 os.environ["HGMERGE"] = "internal:merge"
501 513 os.environ["HGUSER"] = "test"
502 514 os.environ["HGENCODING"] = "ascii"
503 515 os.environ["HGENCODINGMODE"] = "strict"
504 516 os.environ["HGPORT"] = str(options.port)
505 517 os.environ["HGPORT1"] = str(options.port + 1)
506 518 os.environ["HGPORT2"] = str(options.port + 2)
507 519
508 520 if options.with_hg:
509 521 INST = options.with_hg
510 522 else:
511 523 INST = os.path.join(HGTMP, "install")
512 524 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
513 525 PYTHONDIR = os.path.join(INST, "lib", "python")
514 526 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
515 527
516 528 def _hgpath():
517 529 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
518 530 hgpath = os.popen(cmd % python)
519 531 path = hgpath.read().strip()
520 532 hgpath.close()
521 533 return path
522 534
523 535 expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
524 536 hgpkg = None
525 537
526 538 def run_children(tests):
527 539 if not options.with_hg:
528 540 install_hg()
529 541 if hgpkg != expecthg:
530 542 print '# Testing unexpected mercurial: %s' % hgpkg
531 543
532 544 optcopy = dict(options.__dict__)
533 545 optcopy['jobs'] = 1
534 546 optcopy['with_hg'] = INST
535 547 opts = []
536 548 for opt, value in optcopy.iteritems():
537 549 name = '--' + opt.replace('_', '-')
538 550 if value is True:
539 551 opts.append(name)
540 552 elif value is not None:
541 553 opts.append(name + '=' + str(value))
542 554
543 555 tests.reverse()
544 556 jobs = [[] for j in xrange(options.jobs)]
545 557 while tests:
546 558 for j in xrange(options.jobs):
547 559 if not tests: break
548 560 jobs[j].append(tests.pop())
549 561 fps = {}
550 562 for j in xrange(len(jobs)):
551 563 job = jobs[j]
552 564 if not job:
553 565 continue
554 566 rfd, wfd = os.pipe()
555 567 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
556 568 cmdline = [python, sys.argv[0]] + opts + childopts + job
557 569 vlog(' '.join(cmdline))
558 570 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
559 571 os.close(wfd)
560 572 failures = 0
561 573 tested, skipped, failed = 0, 0, 0
562 574 skips = []
563 575 fails = []
564 576 while fps:
565 577 pid, status = os.wait()
566 578 fp = fps.pop(pid)
567 579 l = fp.read().splitlines()
568 580 test, skip, fail = map(int, l[:3])
569 581 split = -fail or len(l)
570 582 for s in l[3:split]:
571 583 skips.append(s.split(" ", 1))
572 584 for s in l[split:]:
573 585 fails.append(s.split(" ", 1))
574 586 tested += test
575 587 skipped += skip
576 588 failed += fail
577 589 vlog('pid %d exited, status %d' % (pid, status))
578 590 failures |= status
579 591 print
580 592 for s in skips:
581 593 print "Skipped %s: %s" % (s[0], s[1])
582 594 for s in fails:
583 595 print "Failed %s: %s" % (s[0], s[1])
584 596
585 597 if hgpkg != expecthg:
586 598 print '# Tested unexpected mercurial: %s' % hgpkg
587 599 print "# Ran %d tests, %d skipped, %d failed." % (
588 600 tested, skipped, failed)
589 601 sys.exit(failures != 0)
590 602
591 603 def run_tests(tests):
592 604 global DAEMON_PIDS, HGRCPATH
593 605 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
594 606 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
595 607
596 608 try:
597 609 if not options.with_hg:
598 610 install_hg()
599 611
600 612 if hgpkg != expecthg:
601 613 print '# Testing unexpected mercurial: %s' % hgpkg
602 614
603 615 if options.timeout > 0:
604 616 try:
605 617 signal.signal(signal.SIGALRM, alarmed)
606 618 vlog('# Running tests with %d-second timeout' %
607 619 options.timeout)
608 620 except AttributeError:
609 621 print 'WARNING: cannot run tests with timeouts'
610 622 options.timeout = 0
611 623
612 624 tested = 0
613 625 failed = 0
614 626 skipped = 0
615 627
616 628 if options.restart:
617 629 orig = list(tests)
618 630 while tests:
619 631 if os.path.exists(tests[0] + ".err"):
620 632 break
621 633 tests.pop(0)
622 634 if not tests:
623 635 print "running all tests"
624 636 tests = orig
625 637
626 638 skips = []
627 639 fails = []
628 640 for test in tests:
629 641 if options.retest and not os.path.exists(test + ".err"):
630 642 skipped += 1
631 643 continue
632 644 ret = run_one(test, skips, fails)
633 645 if ret is None:
634 646 skipped += 1
635 647 elif not ret:
636 648 if options.interactive:
637 649 print "Accept this change? [n] ",
638 650 answer = sys.stdin.readline().strip()
639 651 if answer.lower() in "y yes".split():
640 652 rename(test + ".err", test + ".out")
641 653 tested += 1
642 654 fails.pop()
643 655 continue
644 656 failed += 1
645 657 if options.first:
646 658 break
647 659 tested += 1
648 660
649 661 if options.child:
650 662 fp = os.fdopen(options.child, 'w')
651 663 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
652 664 for s in skips:
653 665 fp.write("%s %s\n" % s)
654 666 for s in fails:
655 667 fp.write("%s %s\n" % s)
656 668 fp.close()
657 669 else:
658 670 print
659 671 for s in skips:
660 672 print "Skipped %s: %s" % s
661 673 for s in fails:
662 674 print "Failed %s: %s" % s
663 675 if hgpkg != expecthg:
664 676 print '# Tested unexpected mercurial: %s' % hgpkg
665 677 print "# Ran %d tests, %d skipped, %d failed." % (
666 678 tested, skipped, failed)
667 679
668 680 if coverage:
669 681 output_coverage()
670 682 except KeyboardInterrupt:
671 683 failed = True
672 684 print "\ninterrupted!"
673 685
674 686 if failed:
675 687 sys.exit(1)
676 688
677 689 if len(args) == 0:
678 690 args = os.listdir(".")
679 691 args.sort()
680 692
681 693 tests = []
682 694 for test in args:
683 695 if (test.startswith("test-") and '~' not in test and
684 696 ('.' not in test or test.endswith('.py') or
685 697 test.endswith('.bat'))):
686 698 tests.append(test)
687 699
688 700 vlog("# Using TESTDIR", TESTDIR)
689 701 vlog("# Using HGTMP", HGTMP)
690 702
691 703 try:
692 704 if len(tests) > 1 and options.jobs > 1:
693 705 run_children(tests)
694 706 else:
695 707 run_tests(tests)
696 708 finally:
697 709 cleanup_exit()
General Comments 0
You need to be logged in to leave comments. Login now