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