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