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