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