25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] |
25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] |
26 |
26 |
27 parser = optparse.OptionParser("%prog [options] [tests]") |
27 parser = optparse.OptionParser("%prog [options] [tests]") |
28 parser.add_option("-C", "--annotate", action="store_true", |
28 parser.add_option("-C", "--annotate", action="store_true", |
29 help="output files annotated with coverage") |
29 help="output files annotated with coverage") |
|
30 parser.add_option("--child", type="int", |
|
31 help="run as child process, summary to given fd") |
30 parser.add_option("-c", "--cover", action="store_true", |
32 parser.add_option("-c", "--cover", action="store_true", |
31 help="print a test coverage report") |
33 help="print a test coverage report") |
32 parser.add_option("-f", "--first", action="store_true", |
34 parser.add_option("-f", "--first", action="store_true", |
33 help="exit on the first test failure") |
35 help="exit on the first test failure") |
34 parser.add_option("-i", "--interactive", action="store_true", |
36 parser.add_option("-i", "--interactive", action="store_true", |
35 help="prompt to accept changed output") |
37 help="prompt to accept changed output") |
|
38 parser.add_option("-j", "--jobs", type="int", |
|
39 help="number of jobs to run in parallel") |
36 parser.add_option("-R", "--restart", action="store_true", |
40 parser.add_option("-R", "--restart", action="store_true", |
37 help="restart at last error") |
41 help="restart at last error") |
|
42 parser.add_option("-p", "--port", type="int", |
|
43 help="port on which servers should listen") |
38 parser.add_option("-r", "--retest", action="store_true", |
44 parser.add_option("-r", "--retest", action="store_true", |
39 help="retest failed tests") |
45 help="retest failed tests") |
40 parser.add_option("-s", "--cover_stdlib", action="store_true", |
46 parser.add_option("-s", "--cover_stdlib", action="store_true", |
41 help="print a test coverage report inc. standard libraries") |
47 help="print a test coverage report inc. standard libraries") |
42 parser.add_option("-t", "--timeout", type="int", |
48 parser.add_option("-t", "--timeout", type="int", |
43 help="kill errant tests after TIMEOUT seconds") |
49 help="kill errant tests after TIMEOUT seconds") |
44 parser.add_option("-v", "--verbose", action="store_true", |
50 parser.add_option("-v", "--verbose", action="store_true", |
45 help="output verbose messages") |
51 help="output verbose messages") |
46 |
52 parser.add_option("--with-hg", type="string", |
47 parser.set_defaults(timeout=180) |
53 help="test existing install at given location") |
|
54 |
|
55 parser.set_defaults(jobs=1, port=20059, timeout=180) |
48 (options, args) = parser.parse_args() |
56 (options, args) = parser.parse_args() |
49 verbose = options.verbose |
57 verbose = options.verbose |
50 coverage = options.cover or options.cover_stdlib or options.annotate |
58 coverage = options.cover or options.cover_stdlib or options.annotate |
51 python = sys.executable |
59 python = sys.executable |
|
60 |
|
61 if options.jobs < 1: |
|
62 print >> sys.stderr, 'ERROR: -j/--jobs must be positive' |
|
63 sys.exit(1) |
|
64 if options.interactive and options.jobs > 1: |
|
65 print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1' |
|
66 sys.exit(1) |
52 |
67 |
53 def vlog(*msg): |
68 def vlog(*msg): |
54 if verbose: |
69 if verbose: |
55 for m in msg: |
70 for m in msg: |
56 print m, |
71 print m, |
366 shutil.rmtree(tmpd, True) |
381 shutil.rmtree(tmpd, True) |
367 if skipped: |
382 if skipped: |
368 return None |
383 return None |
369 return ret == 0 |
384 return ret == 0 |
370 |
385 |
371 |
386 if not options.child: |
372 os.umask(022) |
387 os.umask(022) |
373 |
388 |
374 check_required_tools() |
389 check_required_tools() |
375 |
390 |
376 # Reset some environment variables to well-known values so that |
391 # Reset some environment variables to well-known values so that |
377 # the tests produce repeatable output. |
392 # the tests produce repeatable output. |
378 os.environ['LANG'] = os.environ['LC_ALL'] = 'C' |
393 os.environ['LANG'] = os.environ['LC_ALL'] = 'C' |
379 os.environ['TZ'] = 'GMT' |
394 os.environ['TZ'] = 'GMT' |
380 |
395 |
381 TESTDIR = os.environ["TESTDIR"] = os.getcwd() |
396 TESTDIR = os.environ["TESTDIR"] = os.getcwd() |
382 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.") |
397 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.") |
383 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |
398 DAEMON_PIDS = None |
384 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc') |
399 HGRCPATH = None |
385 |
400 |
386 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' |
401 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' |
387 os.environ["HGMERGE"] = ('python "%s" -L my -L other' |
402 os.environ["HGMERGE"] = ('python "%s" -L my -L other' |
388 % os.path.join(TESTDIR, os.path.pardir, 'contrib', |
403 % os.path.join(TESTDIR, os.path.pardir, |
389 'simplemerge')) |
404 'contrib', 'simplemerge')) |
390 os.environ["HGUSER"] = "test" |
405 os.environ["HGUSER"] = "test" |
391 os.environ["HGENCODING"] = "ascii" |
406 os.environ["HGENCODING"] = "ascii" |
392 os.environ["HGENCODINGMODE"] = "strict" |
407 os.environ["HGENCODINGMODE"] = "strict" |
393 |
408 os.environ["HGPORT"] = str(options.port) |
394 vlog("# Using TESTDIR", TESTDIR) |
409 os.environ["HGPORT1"] = str(options.port + 1) |
395 vlog("# Using HGTMP", HGTMP) |
410 os.environ["HGPORT2"] = str(options.port + 2) |
396 |
411 |
397 INST = os.path.join(HGTMP, "install") |
412 if options.with_hg: |
|
413 INST = options.with_hg |
|
414 else: |
|
415 INST = os.path.join(HGTMP, "install") |
398 BINDIR = os.path.join(INST, "bin") |
416 BINDIR = os.path.join(INST, "bin") |
399 PYTHONDIR = os.path.join(INST, "lib", "python") |
417 PYTHONDIR = os.path.join(INST, "lib", "python") |
400 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") |
418 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") |
401 |
419 |
402 try: |
420 def run_children(tests): |
|
421 if not options.with_hg: |
|
422 install_hg() |
|
423 |
|
424 optcopy = dict(options.__dict__) |
|
425 optcopy['jobs'] = 1 |
|
426 optcopy['with_hg'] = INST |
|
427 opts = [] |
|
428 for opt, value in optcopy.iteritems(): |
|
429 name = '--' + opt.replace('_', '-') |
|
430 if value is True: |
|
431 opts.append(name) |
|
432 elif value is not None: |
|
433 opts.append(name + '=' + str(value)) |
|
434 |
|
435 tests.reverse() |
|
436 jobs = [[] for j in xrange(options.jobs)] |
|
437 while tests: |
|
438 for j in xrange(options.jobs): |
|
439 if not tests: break |
|
440 jobs[j].append(tests.pop()) |
|
441 fps = {} |
|
442 for j in xrange(len(jobs)): |
|
443 job = jobs[j] |
|
444 if not job: |
|
445 continue |
|
446 rfd, wfd = os.pipe() |
|
447 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)] |
|
448 cmdline = [python, sys.argv[0]] + opts + childopts + job |
|
449 vlog(' '.join(cmdline)) |
|
450 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r') |
|
451 os.close(wfd) |
|
452 failures = 0 |
|
453 tested, skipped, failed = 0, 0, 0 |
|
454 while fps: |
|
455 pid, status = os.wait() |
|
456 fp = fps.pop(pid) |
|
457 test, skip, fail = map(int, fp.read().splitlines()) |
|
458 tested += test |
|
459 skipped += skip |
|
460 failed += fail |
|
461 vlog('pid %d exited, status %d' % (pid, status)) |
|
462 failures |= status |
|
463 print "\n# Ran %d tests, %d skipped, %d failed." % ( |
|
464 tested, skipped, failed) |
|
465 sys.exit(failures != 0) |
|
466 |
|
467 def run_tests(tests): |
|
468 global DAEMON_PIDS, HGRCPATH |
|
469 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |
|
470 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc') |
|
471 |
403 try: |
472 try: |
404 install_hg() |
473 if not options.with_hg: |
|
474 install_hg() |
405 |
475 |
406 if options.timeout > 0: |
476 if options.timeout > 0: |
407 try: |
477 try: |
408 signal.signal(signal.SIGALRM, alarmed) |
478 signal.signal(signal.SIGALRM, alarmed) |
409 vlog('# Running tests with %d-second timeout' % |
479 vlog('# Running tests with %d-second timeout' % |