tests/run-tests.py
changeset 2110 25a8d116ab6a
child 2133 4334be196f8d
equal deleted inserted replaced
2106:b03de24ee2ec 2110:25a8d116ab6a
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # run-tests.py - Run a set of tests on Mercurial
       
     4 #
       
     5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
       
     6 #
       
     7 # This software may be used and distributed according to the terms
       
     8 # of the GNU General Public License, incorporated herein by reference.
       
     9 
       
    10 import os, sys, shutil, re
       
    11 import tempfile
       
    12 import difflib
       
    13 import popen2
       
    14 from optparse import OptionParser
       
    15 
       
    16 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
       
    17 
       
    18 parser = OptionParser()
       
    19 parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
       
    20     default=False, help="output verbose messages")
       
    21 (options, args) = parser.parse_args()
       
    22 verbose = options.verbose
       
    23 
       
    24 def vlog(*msg):
       
    25     if verbose:
       
    26         for m in msg:
       
    27             print m,
       
    28         print
       
    29 
       
    30 def show_diff(expected, output):
       
    31     for line in difflib.unified_diff(expected, output,
       
    32             "Expected output", "Test output", lineterm=''):
       
    33         print line
       
    34 
       
    35 def find_program(program):
       
    36     """Search PATH for a executable program"""
       
    37     for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
       
    38         name = os.path.join(p, program)
       
    39         if os.access(name, os.X_OK):
       
    40             return name
       
    41     return None
       
    42 
       
    43 # Before we go any further, check for pre-requisite tools
       
    44 # stuff from coreutils (cat, rm, etc) are not tested
       
    45 for p in required_tools:
       
    46     if os.name == 'nt':
       
    47         p += '.exe'
       
    48     found = find_program(p)
       
    49     if found:
       
    50         vlog("# Found prerequisite", p, "at", found)
       
    51     else:
       
    52         print "WARNING: Did not find prerequisite tool: "+p
       
    53 
       
    54 # Reset some environment variables to well-known values
       
    55 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
       
    56 os.environ['TZ'] = 'GMT'
       
    57 
       
    58 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
       
    59 os.environ["HGMERGE"]  = sys.executable + ' -c "import sys; sys.exit(0)"'
       
    60 os.environ["HGUSER"]   = "test"
       
    61 os.environ["HGRCPATH"] = ""
       
    62 
       
    63 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
       
    64 HGTMP   = os.environ["HGTMP"]   = tempfile.mkdtemp("", "hgtests.")
       
    65 
       
    66 def cleanup_exit():
       
    67     if verbose:
       
    68         print "# Cleaning up HGTMP", HGTMP
       
    69     shutil.rmtree(HGTMP, True)
       
    70 
       
    71 vlog("# Using TESTDIR", TESTDIR)
       
    72 vlog("# Using HGTMP", HGTMP)
       
    73 
       
    74 os.umask(022)
       
    75 
       
    76 vlog("# Performing temporary installation of HG")
       
    77 INST = os.path.join(HGTMP, "install")
       
    78 BINDIR = os.path.join(INST, "bin")
       
    79 PYTHONDIR = os.path.join(INST, "lib", "python")
       
    80 installerrs = os.path.join("tests", "install.err")
       
    81 
       
    82 os.chdir("..") # Get back to hg root
       
    83 cmd = '%s setup.py install --home="%s" --install-lib="%s" >%s 2>&1' % \
       
    84     (sys.executable, INST, PYTHONDIR, installerrs)
       
    85 vlog("# Running", cmd)
       
    86 if os.system(cmd) == 0:
       
    87     if not verbose:
       
    88         os.remove(installerrs)
       
    89 else:
       
    90     f = open(installerrs)
       
    91     for line in f:
       
    92         print line,
       
    93     f.close()
       
    94     cleanup_exit()
       
    95     sys.exit(1)
       
    96 os.chdir(TESTDIR)
       
    97 
       
    98 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
       
    99 os.environ["PYTHONPATH"] = PYTHONDIR
       
   100 
       
   101 tests = 0
       
   102 failed = 0
       
   103 
       
   104 def run(cmd, split_lines=True):
       
   105     """Run command in a sub-process, capturing the output (stdout and stderr).
       
   106     Return the exist code, and output."""
       
   107     # TODO: Use subprocess.Popen if we're running on Python 2.4
       
   108     if os.name == 'nt':
       
   109         tochild, fromchild = os.popen4(cmd)
       
   110         tochild.close()
       
   111         output = fromchild.read()
       
   112         ret = fromchild.close()
       
   113         if ret == None:
       
   114             ret = 0
       
   115     else:
       
   116         proc = popen2.Popen4(cmd)
       
   117         proc.tochild.close()
       
   118         output = proc.fromchild.read()
       
   119         ret = proc.wait()
       
   120     if split_lines:
       
   121         output = output.splitlines()
       
   122     return ret, output
       
   123 
       
   124 def run_one(test):
       
   125     vlog("# Test", test)
       
   126     if not verbose:
       
   127         sys.stdout.write('.')
       
   128         sys.stdout.flush()
       
   129 
       
   130     err = os.path.join(TESTDIR, test+".err")
       
   131     ref = os.path.join(TESTDIR, test+".out")
       
   132 
       
   133     if os.path.exists(err):
       
   134         os.remove(err)       # Remove any previous output files
       
   135 
       
   136     # Make a tmp subdirectory to work in
       
   137     tmpd = os.path.join(HGTMP, test)
       
   138     os.mkdir(tmpd)
       
   139     os.chdir(tmpd)
       
   140 
       
   141     if test.endswith(".py"):
       
   142         cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
       
   143     else:
       
   144         cmd = '"%s"' % (os.path.join(TESTDIR, test))
       
   145 
       
   146     # To reliably get the error code from batch files on WinXP,
       
   147     # the "cmd /c call" prefix is needed. Grrr
       
   148     if os.name == 'nt' and test.endswith(".bat"):
       
   149         cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
       
   150 
       
   151     vlog("# Running", cmd)
       
   152     ret, out = run(cmd)
       
   153     vlog("# Ret was:", ret)
       
   154 
       
   155     if ret == 0:
       
   156         # If reference output file exists, check test output against it
       
   157         if os.path.exists(ref):
       
   158             f = open(ref, "r")
       
   159             ref_out = f.read().splitlines()
       
   160             f.close()
       
   161             if out != ref_out:
       
   162                 ret = 1
       
   163                 print "\nERROR: %s output changed" % (test)
       
   164                 show_diff(ref_out, out)
       
   165     else:
       
   166         print "\nERROR: %s failed with error code %d" % (test, ret)
       
   167 
       
   168     if ret != 0: # Save errors to a file for diagnosis
       
   169         f = open(err, "w")
       
   170         for line in out:
       
   171             f.write(line)
       
   172             f.write("\n")
       
   173         f.close()
       
   174 
       
   175     os.chdir(TESTDIR)
       
   176     shutil.rmtree(tmpd, True)
       
   177     return ret == 0
       
   178 
       
   179 for test in os.listdir("."):
       
   180     if test.startswith("test-"):
       
   181         if '~' in test or re.search(r'\.(out|err)$', test):
       
   182             continue
       
   183         if not run_one(test):
       
   184             failed += 1
       
   185         tests += 1
       
   186 
       
   187 print "# Ran %d tests, %d failed." % (tests, failed)
       
   188 
       
   189 cleanup_exit()
       
   190 
       
   191 if failed:
       
   192     sys.exit(1)