##// END OF EJS Templates
Clear contents of global hgrc for tests before running each test....
Thomas Arendsen Hein -
r2989:3091b115 default
parent child Browse files
Show More
@@ -1,377 +1,381 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 difflib
11 11 import errno
12 12 import optparse
13 13 import os
14 14 import popen2
15 15 import re
16 16 import shutil
17 17 import signal
18 18 import sys
19 19 import tempfile
20 20 import time
21 21
22 22 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed", "merge"]
23 23
24 24 parser = optparse.OptionParser("%prog [options] [tests]")
25 25 parser.add_option("-v", "--verbose", action="store_true",
26 26 help="output verbose messages")
27 27 parser.add_option("-t", "--timeout", type="int",
28 28 help="kill errant tests after TIMEOUT seconds")
29 29 parser.add_option("-c", "--cover", action="store_true",
30 30 help="print a test coverage report")
31 31 parser.add_option("-s", "--cover_stdlib", action="store_true",
32 32 help="print a test coverage report inc. standard libraries")
33 33 parser.add_option("-C", "--annotate", action="store_true",
34 34 help="output files annotated with coverage")
35 35 parser.set_defaults(timeout=180)
36 36 (options, args) = parser.parse_args()
37 37 verbose = options.verbose
38 38 coverage = options.cover or options.cover_stdlib or options.annotate
39 39
40 40 def vlog(*msg):
41 41 if verbose:
42 42 for m in msg:
43 43 print m,
44 44 print
45 45
46 46 def splitnewlines(text):
47 47 '''like str.splitlines, but only split on newlines.
48 48 keep line endings.'''
49 49 i = 0
50 50 lines = []
51 51 while True:
52 52 n = text.find('\n', i)
53 53 if n == -1:
54 54 last = text[i:]
55 55 if last:
56 56 lines.append(last)
57 57 return lines
58 58 lines.append(text[i:n+1])
59 59 i = n + 1
60 60
61 61 def show_diff(expected, output):
62 62 for line in difflib.unified_diff(expected, output,
63 63 "Expected output", "Test output"):
64 64 sys.stdout.write(line)
65 65
66 66 def find_program(program):
67 67 """Search PATH for a executable program"""
68 68 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
69 69 name = os.path.join(p, program)
70 70 if os.access(name, os.X_OK):
71 71 return name
72 72 return None
73 73
74 74 def check_required_tools():
75 75 # Before we go any further, check for pre-requisite tools
76 76 # stuff from coreutils (cat, rm, etc) are not tested
77 77 for p in required_tools:
78 78 if os.name == 'nt':
79 79 p += '.exe'
80 80 found = find_program(p)
81 81 if found:
82 82 vlog("# Found prerequisite", p, "at", found)
83 83 else:
84 84 print "WARNING: Did not find prerequisite tool: "+p
85 85
86 86 def cleanup_exit():
87 87 if verbose:
88 88 print "# Cleaning up HGTMP", HGTMP
89 89 shutil.rmtree(HGTMP, True)
90 90
91 91 def use_correct_python():
92 92 # some tests run python interpreter. they must use same
93 93 # interpreter we use or bad things will happen.
94 94 exedir, exename = os.path.split(sys.executable)
95 95 if exename == 'python':
96 96 path = find_program('python')
97 97 if os.path.dirname(path) == exedir:
98 98 return
99 99 vlog('# Making python executable in test path use correct Python')
100 100 my_python = os.path.join(BINDIR, 'python')
101 101 try:
102 102 os.symlink(sys.executable, my_python)
103 103 except AttributeError:
104 104 # windows fallback
105 105 shutil.copyfile(sys.executable, my_python)
106 106 shutil.copymode(sys.executable, my_python)
107 107
108 108 def install_hg():
109 109 vlog("# Performing temporary installation of HG")
110 110 installerrs = os.path.join("tests", "install.err")
111 111
112 112 os.chdir("..") # Get back to hg root
113 113 cmd = ('%s setup.py clean --all'
114 114 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
115 115 % (sys.executable, INST, PYTHONDIR, installerrs))
116 116 vlog("# Running", cmd)
117 117 if os.system(cmd) == 0:
118 118 if not verbose:
119 119 os.remove(installerrs)
120 120 else:
121 121 f = open(installerrs)
122 122 for line in f:
123 123 print line,
124 124 f.close()
125 125 sys.exit(1)
126 126 os.chdir(TESTDIR)
127 127
128 128 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
129 129 os.environ["PYTHONPATH"] = PYTHONDIR
130 130
131 131 use_correct_python()
132 132
133 133 if coverage:
134 134 vlog("# Installing coverage wrapper")
135 135 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
136 136 if os.path.exists(COVERAGE_FILE):
137 137 os.unlink(COVERAGE_FILE)
138 138 # Create a wrapper script to invoke hg via coverage.py
139 139 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
140 140 f = open(os.path.join(BINDIR, 'hg'), 'w')
141 141 f.write('#!' + sys.executable + '\n')
142 142 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '+ \
143 143 '"%s", "-x", "%s"] + sys.argv[1:])\n' % (
144 144 os.path.join(TESTDIR, 'coverage.py'),
145 145 os.path.join(BINDIR, '_hg.py')))
146 146 f.close()
147 147 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
148 148
149 149 def output_coverage():
150 150 vlog("# Producing coverage report")
151 151 omit = [BINDIR, TESTDIR, PYTHONDIR]
152 152 if not options.cover_stdlib:
153 153 # Exclude as system paths (ignoring empty strings seen on win)
154 154 omit += [x for x in sys.path if x != '']
155 155 omit = ','.join(omit)
156 156 os.chdir(PYTHONDIR)
157 157 cmd = '"%s" "%s" -r "--omit=%s"' % (
158 158 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
159 159 vlog("# Running: "+cmd)
160 160 os.system(cmd)
161 161 if options.annotate:
162 162 adir = os.path.join(TESTDIR, 'annotated')
163 163 if not os.path.isdir(adir):
164 164 os.mkdir(adir)
165 165 cmd = '"%s" "%s" -a "--directory=%s" "--omit=%s"' % (
166 166 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
167 167 adir, omit)
168 168 vlog("# Running: "+cmd)
169 169 os.system(cmd)
170 170
171 171 class Timeout(Exception):
172 172 pass
173 173
174 174 def alarmed(signum, frame):
175 175 raise Timeout
176 176
177 177 def run(cmd):
178 178 """Run command in a sub-process, capturing the output (stdout and stderr).
179 179 Return the exist code, and output."""
180 180 # TODO: Use subprocess.Popen if we're running on Python 2.4
181 181 if os.name == 'nt':
182 182 tochild, fromchild = os.popen4(cmd)
183 183 tochild.close()
184 184 output = fromchild.read()
185 185 ret = fromchild.close()
186 186 if ret == None:
187 187 ret = 0
188 188 else:
189 189 proc = popen2.Popen4(cmd)
190 190 try:
191 191 output = ''
192 192 proc.tochild.close()
193 193 output = proc.fromchild.read()
194 194 ret = proc.wait()
195 195 except Timeout:
196 196 vlog('# Process %d timed out - killing it' % proc.pid)
197 197 os.kill(proc.pid, signal.SIGTERM)
198 198 ret = proc.wait()
199 199 if ret == 0:
200 200 ret = signal.SIGTERM << 8
201 201 return ret, splitnewlines(output)
202 202
203 203 def run_one(test):
204 204 '''tristate output:
205 205 None -> skipped
206 206 True -> passed
207 207 False -> failed'''
208 208
209 209 vlog("# Test", test)
210 210 if not verbose:
211 211 sys.stdout.write('.')
212 212 sys.stdout.flush()
213 213
214 # create a fresh hgrc
215 hgrc = file(HGRCPATH, 'w+')
216 hgrc.close()
217
214 218 err = os.path.join(TESTDIR, test+".err")
215 219 ref = os.path.join(TESTDIR, test+".out")
216 220
217 221 if os.path.exists(err):
218 222 os.remove(err) # Remove any previous output files
219 223
220 224 # Make a tmp subdirectory to work in
221 225 tmpd = os.path.join(HGTMP, test)
222 226 os.mkdir(tmpd)
223 227 os.chdir(tmpd)
224 228
225 229 lctest = test.lower()
226 230
227 231 if lctest.endswith('.py'):
228 232 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
229 233 elif lctest.endswith('.bat'):
230 234 # do not run batch scripts on non-windows
231 235 if os.name != 'nt':
232 236 print '\nSkipping %s: batch script' % test
233 237 return None
234 238 # To reliably get the error code from batch files on WinXP,
235 239 # the "cmd /c call" prefix is needed. Grrr
236 240 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
237 241 else:
238 242 # do not run shell scripts on windows
239 243 if os.name == 'nt':
240 244 print '\nSkipping %s: shell script' % test
241 245 return None
242 246 # do not try to run non-executable programs
243 247 if not os.access(os.path.join(TESTDIR, test), os.X_OK):
244 248 print '\nSkipping %s: not executable' % test
245 249 return None
246 250 cmd = '"%s"' % (os.path.join(TESTDIR, test))
247 251
248 252 if options.timeout > 0:
249 253 signal.alarm(options.timeout)
250 254
251 255 vlog("# Running", cmd)
252 256 ret, out = run(cmd)
253 257 vlog("# Ret was:", ret)
254 258
255 259 if options.timeout > 0:
256 260 signal.alarm(0)
257 261
258 262 diffret = 0
259 263 # If reference output file exists, check test output against it
260 264 if os.path.exists(ref):
261 265 f = open(ref, "r")
262 266 ref_out = splitnewlines(f.read())
263 267 f.close()
264 268 else:
265 269 ref_out = []
266 270 if out != ref_out:
267 271 diffret = 1
268 272 print "\nERROR: %s output changed" % (test)
269 273 show_diff(ref_out, out)
270 274 if ret:
271 275 print "\nERROR: %s failed with error code %d" % (test, ret)
272 276 elif diffret:
273 277 ret = diffret
274 278
275 279 if ret != 0: # Save errors to a file for diagnosis
276 280 f = open(err, "wb")
277 281 for line in out:
278 282 f.write(line)
279 283 f.close()
280 284
281 285 # Kill off any leftover daemon processes
282 286 try:
283 287 fp = file(DAEMON_PIDS)
284 288 for line in fp:
285 289 try:
286 290 pid = int(line)
287 291 except ValueError:
288 292 continue
289 293 try:
290 294 os.kill(pid, 0)
291 295 vlog('# Killing daemon process %d' % pid)
292 296 os.kill(pid, signal.SIGTERM)
293 297 time.sleep(0.25)
294 298 os.kill(pid, 0)
295 299 vlog('# Daemon process %d is stuck - really killing it' % pid)
296 300 os.kill(pid, signal.SIGKILL)
297 301 except OSError, err:
298 302 if err.errno != errno.ESRCH:
299 303 raise
300 304 fp.close()
301 305 os.unlink(DAEMON_PIDS)
302 306 except IOError:
303 307 pass
304 308
305 309 os.chdir(TESTDIR)
306 310 shutil.rmtree(tmpd, True)
307 311 return ret == 0
308 312
309 313
310 314 os.umask(022)
311 315
312 316 check_required_tools()
313 317
314 318 # Reset some environment variables to well-known values so that
315 319 # the tests produce repeatable output.
316 320 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
317 321 os.environ['TZ'] = 'GMT'
318 322
319 323 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
320 324 os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"'
321 325 os.environ["HGUSER"] = "test"
322 os.environ["HGRCPATH"] = ""
323 326
324 327 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
325 328 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
326 329 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
330 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
327 331
328 332 vlog("# Using TESTDIR", TESTDIR)
329 333 vlog("# Using HGTMP", HGTMP)
330 334
331 335 INST = os.path.join(HGTMP, "install")
332 336 BINDIR = os.path.join(INST, "bin")
333 337 PYTHONDIR = os.path.join(INST, "lib", "python")
334 338 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
335 339
336 340 try:
337 341 try:
338 342 install_hg()
339 343
340 344 if options.timeout > 0:
341 345 try:
342 346 signal.signal(signal.SIGALRM, alarmed)
343 347 vlog('# Running tests with %d-second timeout' %
344 348 options.timeout)
345 349 except AttributeError:
346 350 print 'WARNING: cannot run tests with timeouts'
347 351 options.timeout = 0
348 352
349 353 tests = 0
350 354 failed = 0
351 355 skipped = 0
352 356
353 357 if len(args) == 0:
354 358 args = os.listdir(".")
355 359 for test in args:
356 360 if (test.startswith("test-") and '~' not in test and
357 361 ('.' not in test or test.endswith('.py') or
358 362 test.endswith('.bat'))):
359 363 ret = run_one(test)
360 364 if ret is None:
361 365 skipped += 1
362 366 elif not ret:
363 367 failed += 1
364 368 tests += 1
365 369
366 370 print "\n# Ran %d tests, %d skipped, %d failed." % (tests, skipped,
367 371 failed)
368 372 if coverage:
369 373 output_coverage()
370 374 except KeyboardInterrupt:
371 375 failed = True
372 376 print "\ninterrupted!"
373 377 finally:
374 378 cleanup_exit()
375 379
376 380 if failed:
377 381 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now