tests/run-tests.py
changeset 2571 83cfd95eafb5
parent 2570 2264b2b077a1
child 2576 6a961a54f953
equal deleted inserted replaced
2570:2264b2b077a1 2571:83cfd95eafb5
     5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
     5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
     6 #
     6 #
     7 # This software may be used and distributed according to the terms
     7 # This software may be used and distributed according to the terms
     8 # of the GNU General Public License, incorporated herein by reference.
     8 # of the GNU General Public License, incorporated herein by reference.
     9 
     9 
    10 import os, sys, shutil, re
    10 import difflib
       
    11 import errno
       
    12 import optparse
       
    13 import os
       
    14 import popen2
       
    15 import re
       
    16 import shutil
       
    17 import signal
       
    18 import sys
    11 import tempfile
    19 import tempfile
    12 import difflib
    20 import time
    13 import popen2
       
    14 from optparse import OptionParser
       
    15 
    21 
    16 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
    22 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
    17 
    23 
    18 parser = OptionParser("%prog [options] [tests]")
    24 parser = optparse.OptionParser("%prog [options] [tests]")
    19 parser.add_option("-v", "--verbose", action="store_true",
    25 parser.add_option("-v", "--verbose", action="store_true",
       
    26     help="output verbose messages")
       
    27 parser.add_option("-t", "--timeout", type="int",
    20     help="output verbose messages")
    28     help="output verbose messages")
    21 parser.add_option("-c", "--cover", action="store_true",
    29 parser.add_option("-c", "--cover", action="store_true",
    22     help="print a test coverage report")
    30     help="print a test coverage report")
    23 parser.add_option("-s", "--cover_stdlib", action="store_true",
    31 parser.add_option("-s", "--cover_stdlib", action="store_true",
    24     help="print a test coverage report inc. standard libraries")
    32     help="print a test coverage report inc. standard libraries")
    25 parser.add_option("-C", "--annotate", action="store_true",
    33 parser.add_option("-C", "--annotate", action="store_true",
    26     help="output files annotated with coverage")
    34     help="output files annotated with coverage")
       
    35 parser.set_defaults(timeout=30)
    27 (options, args) = parser.parse_args()
    36 (options, args) = parser.parse_args()
    28 verbose = options.verbose
    37 verbose = options.verbose
    29 coverage = options.cover or options.cover_stdlib or options.annotate
    38 coverage = options.cover or options.cover_stdlib or options.annotate
    30 
    39 
    31 def vlog(*msg):
    40 def vlog(*msg):
   157             sys.executable, os.path.join(TESTDIR, 'coverage.py'),
   166             sys.executable, os.path.join(TESTDIR, 'coverage.py'),
   158             adir, omit)
   167             adir, omit)
   159         vlog("# Running: "+cmd)
   168         vlog("# Running: "+cmd)
   160         os.system(cmd)
   169         os.system(cmd)
   161 
   170 
       
   171 class Timeout(Exception):
       
   172     pass
       
   173 
       
   174 def alarmed(signum, frame):
       
   175     raise Timeout
       
   176 
   162 def run(cmd):
   177 def run(cmd):
   163     """Run command in a sub-process, capturing the output (stdout and stderr).
   178     """Run command in a sub-process, capturing the output (stdout and stderr).
   164     Return the exist code, and output."""
   179     Return the exist code, and output."""
   165     # TODO: Use subprocess.Popen if we're running on Python 2.4
   180     # TODO: Use subprocess.Popen if we're running on Python 2.4
   166     if os.name == 'nt':
   181     if os.name == 'nt':
   170         ret = fromchild.close()
   185         ret = fromchild.close()
   171         if ret == None:
   186         if ret == None:
   172             ret = 0
   187             ret = 0
   173     else:
   188     else:
   174         proc = popen2.Popen4(cmd)
   189         proc = popen2.Popen4(cmd)
   175         proc.tochild.close()
   190         try:
   176         output = proc.fromchild.read()
   191             output = ''
   177         ret = proc.wait()
   192             proc.tochild.close()
       
   193             output = proc.fromchild.read()
       
   194             ret = proc.wait()
       
   195         except Timeout:
       
   196             vlog('# Process %d timed out - killing it' % proc.pid)
       
   197             os.kill(proc.pid, signal.SIGTERM)
       
   198             ret = proc.wait()
       
   199             if ret == 0:
       
   200                 ret = signal.SIGTERM << 8
   178     return ret, splitnewlines(output)
   201     return ret, splitnewlines(output)
   179 
   202 
   180 def run_one(test):
   203 def run_one(test):
   181     vlog("# Test", test)
   204     vlog("# Test", test)
   182     if not verbose:
   205     if not verbose:
   202     # To reliably get the error code from batch files on WinXP,
   225     # To reliably get the error code from batch files on WinXP,
   203     # the "cmd /c call" prefix is needed. Grrr
   226     # the "cmd /c call" prefix is needed. Grrr
   204     if os.name == 'nt' and test.endswith(".bat"):
   227     if os.name == 'nt' and test.endswith(".bat"):
   205         cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
   228         cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
   206 
   229 
       
   230     if options.timeout > 0:
       
   231         signal.alarm(options.timeout)
       
   232 
   207     vlog("# Running", cmd)
   233     vlog("# Running", cmd)
   208     ret, out = run(cmd)
   234     ret, out = run(cmd)
   209     vlog("# Ret was:", ret)
   235     vlog("# Ret was:", ret)
       
   236 
       
   237     if options.timeout > 0:
       
   238         signal.alarm(0)
   210 
   239 
   211     diffret = 0
   240     diffret = 0
   212     # If reference output file exists, check test output against it
   241     # If reference output file exists, check test output against it
   213     if os.path.exists(ref):
   242     if os.path.exists(ref):
   214         f = open(ref, "r")
   243         f = open(ref, "r")
   229         f = open(err, "wb")
   258         f = open(err, "wb")
   230         for line in out:
   259         for line in out:
   231             f.write(line)
   260             f.write(line)
   232         f.close()
   261         f.close()
   233 
   262 
       
   263     # Kill off any leftover daemon processes
       
   264     try:
       
   265         fp = file(DAEMON_PIDS)
       
   266         for line in fp:
       
   267             try:
       
   268                 pid = int(line)
       
   269             except ValueError:
       
   270                 continue
       
   271             try:
       
   272                 os.kill(pid, 0)
       
   273                 vlog('# Killing daemon process %d' % pid)
       
   274                 os.kill(pid, signal.SIGTERM)
       
   275                 time.sleep(0.25)
       
   276                 os.kill(pid, 0)
       
   277                 vlog('# Daemon process %d is stuck - really killing it' % pid)
       
   278                 os.kill(pid, signal.SIGKILL)
       
   279             except OSError, err:
       
   280                 if err.errno != errno.ESRCH:
       
   281                     raise
       
   282         fp.close()
       
   283         os.unlink(DAEMON_PIDS)
       
   284     except IOError:
       
   285         pass
       
   286 
   234     os.chdir(TESTDIR)
   287     os.chdir(TESTDIR)
   235     shutil.rmtree(tmpd, True)
   288     shutil.rmtree(tmpd, True)
   236     return ret == 0
   289     return ret == 0
   237 
   290 
   238 
   291 
   250 os.environ["HGUSER"]   = "test"
   303 os.environ["HGUSER"]   = "test"
   251 os.environ["HGRCPATH"] = ""
   304 os.environ["HGRCPATH"] = ""
   252 
   305 
   253 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
   306 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
   254 HGTMP   = os.environ["HGTMP"]   = tempfile.mkdtemp("", "hgtests.")
   307 HGTMP   = os.environ["HGTMP"]   = tempfile.mkdtemp("", "hgtests.")
       
   308 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
       
   309 
   255 vlog("# Using TESTDIR", TESTDIR)
   310 vlog("# Using TESTDIR", TESTDIR)
   256 vlog("# Using HGTMP", HGTMP)
   311 vlog("# Using HGTMP", HGTMP)
   257 
   312 
   258 INST = os.path.join(HGTMP, "install")
   313 INST = os.path.join(HGTMP, "install")
   259 BINDIR = os.path.join(INST, "bin")
   314 BINDIR = os.path.join(INST, "bin")
   261 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
   316 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
   262 
   317 
   263 try:
   318 try:
   264     try:
   319     try:
   265         install_hg()
   320         install_hg()
       
   321 
       
   322         if options.timeout > 0:
       
   323             try:
       
   324                 signal.signal(signal.SIGALRM, alarmed)
       
   325                 vlog('# Running tests with %d-second timeout' %
       
   326                      options.timeout)
       
   327             except AttributeError:
       
   328                 print 'WARNING: cannot run tests with timeouts'
       
   329                 options.timeout = 0
   266 
   330 
   267         tests = 0
   331         tests = 0
   268         failed = 0
   332         failed = 0
   269 
   333 
   270         if len(args) == 0:
   334         if len(args) == 0: