comparison tests/run-tests.py @ 2110:25a8d116ab6a

Add a pure python version of run-tests. If this works well for most people, it should replace the shell version of run-test.
author Stephen Darnell <stephen@darnell.plus.com>
date Fri, 21 Apr 2006 18:47:55 +0200
parents
children 4334be196f8d
comparison
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)