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