108 parser.add_option("-p", "--port", type="int", |
109 parser.add_option("-p", "--port", type="int", |
109 help="port on which servers should listen" |
110 help="port on which servers should listen" |
110 " (default: $%s or %d)" % defaults['port']) |
111 " (default: $%s or %d)" % defaults['port']) |
111 parser.add_option("-r", "--retest", action="store_true", |
112 parser.add_option("-r", "--retest", action="store_true", |
112 help="retest failed tests") |
113 help="retest failed tests") |
113 parser.add_option("-s", "--cover_stdlib", action="store_true", |
|
114 help="print a test coverage report inc. standard libraries") |
|
115 parser.add_option("-S", "--noskips", action="store_true", |
114 parser.add_option("-S", "--noskips", action="store_true", |
116 help="don't report skip tests verbosely") |
115 help="don't report skip tests verbosely") |
117 parser.add_option("-t", "--timeout", type="int", |
116 parser.add_option("-t", "--timeout", type="int", |
118 help="kill errant tests after TIMEOUT seconds" |
117 help="kill errant tests after TIMEOUT seconds" |
119 " (default: $%s or %d)" % defaults['timeout']) |
118 " (default: $%s or %d)" % defaults['timeout']) |
153 if not os.access(hgbin, os.X_OK): |
152 if not os.access(hgbin, os.X_OK): |
154 parser.error('--local specified, but %r not found or not executable' |
153 parser.error('--local specified, but %r not found or not executable' |
155 % hgbin) |
154 % hgbin) |
156 options.with_hg = hgbin |
155 options.with_hg = hgbin |
157 |
156 |
158 options.anycoverage = (options.cover or |
157 options.anycoverage = options.cover or options.annotate |
159 options.cover_stdlib or |
158 if options.anycoverage: |
160 options.annotate) |
159 try: |
161 |
160 import coverage |
162 if options.anycoverage and options.with_hg: |
161 covver = version.StrictVersion(coverage.__version__).version |
163 # I'm not sure if this is a fundamental limitation or just a |
162 if covver < (3, 3): |
164 # bug. But I don't want to waste people's time and energy doing |
163 parser.error('coverage options require coverage 3.3 or later') |
165 # test runs that don't give the results they want. |
164 except ImportError: |
166 parser.error("sorry, coverage options do not work when --with-hg " |
165 parser.error('coverage options now require the coverage package') |
167 "or --local specified") |
166 |
|
167 if options.anycoverage and options.local: |
|
168 # this needs some path mangling somewhere, I guess |
|
169 parser.error("sorry, coverage options do not work when --local " |
|
170 "is specified") |
168 |
171 |
169 global vlog |
172 global vlog |
170 if options.verbose: |
173 if options.verbose: |
171 if options.jobs > 1 or options.child is not None: |
174 if options.jobs > 1 or options.child is not None: |
172 pid = "[%d]" % os.getpid() |
175 pid = "[%d]" % os.getpid() |
388 for line in lines: |
391 for line in lines: |
389 f.write(line + '\n') |
392 f.write(line + '\n') |
390 f.close() |
393 f.close() |
391 |
394 |
392 if options.anycoverage: |
395 if options.anycoverage: |
393 vlog("# Installing coverage wrapper") |
396 custom = os.path.join(TESTDIR, 'sitecustomize.py') |
394 os.environ['COVERAGE_FILE'] = COVERAGE_FILE |
397 target = os.path.join(PYTHONDIR, 'sitecustomize.py') |
395 if os.path.exists(COVERAGE_FILE): |
398 vlog('# Installing coverage trigger to %s' % target) |
396 os.unlink(COVERAGE_FILE) |
399 shutil.copyfile(custom, target) |
397 # Create a wrapper script to invoke hg via coverage.py |
400 rc = os.path.join(TESTDIR, '.coveragerc') |
398 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py")) |
401 vlog('# Installing coverage rc to %s' % rc) |
399 f = open(os.path.join(BINDIR, 'hg'), 'w') |
402 os.environ['COVERAGE_PROCESS_START'] = rc |
400 f.write('#!' + sys.executable + '\n') |
403 fn = os.path.join(INST, '..', '.coverage') |
401 f.write('import sys, os; os.execv(sys.executable, [sys.executable, ' |
404 os.environ['COVERAGE_FILE'] = fn |
402 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' % |
|
403 (os.path.join(TESTDIR, 'coverage.py'), |
|
404 os.path.join(BINDIR, '_hg.py'))) |
|
405 f.close() |
|
406 os.chmod(os.path.join(BINDIR, 'hg'), 0700) |
|
407 |
405 |
408 def outputcoverage(options): |
406 def outputcoverage(options): |
409 |
407 |
410 vlog('# Producing coverage report') |
408 vlog('# Producing coverage report') |
411 os.chdir(PYTHONDIR) |
409 os.chdir(PYTHONDIR) |
412 |
410 |
413 def covrun(*args): |
411 def covrun(*args): |
414 start = sys.executable, os.path.join(TESTDIR, 'coverage.py') |
412 cmd = 'coverage %s' % ' '.join(args) |
415 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args)) |
|
416 vlog('# Running: %s' % cmd) |
413 vlog('# Running: %s' % cmd) |
417 os.system(cmd) |
414 os.system(cmd) |
418 |
415 |
419 omit = [BINDIR, TESTDIR, PYTHONDIR] |
416 if options.child: |
420 if not options.cover_stdlib: |
417 return |
421 # Exclude as system paths (ignoring empty strings seen on win) |
418 |
422 omit += [x for x in sys.path if x != ''] |
419 covrun('-c') |
423 omit = ','.join(omit) |
420 omit = ','.join([BINDIR, TESTDIR]) |
424 |
|
425 covrun('-c') # combine from parallel processes |
|
426 for fn in os.listdir(TESTDIR): |
|
427 if fn.startswith('.coverage.'): |
|
428 os.unlink(os.path.join(TESTDIR, fn)) |
|
429 |
|
430 covrun('-i', '-r', '"--omit=%s"' % omit) # report |
421 covrun('-i', '-r', '"--omit=%s"' % omit) # report |
431 if options.annotate: |
422 if options.annotate: |
432 adir = os.path.join(TESTDIR, 'annotated') |
423 adir = os.path.join(TESTDIR, 'annotated') |
433 if not os.path.isdir(adir): |
424 if not os.path.isdir(adir): |
434 os.mkdir(adir) |
425 os.mkdir(adir) |
666 |
657 |
667 optcopy = dict(options.__dict__) |
658 optcopy = dict(options.__dict__) |
668 optcopy['jobs'] = 1 |
659 optcopy['jobs'] = 1 |
669 if optcopy['with_hg'] is None: |
660 if optcopy['with_hg'] is None: |
670 optcopy['with_hg'] = os.path.join(BINDIR, "hg") |
661 optcopy['with_hg'] = os.path.join(BINDIR, "hg") |
|
662 optcopy.pop('anycoverage', None) |
|
663 |
671 opts = [] |
664 opts = [] |
672 for opt, value in optcopy.iteritems(): |
665 for opt, value in optcopy.iteritems(): |
673 name = '--' + opt.replace('_', '-') |
666 name = '--' + opt.replace('_', '-') |
674 if value is True: |
667 if value is True: |
675 opts.append(name) |
668 opts.append(name) |
727 print "Failed %s: %s" % (s[0], s[1]) |
720 print "Failed %s: %s" % (s[0], s[1]) |
728 |
721 |
729 _checkhglib("Tested") |
722 _checkhglib("Tested") |
730 print "# Ran %d tests, %d skipped, %d failed." % ( |
723 print "# Ran %d tests, %d skipped, %d failed." % ( |
731 tested, skipped, failed) |
724 tested, skipped, failed) |
|
725 |
|
726 if options.anycoverage: |
|
727 outputcoverage(options) |
732 sys.exit(failures != 0) |
728 sys.exit(failures != 0) |
733 |
729 |
734 def runtests(options, tests): |
730 def runtests(options, tests): |
735 global DAEMON_PIDS, HGRCPATH |
731 global DAEMON_PIDS, HGRCPATH |
736 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |
732 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |