##// END OF EJS Templates
run-tests.py must print changed test output no matter what exit code is.
Vadim Gelfer -
r2213:6f76a479 default
parent child Browse files
Show More
@@ -1,251 +1,253 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms
8 8 # of the GNU General Public License, incorporated herein by reference.
9 9
10 10 import os, sys, shutil, re
11 11 import tempfile
12 12 import difflib
13 13 import popen2
14 14 from optparse import OptionParser
15 15
16 16 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
17 17
18 18 parser = OptionParser("%prog [options] [tests]")
19 19 parser.add_option("-v", "--verbose", action="store_true",
20 20 help="output verbose messages")
21 21 parser.add_option("-c", "--cover", action="store_true",
22 22 help="print a test coverage report")
23 23 parser.add_option("-s", "--cover_stdlib", action="store_true",
24 24 help="print a test coverage report inc. standard libraries")
25 25 parser.add_option("-C", "--annotate", action="store_true",
26 26 help="output files annotated with coverage")
27 27 (options, args) = parser.parse_args()
28 28 verbose = options.verbose
29 29 coverage = options.cover or options.cover_stdlib or options.annotate
30 30
31 31 def vlog(*msg):
32 32 if verbose:
33 33 for m in msg:
34 34 print m,
35 35 print
36 36
37 37 def show_diff(expected, output):
38 38 for line in difflib.unified_diff(expected, output,
39 39 "Expected output", "Test output", lineterm=''):
40 40 print line
41 41
42 42 def find_program(program):
43 43 """Search PATH for a executable program"""
44 44 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
45 45 name = os.path.join(p, program)
46 46 if os.access(name, os.X_OK):
47 47 return name
48 48 return None
49 49
50 50 def check_required_tools():
51 51 # Before we go any further, check for pre-requisite tools
52 52 # stuff from coreutils (cat, rm, etc) are not tested
53 53 for p in required_tools:
54 54 if os.name == 'nt':
55 55 p += '.exe'
56 56 found = find_program(p)
57 57 if found:
58 58 vlog("# Found prerequisite", p, "at", found)
59 59 else:
60 60 print "WARNING: Did not find prerequisite tool: "+p
61 61
62 62 def cleanup_exit():
63 63 if verbose:
64 64 print "# Cleaning up HGTMP", HGTMP
65 65 shutil.rmtree(HGTMP, True)
66 66
67 67 def install_hg():
68 68 vlog("# Performing temporary installation of HG")
69 69 installerrs = os.path.join("tests", "install.err")
70 70
71 71 os.chdir("..") # Get back to hg root
72 72 cmd = ('%s setup.py clean --all'
73 73 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
74 74 % (sys.executable, INST, PYTHONDIR, installerrs))
75 75 vlog("# Running", cmd)
76 76 if os.system(cmd) == 0:
77 77 if not verbose:
78 78 os.remove(installerrs)
79 79 else:
80 80 f = open(installerrs)
81 81 for line in f:
82 82 print line,
83 83 f.close()
84 84 sys.exit(1)
85 85 os.chdir(TESTDIR)
86 86
87 87 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
88 88 os.environ["PYTHONPATH"] = PYTHONDIR
89 89
90 90 if coverage:
91 91 vlog("# Installing coverage wrapper")
92 92 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
93 93 if os.path.exists(COVERAGE_FILE):
94 94 os.unlink(COVERAGE_FILE)
95 95 # Create a wrapper script to invoke hg via coverage.py
96 96 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
97 97 f = open(os.path.join(BINDIR, 'hg'), 'w')
98 98 f.write('#!' + sys.executable + '\n')
99 99 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '+ \
100 100 '"%s", "-x", "%s"] + sys.argv[1:])\n' % (
101 101 os.path.join(TESTDIR, 'coverage.py'),
102 102 os.path.join(BINDIR, '_hg.py')))
103 103 f.close()
104 104 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
105 105
106 106 def output_coverage():
107 107 vlog("# Producing coverage report")
108 108 omit = [BINDIR, TESTDIR, PYTHONDIR]
109 109 if not options.cover_stdlib:
110 110 # Exclude as system paths (ignoring empty strings seen on win)
111 111 omit += [x for x in sys.path if x != '']
112 112 omit = ','.join(omit)
113 113 os.chdir(PYTHONDIR)
114 114 cmd = '"%s" "%s" -r "--omit=%s"' % (
115 115 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
116 116 vlog("# Running: "+cmd)
117 117 os.system(cmd)
118 118 if options.annotate:
119 119 adir = os.path.join(TESTDIR, 'annotated')
120 120 if not os.path.isdir(adir):
121 121 os.mkdir(adir)
122 122 cmd = '"%s" "%s" -a "--directory=%s" "--omit=%s"' % (
123 123 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
124 124 adir, omit)
125 125 vlog("# Running: "+cmd)
126 126 os.system(cmd)
127 127
128 128 def run(cmd, split_lines=True):
129 129 """Run command in a sub-process, capturing the output (stdout and stderr).
130 130 Return the exist code, and output."""
131 131 # TODO: Use subprocess.Popen if we're running on Python 2.4
132 132 if os.name == 'nt':
133 133 tochild, fromchild = os.popen4(cmd)
134 134 tochild.close()
135 135 output = fromchild.read()
136 136 ret = fromchild.close()
137 137 if ret == None:
138 138 ret = 0
139 139 else:
140 140 proc = popen2.Popen4(cmd)
141 141 proc.tochild.close()
142 142 output = proc.fromchild.read()
143 143 ret = proc.wait()
144 144 if split_lines:
145 145 output = output.splitlines()
146 146 return ret, output
147 147
148 148 def run_one(test):
149 149 vlog("# Test", test)
150 150 if not verbose:
151 151 sys.stdout.write('.')
152 152 sys.stdout.flush()
153 153
154 154 err = os.path.join(TESTDIR, test+".err")
155 155 ref = os.path.join(TESTDIR, test+".out")
156 156
157 157 if os.path.exists(err):
158 158 os.remove(err) # Remove any previous output files
159 159
160 160 # Make a tmp subdirectory to work in
161 161 tmpd = os.path.join(HGTMP, test)
162 162 os.mkdir(tmpd)
163 163 os.chdir(tmpd)
164 164
165 165 if test.endswith(".py"):
166 166 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
167 167 else:
168 168 cmd = '"%s"' % (os.path.join(TESTDIR, test))
169 169
170 170 # To reliably get the error code from batch files on WinXP,
171 171 # the "cmd /c call" prefix is needed. Grrr
172 172 if os.name == 'nt' and test.endswith(".bat"):
173 173 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
174 174
175 175 vlog("# Running", cmd)
176 176 ret, out = run(cmd)
177 177 vlog("# Ret was:", ret)
178 178
179 if ret == 0:
180 # If reference output file exists, check test output against it
181 if os.path.exists(ref):
182 f = open(ref, "r")
183 ref_out = f.read().splitlines()
184 f.close()
185 if out != ref_out:
186 ret = 1
187 print "\nERROR: %s output changed" % (test)
188 show_diff(ref_out, out)
189 else:
179 diffret = 0
180 # If reference output file exists, check test output against it
181 if os.path.exists(ref):
182 f = open(ref, "r")
183 ref_out = f.read().splitlines()
184 f.close()
185 if out != ref_out:
186 diffret = 1
187 print "\nERROR: %s output changed" % (test)
188 show_diff(ref_out, out)
189 if ret:
190 190 print "\nERROR: %s failed with error code %d" % (test, ret)
191 elif diffret:
192 ret = diffret
191 193
192 194 if ret != 0: # Save errors to a file for diagnosis
193 195 f = open(err, "w")
194 196 for line in out:
195 197 f.write(line)
196 198 f.write("\n")
197 199 f.close()
198 200
199 201 os.chdir(TESTDIR)
200 202 shutil.rmtree(tmpd, True)
201 203 return ret == 0
202 204
203 205
204 206 os.umask(022)
205 207
206 208 check_required_tools()
207 209
208 210 # Reset some environment variables to well-known values so that
209 211 # the tests produce repeatable output.
210 212 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
211 213 os.environ['TZ'] = 'GMT'
212 214
213 215 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
214 216 os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"'
215 217 os.environ["HGUSER"] = "test"
216 218 os.environ["HGRCPATH"] = ""
217 219
218 220 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
219 221 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
220 222 vlog("# Using TESTDIR", TESTDIR)
221 223 vlog("# Using HGTMP", HGTMP)
222 224
223 225 INST = os.path.join(HGTMP, "install")
224 226 BINDIR = os.path.join(INST, "bin")
225 227 PYTHONDIR = os.path.join(INST, "lib", "python")
226 228 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
227 229
228 230 try:
229 231 install_hg()
230 232
231 233 tests = 0
232 234 failed = 0
233 235
234 236 if len(args) == 0:
235 237 args = os.listdir(".")
236 238 for test in args:
237 239 if test.startswith("test-"):
238 240 if '~' in test or re.search(r'\.(out|err)$', test):
239 241 continue
240 242 if not run_one(test):
241 243 failed += 1
242 244 tests += 1
243 245
244 246 print "\n# Ran %d tests, %d failed." % (tests, failed)
245 247 if coverage:
246 248 output_coverage()
247 249 finally:
248 250 cleanup_exit()
249 251
250 252 if failed:
251 253 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now