tests/run-tests.py
changeset 5384 e3a0c092b4e2
parent 5383 7cdc896fdcd5
child 5388 557e4a916e12
equal deleted inserted replaced
5383:7cdc896fdcd5 5384:e3a0c092b4e2
    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' %
   413                 options.timeout = 0
   483                 options.timeout = 0
   414 
   484 
   415         tested = 0
   485         tested = 0
   416         failed = 0
   486         failed = 0
   417         skipped = 0
   487         skipped = 0
   418 
       
   419         if len(args) == 0:
       
   420             args = os.listdir(".")
       
   421             args.sort()
       
   422 
       
   423 
       
   424         tests = []
       
   425         for test in args:
       
   426             if (test.startswith("test-") and '~' not in test and
       
   427                 ('.' not in test or test.endswith('.py') or
       
   428                  test.endswith('.bat'))):
       
   429                 tests.append(test)
       
   430 
   488 
   431         if options.restart:
   489         if options.restart:
   432             orig = list(tests)
   490             orig = list(tests)
   433             while tests:
   491             while tests:
   434                 if os.path.exists(tests[0] + ".err"):
   492                 if os.path.exists(tests[0] + ".err"):
   456                 failed += 1
   514                 failed += 1
   457                 if options.first:
   515                 if options.first:
   458                     break
   516                     break
   459             tested += 1
   517             tested += 1
   460 
   518 
   461         print "\n# Ran %d tests, %d skipped, %d failed." % (tested, skipped,
   519         if options.child:
   462                                                             failed)
   520             fp = os.fdopen(options.child, 'w')
       
   521             fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
       
   522             fp.close()
       
   523         else:
       
   524             print "\n# Ran %d tests, %d skipped, %d failed." % (
       
   525                 tested, skipped, failed)
       
   526 
   463         if coverage:
   527         if coverage:
   464             output_coverage()
   528             output_coverage()
   465     except KeyboardInterrupt:
   529     except KeyboardInterrupt:
   466         failed = True
   530         failed = True
   467         print "\ninterrupted!"
   531         print "\ninterrupted!"
       
   532 
       
   533     if failed:
       
   534         sys.exit(1)
       
   535 
       
   536 if len(args) == 0:
       
   537     args = os.listdir(".")
       
   538     args.sort()
       
   539 
       
   540 tests = []
       
   541 for test in args:
       
   542     if (test.startswith("test-") and '~' not in test and
       
   543         ('.' not in test or test.endswith('.py') or
       
   544          test.endswith('.bat'))):
       
   545         tests.append(test)
       
   546 
       
   547 vlog("# Using TESTDIR", TESTDIR)
       
   548 vlog("# Using HGTMP", HGTMP)
       
   549 
       
   550 try:
       
   551     if len(tests) > 1 and options.jobs > 1:
       
   552         run_children(tests)
       
   553     else:
       
   554         run_tests(tests)
   468 finally:
   555 finally:
   469     cleanup_exit()
   556     cleanup_exit()
   470 
       
   471 if failed:
       
   472     sys.exit(1)