##// END OF EJS Templates
run-tests: move bits of main program so it's all at the bottom.
Greg Ward -
r8093:70d8f702 default
parent child Browse files
Show More
@@ -1,709 +1,709 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 try:
15 15 import subprocess
16 16 subprocess.Popen # trigger ImportError early
17 17 closefds = os.name == 'posix'
18 18 def Popen4(cmd, bufsize=-1):
19 19 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
20 20 close_fds=closefds,
21 21 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
22 22 stderr=subprocess.STDOUT)
23 23 p.fromchild = p.stdout
24 24 p.tochild = p.stdin
25 25 p.childerr = p.stderr
26 26 return p
27 27 except ImportError:
28 28 subprocess = None
29 29 from popen2 import Popen4
30 30 import shutil
31 31 import signal
32 32 import sys
33 33 import tempfile
34 34 import time
35 35
36 36 # reserved exit code to skip test (used by hghave)
37 37 SKIPPED_STATUS = 80
38 38 SKIPPED_PREFIX = 'skipped: '
39 39 FAILED_PREFIX = 'hghave check failed: '
40 40
41 41 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
42 42
43 43 defaults = {
44 44 'jobs': ('HGTEST_JOBS', 1),
45 45 'timeout': ('HGTEST_TIMEOUT', 180),
46 46 'port': ('HGTEST_PORT', 20059),
47 47 }
48 48
49 49 # globals set by parse_args() (ugh)
50 50 verbose = False
51 51 nodiff = False
52 52 coverage = False
53 53 python = None
54 54
55 55 def parse_args():
56 56 parser = optparse.OptionParser("%prog [options] [tests]")
57 57 parser.add_option("-C", "--annotate", action="store_true",
58 58 help="output files annotated with coverage")
59 59 parser.add_option("--child", type="int",
60 60 help="run as child process, summary to given fd")
61 61 parser.add_option("-c", "--cover", action="store_true",
62 62 help="print a test coverage report")
63 63 parser.add_option("-f", "--first", action="store_true",
64 64 help="exit on the first test failure")
65 65 parser.add_option("-i", "--interactive", action="store_true",
66 66 help="prompt to accept changed output")
67 67 parser.add_option("-j", "--jobs", type="int",
68 68 help="number of jobs to run in parallel"
69 69 " (default: $%s or %d)" % defaults['jobs'])
70 70 parser.add_option("--keep-tmpdir", action="store_true",
71 71 help="keep temporary directory after running tests"
72 72 " (best used with --tmpdir)")
73 73 parser.add_option("-R", "--restart", action="store_true",
74 74 help="restart at last error")
75 75 parser.add_option("-p", "--port", type="int",
76 76 help="port on which servers should listen"
77 77 " (default: $%s or %d)" % defaults['port'])
78 78 parser.add_option("-r", "--retest", action="store_true",
79 79 help="retest failed tests")
80 80 parser.add_option("-s", "--cover_stdlib", action="store_true",
81 81 help="print a test coverage report inc. standard libraries")
82 82 parser.add_option("-t", "--timeout", type="int",
83 83 help="kill errant tests after TIMEOUT seconds"
84 84 " (default: $%s or %d)" % defaults['timeout'])
85 85 parser.add_option("--tmpdir", type="string",
86 86 help="run tests in the given temporary directory")
87 87 parser.add_option("-v", "--verbose", action="store_true",
88 88 help="output verbose messages")
89 89 parser.add_option("-n", "--nodiff", action="store_true",
90 90 help="skip showing test changes")
91 91 parser.add_option("--with-hg", type="string",
92 92 help="test existing install at given location")
93 93 parser.add_option("--pure", action="store_true",
94 94 help="use pure Python code instead of C extensions")
95 95
96 96 for option, default in defaults.items():
97 97 defaults[option] = int(os.environ.get(*default))
98 98 parser.set_defaults(**defaults)
99 99 (options, args) = parser.parse_args()
100 100
101 101 global verbose, nodiff, coverage, python
102 102 verbose = options.verbose
103 103 nodiff = options.nodiff
104 104 coverage = options.cover or options.cover_stdlib or options.annotate
105 105 python = sys.executable
106 106
107 107 if options.jobs < 1:
108 108 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
109 109 sys.exit(1)
110 110 if options.interactive and options.jobs > 1:
111 111 print '(--interactive overrides --jobs)'
112 112 options.jobs = 1
113 113
114 114 return (options, args)
115 115
116 116 def rename(src, dst):
117 117 """Like os.rename(), trade atomicity and opened files friendliness
118 118 for existing destination support.
119 119 """
120 120 shutil.copy(src, dst)
121 121 os.remove(src)
122 122
123 123 def vlog(*msg):
124 124 if verbose:
125 125 for m in msg:
126 126 print m,
127 127 print
128 128
129 129 def splitnewlines(text):
130 130 '''like str.splitlines, but only split on newlines.
131 131 keep line endings.'''
132 132 i = 0
133 133 lines = []
134 134 while True:
135 135 n = text.find('\n', i)
136 136 if n == -1:
137 137 last = text[i:]
138 138 if last:
139 139 lines.append(last)
140 140 return lines
141 141 lines.append(text[i:n+1])
142 142 i = n + 1
143 143
144 144 def parse_hghave_output(lines):
145 145 '''Parse hghave log lines.
146 146 Return tuple of lists (missing, failed):
147 147 * the missing/unknown features
148 148 * the features for which existence check failed'''
149 149 missing = []
150 150 failed = []
151 151 for line in lines:
152 152 if line.startswith(SKIPPED_PREFIX):
153 153 line = line.splitlines()[0]
154 154 missing.append(line[len(SKIPPED_PREFIX):])
155 155 elif line.startswith(FAILED_PREFIX):
156 156 line = line.splitlines()[0]
157 157 failed.append(line[len(FAILED_PREFIX):])
158 158
159 159 return missing, failed
160 160
161 161 def show_diff(expected, output):
162 162 for line in difflib.unified_diff(expected, output,
163 163 "Expected output", "Test output"):
164 164 sys.stdout.write(line)
165 165
166 166 def find_program(program):
167 167 """Search PATH for a executable program"""
168 168 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
169 169 name = os.path.join(p, program)
170 170 if os.access(name, os.X_OK):
171 171 return name
172 172 return None
173 173
174 174 def check_required_tools():
175 175 # Before we go any further, check for pre-requisite tools
176 176 # stuff from coreutils (cat, rm, etc) are not tested
177 177 for p in required_tools:
178 178 if os.name == 'nt':
179 179 p += '.exe'
180 180 found = find_program(p)
181 181 if found:
182 182 vlog("# Found prerequisite", p, "at", found)
183 183 else:
184 184 print "WARNING: Did not find prerequisite tool: "+p
185 185
186 186 def cleanup_exit():
187 187 if not options.keep_tmpdir:
188 188 if verbose:
189 189 print "# Cleaning up HGTMP", HGTMP
190 190 shutil.rmtree(HGTMP, True)
191 191
192 192 def use_correct_python():
193 193 # some tests run python interpreter. they must use same
194 194 # interpreter we use or bad things will happen.
195 195 exedir, exename = os.path.split(sys.executable)
196 196 if exename == 'python':
197 197 path = find_program('python')
198 198 if os.path.dirname(path) == exedir:
199 199 return
200 200 vlog('# Making python executable in test path use correct Python')
201 201 my_python = os.path.join(BINDIR, 'python')
202 202 try:
203 203 os.symlink(sys.executable, my_python)
204 204 except AttributeError:
205 205 # windows fallback
206 206 shutil.copyfile(sys.executable, my_python)
207 207 shutil.copymode(sys.executable, my_python)
208 208
209 209 def install_hg():
210 210 global python
211 211 vlog("# Performing temporary installation of HG")
212 212 installerrs = os.path.join("tests", "install.err")
213 213 pure = options.pure and "--pure" or ""
214 214
215 215 # Run installer in hg root
216 216 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
217 217 cmd = ('%s setup.py %s clean --all'
218 218 ' install --force --prefix="%s" --install-lib="%s"'
219 219 ' --install-scripts="%s" >%s 2>&1'
220 220 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
221 221 vlog("# Running", cmd)
222 222 if os.system(cmd) == 0:
223 223 if not verbose:
224 224 os.remove(installerrs)
225 225 else:
226 226 f = open(installerrs)
227 227 for line in f:
228 228 print line,
229 229 f.close()
230 230 sys.exit(1)
231 231 os.chdir(TESTDIR)
232 232
233 233 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
234 234
235 235 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
236 236 pythonpath = os.environ.get("PYTHONPATH")
237 237 if pythonpath:
238 238 pythonpath = pydir + os.pathsep + pythonpath
239 239 else:
240 240 pythonpath = pydir
241 241 os.environ["PYTHONPATH"] = pythonpath
242 242
243 243 use_correct_python()
244 244 global hgpkg
245 245 hgpkg = _hgpath()
246 246
247 247 vlog("# Installing dummy diffstat")
248 248 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
249 249 f.write('#!' + sys.executable + '\n'
250 250 'import sys\n'
251 251 'files = 0\n'
252 252 'for line in sys.stdin:\n'
253 253 ' if line.startswith("diff "):\n'
254 254 ' files += 1\n'
255 255 'sys.stdout.write("files patched: %d\\n" % files)\n')
256 256 f.close()
257 257 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
258 258
259 259 if coverage:
260 260 vlog("# Installing coverage wrapper")
261 261 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
262 262 if os.path.exists(COVERAGE_FILE):
263 263 os.unlink(COVERAGE_FILE)
264 264 # Create a wrapper script to invoke hg via coverage.py
265 265 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
266 266 f = open(os.path.join(BINDIR, 'hg'), 'w')
267 267 f.write('#!' + sys.executable + '\n')
268 268 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
269 269 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
270 270 (os.path.join(TESTDIR, 'coverage.py'),
271 271 os.path.join(BINDIR, '_hg.py')))
272 272 f.close()
273 273 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
274 274 python = '"%s" "%s" -x' % (sys.executable,
275 275 os.path.join(TESTDIR,'coverage.py'))
276 276
277 277 def _hgpath():
278 278 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
279 279 hgpath = os.popen(cmd % python)
280 280 path = hgpath.read().strip()
281 281 hgpath.close()
282 282 return path
283 283
284 284 def output_coverage():
285 285 vlog("# Producing coverage report")
286 286 omit = [BINDIR, TESTDIR, PYTHONDIR]
287 287 if not options.cover_stdlib:
288 288 # Exclude as system paths (ignoring empty strings seen on win)
289 289 omit += [x for x in sys.path if x != '']
290 290 omit = ','.join(omit)
291 291 os.chdir(PYTHONDIR)
292 292 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
293 293 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
294 294 vlog("# Running: "+cmd)
295 295 os.system(cmd)
296 296 if options.annotate:
297 297 adir = os.path.join(TESTDIR, 'annotated')
298 298 if not os.path.isdir(adir):
299 299 os.mkdir(adir)
300 300 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
301 301 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
302 302 adir, omit)
303 303 vlog("# Running: "+cmd)
304 304 os.system(cmd)
305 305
306 306 class Timeout(Exception):
307 307 pass
308 308
309 309 def alarmed(signum, frame):
310 310 raise Timeout
311 311
312 312 def run(cmd):
313 313 """Run command in a sub-process, capturing the output (stdout and stderr).
314 314 Return the exist code, and output."""
315 315 # TODO: Use subprocess.Popen if we're running on Python 2.4
316 316 if os.name == 'nt' or sys.platform.startswith('java'):
317 317 tochild, fromchild = os.popen4(cmd)
318 318 tochild.close()
319 319 output = fromchild.read()
320 320 ret = fromchild.close()
321 321 if ret == None:
322 322 ret = 0
323 323 else:
324 324 proc = Popen4(cmd)
325 325 try:
326 326 output = ''
327 327 proc.tochild.close()
328 328 output = proc.fromchild.read()
329 329 ret = proc.wait()
330 330 if os.WIFEXITED(ret):
331 331 ret = os.WEXITSTATUS(ret)
332 332 except Timeout:
333 333 vlog('# Process %d timed out - killing it' % proc.pid)
334 334 os.kill(proc.pid, signal.SIGTERM)
335 335 ret = proc.wait()
336 336 if ret == 0:
337 337 ret = signal.SIGTERM << 8
338 338 output += ("\n### Abort: timeout after %d seconds.\n"
339 339 % options.timeout)
340 340 return ret, splitnewlines(output)
341 341
342 342 def run_one(test, skips, fails):
343 343 '''tristate output:
344 344 None -> skipped
345 345 True -> passed
346 346 False -> failed'''
347 347
348 348 def skip(msg):
349 349 if not verbose:
350 350 skips.append((test, msg))
351 351 else:
352 352 print "\nSkipping %s: %s" % (test, msg)
353 353 return None
354 354
355 355 def fail(msg):
356 356 fails.append((test, msg))
357 357 if not nodiff:
358 358 print "\nERROR: %s %s" % (test, msg)
359 359 return None
360 360
361 361 vlog("# Test", test)
362 362
363 363 # create a fresh hgrc
364 364 hgrc = file(HGRCPATH, 'w+')
365 365 hgrc.write('[ui]\n')
366 366 hgrc.write('slash = True\n')
367 367 hgrc.write('[defaults]\n')
368 368 hgrc.write('backout = -d "0 0"\n')
369 369 hgrc.write('commit = -d "0 0"\n')
370 370 hgrc.write('debugrawcommit = -d "0 0"\n')
371 371 hgrc.write('tag = -d "0 0"\n')
372 372 hgrc.close()
373 373
374 374 err = os.path.join(TESTDIR, test+".err")
375 375 ref = os.path.join(TESTDIR, test+".out")
376 376 testpath = os.path.join(TESTDIR, test)
377 377
378 378 if os.path.exists(err):
379 379 os.remove(err) # Remove any previous output files
380 380
381 381 # Make a tmp subdirectory to work in
382 382 tmpd = os.path.join(HGTMP, test)
383 383 os.mkdir(tmpd)
384 384 os.chdir(tmpd)
385 385
386 386 try:
387 387 tf = open(testpath)
388 388 firstline = tf.readline().rstrip()
389 389 tf.close()
390 390 except:
391 391 firstline = ''
392 392 lctest = test.lower()
393 393
394 394 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
395 395 cmd = '%s "%s"' % (python, testpath)
396 396 elif lctest.endswith('.bat'):
397 397 # do not run batch scripts on non-windows
398 398 if os.name != 'nt':
399 399 return skip("batch script")
400 400 # To reliably get the error code from batch files on WinXP,
401 401 # the "cmd /c call" prefix is needed. Grrr
402 402 cmd = 'cmd /c call "%s"' % testpath
403 403 else:
404 404 # do not run shell scripts on windows
405 405 if os.name == 'nt':
406 406 return skip("shell script")
407 407 # do not try to run non-executable programs
408 408 if not os.path.exists(testpath):
409 409 return fail("does not exist")
410 410 elif not os.access(testpath, os.X_OK):
411 411 return skip("not executable")
412 412 cmd = '"%s"' % testpath
413 413
414 414 if options.timeout > 0:
415 415 signal.alarm(options.timeout)
416 416
417 417 vlog("# Running", cmd)
418 418 ret, out = run(cmd)
419 419 vlog("# Ret was:", ret)
420 420
421 421 if options.timeout > 0:
422 422 signal.alarm(0)
423 423
424 424 mark = '.'
425 425
426 426 skipped = (ret == SKIPPED_STATUS)
427 427 # If reference output file exists, check test output against it
428 428 if os.path.exists(ref):
429 429 f = open(ref, "r")
430 430 ref_out = splitnewlines(f.read())
431 431 f.close()
432 432 else:
433 433 ref_out = []
434 434 if skipped:
435 435 mark = 's'
436 436 missing, failed = parse_hghave_output(out)
437 437 if not missing:
438 438 missing = ['irrelevant']
439 439 if failed:
440 440 fail("hghave failed checking for %s" % failed[-1])
441 441 skipped = False
442 442 else:
443 443 skip(missing[-1])
444 444 elif out != ref_out:
445 445 mark = '!'
446 446 if ret:
447 447 fail("output changed and returned error code %d" % ret)
448 448 else:
449 449 fail("output changed")
450 450 if not nodiff:
451 451 show_diff(ref_out, out)
452 452 ret = 1
453 453 elif ret:
454 454 mark = '!'
455 455 fail("returned error code %d" % ret)
456 456
457 457 if not verbose:
458 458 sys.stdout.write(mark)
459 459 sys.stdout.flush()
460 460
461 461 if ret != 0 and not skipped:
462 462 # Save errors to a file for diagnosis
463 463 f = open(err, "wb")
464 464 for line in out:
465 465 f.write(line)
466 466 f.close()
467 467
468 468 # Kill off any leftover daemon processes
469 469 try:
470 470 fp = file(DAEMON_PIDS)
471 471 for line in fp:
472 472 try:
473 473 pid = int(line)
474 474 except ValueError:
475 475 continue
476 476 try:
477 477 os.kill(pid, 0)
478 478 vlog('# Killing daemon process %d' % pid)
479 479 os.kill(pid, signal.SIGTERM)
480 480 time.sleep(0.25)
481 481 os.kill(pid, 0)
482 482 vlog('# Daemon process %d is stuck - really killing it' % pid)
483 483 os.kill(pid, signal.SIGKILL)
484 484 except OSError, err:
485 485 if err.errno != errno.ESRCH:
486 486 raise
487 487 fp.close()
488 488 os.unlink(DAEMON_PIDS)
489 489 except IOError:
490 490 pass
491 491
492 492 os.chdir(TESTDIR)
493 493 if not options.keep_tmpdir:
494 494 shutil.rmtree(tmpd, True)
495 495 if skipped:
496 496 return None
497 497 return ret == 0
498 498
499 (options, args) = parse_args()
500 if not options.child:
501 os.umask(022)
502
503 check_required_tools()
504
505 # Reset some environment variables to well-known values so that
506 # the tests produce repeatable output.
507 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
508 os.environ['TZ'] = 'GMT'
509 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
510 os.environ['CDPATH'] = ''
511
512 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
513 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
514 options.tmpdir))
515 DAEMON_PIDS = None
516 HGRCPATH = None
517
518 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
519 os.environ["HGMERGE"] = "internal:merge"
520 os.environ["HGUSER"] = "test"
521 os.environ["HGENCODING"] = "ascii"
522 os.environ["HGENCODINGMODE"] = "strict"
523 os.environ["HGPORT"] = str(options.port)
524 os.environ["HGPORT1"] = str(options.port + 1)
525 os.environ["HGPORT2"] = str(options.port + 2)
526
527 if options.with_hg:
528 INST = options.with_hg
529 else:
530 INST = os.path.join(HGTMP, "install")
531 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
532 PYTHONDIR = os.path.join(INST, "lib", "python")
533 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
534
535 expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
536 hgpkg = None
537
538 499 def run_children(tests):
539 500 if not options.with_hg:
540 501 install_hg()
541 502 if hgpkg != expecthg:
542 503 print '# Testing unexpected mercurial: %s' % hgpkg
543 504
544 505 optcopy = dict(options.__dict__)
545 506 optcopy['jobs'] = 1
546 507 optcopy['with_hg'] = INST
547 508 opts = []
548 509 for opt, value in optcopy.iteritems():
549 510 name = '--' + opt.replace('_', '-')
550 511 if value is True:
551 512 opts.append(name)
552 513 elif value is not None:
553 514 opts.append(name + '=' + str(value))
554 515
555 516 tests.reverse()
556 517 jobs = [[] for j in xrange(options.jobs)]
557 518 while tests:
558 519 for j in xrange(options.jobs):
559 520 if not tests: break
560 521 jobs[j].append(tests.pop())
561 522 fps = {}
562 523 for j in xrange(len(jobs)):
563 524 job = jobs[j]
564 525 if not job:
565 526 continue
566 527 rfd, wfd = os.pipe()
567 528 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
568 529 cmdline = [python, sys.argv[0]] + opts + childopts + job
569 530 vlog(' '.join(cmdline))
570 531 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
571 532 os.close(wfd)
572 533 failures = 0
573 534 tested, skipped, failed = 0, 0, 0
574 535 skips = []
575 536 fails = []
576 537 while fps:
577 538 pid, status = os.wait()
578 539 fp = fps.pop(pid)
579 540 l = fp.read().splitlines()
580 541 test, skip, fail = map(int, l[:3])
581 542 split = -fail or len(l)
582 543 for s in l[3:split]:
583 544 skips.append(s.split(" ", 1))
584 545 for s in l[split:]:
585 546 fails.append(s.split(" ", 1))
586 547 tested += test
587 548 skipped += skip
588 549 failed += fail
589 550 vlog('pid %d exited, status %d' % (pid, status))
590 551 failures |= status
591 552 print
592 553 for s in skips:
593 554 print "Skipped %s: %s" % (s[0], s[1])
594 555 for s in fails:
595 556 print "Failed %s: %s" % (s[0], s[1])
596 557
597 558 if hgpkg != expecthg:
598 559 print '# Tested unexpected mercurial: %s' % hgpkg
599 560 print "# Ran %d tests, %d skipped, %d failed." % (
600 561 tested, skipped, failed)
601 562 sys.exit(failures != 0)
602 563
603 564 def run_tests(tests):
604 565 global DAEMON_PIDS, HGRCPATH
605 566 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
606 567 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
607 568
608 569 try:
609 570 if not options.with_hg:
610 571 install_hg()
611 572
612 573 if hgpkg != expecthg:
613 574 print '# Testing unexpected mercurial: %s' % hgpkg
614 575
615 576 if options.timeout > 0:
616 577 try:
617 578 signal.signal(signal.SIGALRM, alarmed)
618 579 vlog('# Running tests with %d-second timeout' %
619 580 options.timeout)
620 581 except AttributeError:
621 582 print 'WARNING: cannot run tests with timeouts'
622 583 options.timeout = 0
623 584
624 585 tested = 0
625 586 failed = 0
626 587 skipped = 0
627 588
628 589 if options.restart:
629 590 orig = list(tests)
630 591 while tests:
631 592 if os.path.exists(tests[0] + ".err"):
632 593 break
633 594 tests.pop(0)
634 595 if not tests:
635 596 print "running all tests"
636 597 tests = orig
637 598
638 599 skips = []
639 600 fails = []
640 601 for test in tests:
641 602 if options.retest and not os.path.exists(test + ".err"):
642 603 skipped += 1
643 604 continue
644 605 ret = run_one(test, skips, fails)
645 606 if ret is None:
646 607 skipped += 1
647 608 elif not ret:
648 609 if options.interactive:
649 610 print "Accept this change? [n] ",
650 611 answer = sys.stdin.readline().strip()
651 612 if answer.lower() in "y yes".split():
652 613 rename(test + ".err", test + ".out")
653 614 tested += 1
654 615 fails.pop()
655 616 continue
656 617 failed += 1
657 618 if options.first:
658 619 break
659 620 tested += 1
660 621
661 622 if options.child:
662 623 fp = os.fdopen(options.child, 'w')
663 624 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
664 625 for s in skips:
665 626 fp.write("%s %s\n" % s)
666 627 for s in fails:
667 628 fp.write("%s %s\n" % s)
668 629 fp.close()
669 630 else:
670 631 print
671 632 for s in skips:
672 633 print "Skipped %s: %s" % s
673 634 for s in fails:
674 635 print "Failed %s: %s" % s
675 636 if hgpkg != expecthg:
676 637 print '# Tested unexpected mercurial: %s' % hgpkg
677 638 print "# Ran %d tests, %d skipped, %d failed." % (
678 639 tested, skipped, failed)
679 640
680 641 if coverage:
681 642 output_coverage()
682 643 except KeyboardInterrupt:
683 644 failed = True
684 645 print "\ninterrupted!"
685 646
686 647 if failed:
687 648 sys.exit(1)
688 649
650 (options, args) = parse_args()
651 if not options.child:
652 os.umask(022)
653
654 check_required_tools()
655
656 # Reset some environment variables to well-known values so that
657 # the tests produce repeatable output.
658 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
659 os.environ['TZ'] = 'GMT'
660 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
661 os.environ['CDPATH'] = ''
662
663 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
664 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
665 options.tmpdir))
666 DAEMON_PIDS = None
667 HGRCPATH = None
668
669 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
670 os.environ["HGMERGE"] = "internal:merge"
671 os.environ["HGUSER"] = "test"
672 os.environ["HGENCODING"] = "ascii"
673 os.environ["HGENCODINGMODE"] = "strict"
674 os.environ["HGPORT"] = str(options.port)
675 os.environ["HGPORT1"] = str(options.port + 1)
676 os.environ["HGPORT2"] = str(options.port + 2)
677
678 if options.with_hg:
679 INST = options.with_hg
680 else:
681 INST = os.path.join(HGTMP, "install")
682 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
683 PYTHONDIR = os.path.join(INST, "lib", "python")
684 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
685
686 expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
687 hgpkg = None
688
689 689 if len(args) == 0:
690 690 args = os.listdir(".")
691 691 args.sort()
692 692
693 693 tests = []
694 694 for test in args:
695 695 if (test.startswith("test-") and '~' not in test and
696 696 ('.' not in test or test.endswith('.py') or
697 697 test.endswith('.bat'))):
698 698 tests.append(test)
699 699
700 700 vlog("# Using TESTDIR", TESTDIR)
701 701 vlog("# Using HGTMP", HGTMP)
702 702
703 703 try:
704 704 if len(tests) > 1 and options.jobs > 1:
705 705 run_children(tests)
706 706 else:
707 707 run_tests(tests)
708 708 finally:
709 709 cleanup_exit()
General Comments 0
You need to be logged in to leave comments. Login now