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