67 # reserved exit code to skip test (used by hghave) |
68 # reserved exit code to skip test (used by hghave) |
68 SKIPPED_STATUS = 80 |
69 SKIPPED_STATUS = 80 |
69 SKIPPED_PREFIX = 'skipped: ' |
70 SKIPPED_PREFIX = 'skipped: ' |
70 FAILED_PREFIX = 'hghave check failed: ' |
71 FAILED_PREFIX = 'hghave check failed: ' |
71 PYTHON = sys.executable |
72 PYTHON = sys.executable |
|
73 IMPL_PATH = 'PYTHONPATH' |
|
74 if 'java' in sys.platform: |
|
75 IMPL_PATH = 'JYTHONPATH' |
72 |
76 |
73 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] |
77 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] |
74 |
78 |
75 defaults = { |
79 defaults = { |
76 'jobs': ('HGTEST_JOBS', 1), |
80 'jobs': ('HGTEST_JOBS', 1), |
108 parser.add_option("-p", "--port", type="int", |
112 parser.add_option("-p", "--port", type="int", |
109 help="port on which servers should listen" |
113 help="port on which servers should listen" |
110 " (default: $%s or %d)" % defaults['port']) |
114 " (default: $%s or %d)" % defaults['port']) |
111 parser.add_option("-r", "--retest", action="store_true", |
115 parser.add_option("-r", "--retest", action="store_true", |
112 help="retest failed tests") |
116 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", |
117 parser.add_option("-S", "--noskips", action="store_true", |
116 help="don't report skip tests verbosely") |
118 help="don't report skip tests verbosely") |
117 parser.add_option("-t", "--timeout", type="int", |
119 parser.add_option("-t", "--timeout", type="int", |
118 help="kill errant tests after TIMEOUT seconds" |
120 help="kill errant tests after TIMEOUT seconds" |
119 " (default: $%s or %d)" % defaults['timeout']) |
121 " (default: $%s or %d)" % defaults['timeout']) |
139 for option, default in defaults.items(): |
141 for option, default in defaults.items(): |
140 defaults[option] = int(os.environ.get(*default)) |
142 defaults[option] = int(os.environ.get(*default)) |
141 parser.set_defaults(**defaults) |
143 parser.set_defaults(**defaults) |
142 (options, args) = parser.parse_args() |
144 (options, args) = parser.parse_args() |
143 |
145 |
|
146 # jython is always pure |
|
147 if 'java' in sys.platform or '__pypy__' in sys.modules: |
|
148 options.pure = True |
|
149 |
144 if options.with_hg: |
150 if options.with_hg: |
145 if not (os.path.isfile(options.with_hg) and |
151 if not (os.path.isfile(options.with_hg) and |
146 os.access(options.with_hg, os.X_OK)): |
152 os.access(options.with_hg, os.X_OK)): |
147 parser.error('--with-hg must specify an executable hg script') |
153 parser.error('--with-hg must specify an executable hg script') |
148 if not os.path.basename(options.with_hg) == 'hg': |
154 if not os.path.basename(options.with_hg) == 'hg': |
153 if not os.access(hgbin, os.X_OK): |
159 if not os.access(hgbin, os.X_OK): |
154 parser.error('--local specified, but %r not found or not executable' |
160 parser.error('--local specified, but %r not found or not executable' |
155 % hgbin) |
161 % hgbin) |
156 options.with_hg = hgbin |
162 options.with_hg = hgbin |
157 |
163 |
158 options.anycoverage = (options.cover or |
164 options.anycoverage = options.cover or options.annotate |
159 options.cover_stdlib or |
165 if options.anycoverage: |
160 options.annotate) |
166 try: |
161 |
167 import coverage |
162 if options.anycoverage and options.with_hg: |
168 covver = version.StrictVersion(coverage.__version__).version |
163 # I'm not sure if this is a fundamental limitation or just a |
169 if covver < (3, 3): |
164 # bug. But I don't want to waste people's time and energy doing |
170 parser.error('coverage options require coverage 3.3 or later') |
165 # test runs that don't give the results they want. |
171 except ImportError: |
166 parser.error("sorry, coverage options do not work when --with-hg " |
172 parser.error('coverage options now require the coverage package') |
167 "or --local specified") |
173 |
|
174 if options.anycoverage and options.local: |
|
175 # this needs some path mangling somewhere, I guess |
|
176 parser.error("sorry, coverage options do not work when --local " |
|
177 "is specified") |
168 |
178 |
169 global vlog |
179 global vlog |
170 if options.verbose: |
180 if options.verbose: |
171 if options.jobs > 1 or options.child is not None: |
181 if options.jobs > 1 or options.child is not None: |
172 pid = "[%d]" % os.getpid() |
182 pid = "[%d]" % os.getpid() |
388 for line in lines: |
398 for line in lines: |
389 f.write(line + '\n') |
399 f.write(line + '\n') |
390 f.close() |
400 f.close() |
391 |
401 |
392 if options.anycoverage: |
402 if options.anycoverage: |
393 vlog("# Installing coverage wrapper") |
403 custom = os.path.join(TESTDIR, 'sitecustomize.py') |
394 os.environ['COVERAGE_FILE'] = COVERAGE_FILE |
404 target = os.path.join(PYTHONDIR, 'sitecustomize.py') |
395 if os.path.exists(COVERAGE_FILE): |
405 vlog('# Installing coverage trigger to %s' % target) |
396 os.unlink(COVERAGE_FILE) |
406 shutil.copyfile(custom, target) |
397 # Create a wrapper script to invoke hg via coverage.py |
407 rc = os.path.join(TESTDIR, '.coveragerc') |
398 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py")) |
408 vlog('# Installing coverage rc to %s' % rc) |
399 f = open(os.path.join(BINDIR, 'hg'), 'w') |
409 os.environ['COVERAGE_PROCESS_START'] = rc |
400 f.write('#!' + sys.executable + '\n') |
410 fn = os.path.join(INST, '..', '.coverage') |
401 f.write('import sys, os; os.execv(sys.executable, [sys.executable, ' |
411 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 |
412 |
408 def outputcoverage(options): |
413 def outputcoverage(options): |
409 |
414 |
410 vlog('# Producing coverage report') |
415 vlog('# Producing coverage report') |
411 os.chdir(PYTHONDIR) |
416 os.chdir(PYTHONDIR) |
412 |
417 |
413 def covrun(*args): |
418 def covrun(*args): |
414 start = sys.executable, os.path.join(TESTDIR, 'coverage.py') |
419 cmd = 'coverage %s' % ' '.join(args) |
415 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args)) |
|
416 vlog('# Running: %s' % cmd) |
420 vlog('# Running: %s' % cmd) |
417 os.system(cmd) |
421 os.system(cmd) |
418 |
422 |
419 omit = [BINDIR, TESTDIR, PYTHONDIR] |
423 if options.child: |
420 if not options.cover_stdlib: |
424 return |
421 # Exclude as system paths (ignoring empty strings seen on win) |
425 |
422 omit += [x for x in sys.path if x != ''] |
426 covrun('-c') |
423 omit = ','.join(omit) |
427 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 |
428 covrun('-i', '-r', '"--omit=%s"' % omit) # report |
431 if options.annotate: |
429 if options.annotate: |
432 adir = os.path.join(TESTDIR, 'annotated') |
430 adir = os.path.join(TESTDIR, 'annotated') |
433 if not os.path.isdir(adir): |
431 if not os.path.isdir(adir): |
434 os.mkdir(adir) |
432 os.mkdir(adir) |
666 |
664 |
667 optcopy = dict(options.__dict__) |
665 optcopy = dict(options.__dict__) |
668 optcopy['jobs'] = 1 |
666 optcopy['jobs'] = 1 |
669 if optcopy['with_hg'] is None: |
667 if optcopy['with_hg'] is None: |
670 optcopy['with_hg'] = os.path.join(BINDIR, "hg") |
668 optcopy['with_hg'] = os.path.join(BINDIR, "hg") |
|
669 optcopy.pop('anycoverage', None) |
|
670 |
671 opts = [] |
671 opts = [] |
672 for opt, value in optcopy.iteritems(): |
672 for opt, value in optcopy.iteritems(): |
673 name = '--' + opt.replace('_', '-') |
673 name = '--' + opt.replace('_', '-') |
674 if value is True: |
674 if value is True: |
675 opts.append(name) |
675 opts.append(name) |
727 print "Failed %s: %s" % (s[0], s[1]) |
727 print "Failed %s: %s" % (s[0], s[1]) |
728 |
728 |
729 _checkhglib("Tested") |
729 _checkhglib("Tested") |
730 print "# Ran %d tests, %d skipped, %d failed." % ( |
730 print "# Ran %d tests, %d skipped, %d failed." % ( |
731 tested, skipped, failed) |
731 tested, skipped, failed) |
|
732 |
|
733 if options.anycoverage: |
|
734 outputcoverage(options) |
732 sys.exit(failures != 0) |
735 sys.exit(failures != 0) |
733 |
736 |
734 def runtests(options, tests): |
737 def runtests(options, tests): |
735 global DAEMON_PIDS, HGRCPATH |
738 global DAEMON_PIDS, HGRCPATH |
736 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |
739 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |
844 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C' |
847 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C' |
845 os.environ['TZ'] = 'GMT' |
848 os.environ['TZ'] = 'GMT' |
846 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>" |
849 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>" |
847 os.environ['CDPATH'] = '' |
850 os.environ['CDPATH'] = '' |
848 os.environ['COLUMNS'] = '80' |
851 os.environ['COLUMNS'] = '80' |
|
852 os.environ['GREP_OPTIONS'] = '' |
849 os.environ['http_proxy'] = '' |
853 os.environ['http_proxy'] = '' |
850 |
854 |
851 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE |
855 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE |
852 TESTDIR = os.environ["TESTDIR"] = os.getcwd() |
856 TESTDIR = os.environ["TESTDIR"] = os.getcwd() |
853 if options.tmpdir: |
857 if options.tmpdir: |
908 pypath = [PYTHONDIR, TESTDIR] |
912 pypath = [PYTHONDIR, TESTDIR] |
909 # We have to augment PYTHONPATH, rather than simply replacing |
913 # We have to augment PYTHONPATH, rather than simply replacing |
910 # it, in case external libraries are only available via current |
914 # it, in case external libraries are only available via current |
911 # PYTHONPATH. (In particular, the Subversion bindings on OS X |
915 # PYTHONPATH. (In particular, the Subversion bindings on OS X |
912 # are in /opt/subversion.) |
916 # are in /opt/subversion.) |
913 oldpypath = os.environ.get('PYTHONPATH') |
917 oldpypath = os.environ.get(IMPL_PATH) |
914 if oldpypath: |
918 if oldpypath: |
915 pypath.append(oldpypath) |
919 pypath.append(oldpypath) |
916 os.environ['PYTHONPATH'] = os.pathsep.join(pypath) |
920 os.environ[IMPL_PATH] = os.pathsep.join(pypath) |
917 |
921 |
918 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") |
922 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") |
919 |
923 |
920 if len(args) == 0: |
924 if len(args) == 0: |
921 args = os.listdir(".") |
925 args = os.listdir(".") |
932 return |
936 return |
933 |
937 |
934 vlog("# Using TESTDIR", TESTDIR) |
938 vlog("# Using TESTDIR", TESTDIR) |
935 vlog("# Using HGTMP", HGTMP) |
939 vlog("# Using HGTMP", HGTMP) |
936 vlog("# Using PATH", os.environ["PATH"]) |
940 vlog("# Using PATH", os.environ["PATH"]) |
937 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"]) |
941 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH]) |
938 |
942 |
939 try: |
943 try: |
940 if len(tests) > 1 and options.jobs > 1: |
944 if len(tests) > 1 and options.jobs > 1: |
941 runchildren(options, tests) |
945 runchildren(options, tests) |
942 else: |
946 else: |