tests/run-tests.py
branchstable
changeset 718 88d7be7899ac
parent 693 8428f3bda904
child 719 ad3048630bd8
equal deleted inserted replaced
717:43efa35c5eed 718:88d7be7899ac
    39 # (You could use any subset of the tests: test-s* happens to match
    39 # (You could use any subset of the tests: test-s* happens to match
    40 # enough that it's worth doing parallel runs, few enough that it
    40 # enough that it's worth doing parallel runs, few enough that it
    41 # completes fairly quickly, includes both shell and Python scripts, and
    41 # completes fairly quickly, includes both shell and Python scripts, and
    42 # includes some scripts that run daemon processes.)
    42 # includes some scripts that run daemon processes.)
    43 
    43 
       
    44 from distutils import version
    44 import difflib
    45 import difflib
    45 import errno
    46 import errno
    46 import optparse
    47 import optparse
    47 import os
    48 import os
    48 import signal
    49 import signal
   108     parser.add_option("-p", "--port", type="int",
   109     parser.add_option("-p", "--port", type="int",
   109         help="port on which servers should listen"
   110         help="port on which servers should listen"
   110              " (default: $%s or %d)" % defaults['port'])
   111              " (default: $%s or %d)" % defaults['port'])
   111     parser.add_option("-r", "--retest", action="store_true",
   112     parser.add_option("-r", "--retest", action="store_true",
   112         help="retest failed tests")
   113         help="retest failed tests")
   113     parser.add_option("-s", "--cover_stdlib", action="store_true",
       
   114         help="print a test coverage report inc. standard libraries")
       
   115     parser.add_option("-S", "--noskips", action="store_true",
   114     parser.add_option("-S", "--noskips", action="store_true",
   116         help="don't report skip tests verbosely")
   115         help="don't report skip tests verbosely")
   117     parser.add_option("-t", "--timeout", type="int",
   116     parser.add_option("-t", "--timeout", type="int",
   118         help="kill errant tests after TIMEOUT seconds"
   117         help="kill errant tests after TIMEOUT seconds"
   119              " (default: $%s or %d)" % defaults['timeout'])
   118              " (default: $%s or %d)" % defaults['timeout'])
   153         if not os.access(hgbin, os.X_OK):
   152         if not os.access(hgbin, os.X_OK):
   154             parser.error('--local specified, but %r not found or not executable'
   153             parser.error('--local specified, but %r not found or not executable'
   155                          % hgbin)
   154                          % hgbin)
   156         options.with_hg = hgbin
   155         options.with_hg = hgbin
   157 
   156 
   158     options.anycoverage = (options.cover or
   157     options.anycoverage = options.cover or options.annotate
   159                            options.cover_stdlib or
   158     if options.anycoverage:
   160                            options.annotate)
   159         try:
   161 
   160             import coverage
   162     if options.anycoverage and options.with_hg:
   161             covver = version.StrictVersion(coverage.__version__).version
   163         # I'm not sure if this is a fundamental limitation or just a
   162             if covver < (3, 3):
   164         # bug.  But I don't want to waste people's time and energy doing
   163                 parser.error('coverage options require coverage 3.3 or later')
   165         # test runs that don't give the results they want.
   164         except ImportError:
   166         parser.error("sorry, coverage options do not work when --with-hg "
   165             parser.error('coverage options now require the coverage package')
   167                      "or --local specified")
   166 
       
   167     if options.anycoverage and options.local:
       
   168         # this needs some path mangling somewhere, I guess
       
   169         parser.error("sorry, coverage options do not work when --local "
       
   170                      "is specified")
   168 
   171 
   169     global vlog
   172     global vlog
   170     if options.verbose:
   173     if options.verbose:
   171         if options.jobs > 1 or options.child is not None:
   174         if options.jobs > 1 or options.child is not None:
   172             pid = "[%d]" % os.getpid()
   175             pid = "[%d]" % os.getpid()
   388         for line in lines:
   391         for line in lines:
   389             f.write(line + '\n')
   392             f.write(line + '\n')
   390         f.close()
   393         f.close()
   391 
   394 
   392     if options.anycoverage:
   395     if options.anycoverage:
   393         vlog("# Installing coverage wrapper")
   396         custom = os.path.join(TESTDIR, 'sitecustomize.py')
   394         os.environ['COVERAGE_FILE'] = COVERAGE_FILE
   397         target = os.path.join(PYTHONDIR, 'sitecustomize.py')
   395         if os.path.exists(COVERAGE_FILE):
   398         vlog('# Installing coverage trigger to %s' % target)
   396             os.unlink(COVERAGE_FILE)
   399         shutil.copyfile(custom, target)
   397         # Create a wrapper script to invoke hg via coverage.py
   400         rc = os.path.join(TESTDIR, '.coveragerc')
   398         os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
   401         vlog('# Installing coverage rc to %s' % rc)
   399         f = open(os.path.join(BINDIR, 'hg'), 'w')
   402         os.environ['COVERAGE_PROCESS_START'] = rc
   400         f.write('#!' + sys.executable + '\n')
   403         fn = os.path.join(INST, '..', '.coverage')
   401         f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
   404         os.environ['COVERAGE_FILE'] = fn
   402                 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
       
   403                 (os.path.join(TESTDIR, 'coverage.py'),
       
   404                  os.path.join(BINDIR, '_hg.py')))
       
   405         f.close()
       
   406         os.chmod(os.path.join(BINDIR, 'hg'), 0700)
       
   407 
   405 
   408 def outputcoverage(options):
   406 def outputcoverage(options):
   409 
   407 
   410     vlog('# Producing coverage report')
   408     vlog('# Producing coverage report')
   411     os.chdir(PYTHONDIR)
   409     os.chdir(PYTHONDIR)
   412 
   410 
   413     def covrun(*args):
   411     def covrun(*args):
   414         start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
   412         cmd = 'coverage %s' % ' '.join(args)
   415         cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
       
   416         vlog('# Running: %s' % cmd)
   413         vlog('# Running: %s' % cmd)
   417         os.system(cmd)
   414         os.system(cmd)
   418 
   415 
   419     omit = [BINDIR, TESTDIR, PYTHONDIR]
   416     if options.child:
   420     if not options.cover_stdlib:
   417         return
   421         # Exclude as system paths (ignoring empty strings seen on win)
   418 
   422         omit += [x for x in sys.path if x != '']
   419     covrun('-c')
   423     omit = ','.join(omit)
   420     omit = ','.join([BINDIR, TESTDIR])
   424 
       
   425     covrun('-c') # combine from parallel processes
       
   426     for fn in os.listdir(TESTDIR):
       
   427         if fn.startswith('.coverage.'):
       
   428             os.unlink(os.path.join(TESTDIR, fn))
       
   429 
       
   430     covrun('-i', '-r', '"--omit=%s"' % omit) # report
   421     covrun('-i', '-r', '"--omit=%s"' % omit) # report
   431     if options.annotate:
   422     if options.annotate:
   432         adir = os.path.join(TESTDIR, 'annotated')
   423         adir = os.path.join(TESTDIR, 'annotated')
   433         if not os.path.isdir(adir):
   424         if not os.path.isdir(adir):
   434             os.mkdir(adir)
   425             os.mkdir(adir)
   666 
   657 
   667     optcopy = dict(options.__dict__)
   658     optcopy = dict(options.__dict__)
   668     optcopy['jobs'] = 1
   659     optcopy['jobs'] = 1
   669     if optcopy['with_hg'] is None:
   660     if optcopy['with_hg'] is None:
   670         optcopy['with_hg'] = os.path.join(BINDIR, "hg")
   661         optcopy['with_hg'] = os.path.join(BINDIR, "hg")
       
   662     optcopy.pop('anycoverage', None)
       
   663 
   671     opts = []
   664     opts = []
   672     for opt, value in optcopy.iteritems():
   665     for opt, value in optcopy.iteritems():
   673         name = '--' + opt.replace('_', '-')
   666         name = '--' + opt.replace('_', '-')
   674         if value is True:
   667         if value is True:
   675             opts.append(name)
   668             opts.append(name)
   727         print "Failed %s: %s" % (s[0], s[1])
   720         print "Failed %s: %s" % (s[0], s[1])
   728 
   721 
   729     _checkhglib("Tested")
   722     _checkhglib("Tested")
   730     print "# Ran %d tests, %d skipped, %d failed." % (
   723     print "# Ran %d tests, %d skipped, %d failed." % (
   731         tested, skipped, failed)
   724         tested, skipped, failed)
       
   725 
       
   726     if options.anycoverage:
       
   727         outputcoverage(options)
   732     sys.exit(failures != 0)
   728     sys.exit(failures != 0)
   733 
   729 
   734 def runtests(options, tests):
   730 def runtests(options, tests):
   735     global DAEMON_PIDS, HGRCPATH
   731     global DAEMON_PIDS, HGRCPATH
   736     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
   732     DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')