Mercurial > hg
comparison tests/run-tests.py @ 8674:0941ee76489e
run-tests: redefine --with-hg so it takes the 'hg' script to run.
- in parseargs(), check that --with-hg value is valid
- add handy --local option for "--with-hg=../hg"
- ensure that we always set PATH and PYTHONPATH (not just
when doing a temporary install)
- override any existing PYTHONPATH, so test success does not
depend on whatever happens to be in the caller's environment
- give tests a little more control by exporting $PYTHON to the
environment; needed by test-convert and test-mergetool when
they run hg with a stripped-down $PATH
Also, add a big comment explaining all the corner cases to test for the
next person who tries to modify this script.
author | Greg Ward <greg-hg@gerg.ca> |
---|---|
date | Sun, 31 May 2009 15:20:31 -0400 |
parents | a8066f2fd1aa |
children | 78ab2a12b4d9 |
comparison
equal
deleted
inserted
replaced
8673:a8066f2fd1aa | 8674:0941ee76489e |
---|---|
4 # | 4 # |
5 # Copyright 2006 Matt Mackall <mpm@selenic.com> | 5 # Copyright 2006 Matt Mackall <mpm@selenic.com> |
6 # | 6 # |
7 # This software may be used and distributed according to the terms of the | 7 # This software may be used and distributed according to the terms of the |
8 # GNU General Public License version 2, incorporated herein by reference. | 8 # GNU General Public License version 2, incorporated herein by reference. |
9 | |
10 # Modifying this script is tricky because it has many modes: | |
11 # - serial (default) vs parallel (-jN, N > 1) | |
12 # - no coverage (default) vs coverage (-c, -C, -s) | |
13 # - temp install (default) vs specific hg script (--with-hg, --local) | |
14 # - tests are a mix of shell scripts and Python scripts | |
15 # | |
16 # If you change this script, it is recommended that you ensure you | |
17 # haven't broken it by running it in various modes with a representative | |
18 # sample of test scripts. For example: | |
19 # | |
20 # 1) serial, no coverage, temp install: | |
21 # ./run-tests.py test-s* | |
22 # 2) serial, no coverage, local hg: | |
23 # ./run-tests.py --local test-s* | |
24 # 3) serial, coverage, temp install: | |
25 # ./run-tests.py -c test-s* | |
26 # 4) serial, coverage, local hg: | |
27 # ./run-tests.py -c --local test-s* # unsupported | |
28 # 5) parallel, no coverage, temp install: | |
29 # ./run-tests.py -j2 test-s* | |
30 # 6) parallel, no coverage, local hg: | |
31 # ./run-tests.py -j2 --local test-s* | |
32 # 7) parallel, coverage, temp install: | |
33 # ./run-tests.py -j2 -c test-s* # currently broken | |
34 # 8) parallel, coverage, local install | |
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken) | |
36 # | |
37 # (You could use any subset of the tests: test-s* happens to match | |
38 # enough that it's worth doing parallel runs, few enough that it | |
39 # completes fairly quickly, includes both shell and Python scripts, and | |
40 # includes some scripts that run daemon processes.) | |
9 | 41 |
10 import difflib | 42 import difflib |
11 import errno | 43 import errno |
12 import optparse | 44 import optparse |
13 import os | 45 import os |
32 # reserved exit code to skip test (used by hghave) | 64 # reserved exit code to skip test (used by hghave) |
33 SKIPPED_STATUS = 80 | 65 SKIPPED_STATUS = 80 |
34 SKIPPED_PREFIX = 'skipped: ' | 66 SKIPPED_PREFIX = 'skipped: ' |
35 FAILED_PREFIX = 'hghave check failed: ' | 67 FAILED_PREFIX = 'hghave check failed: ' |
36 PYTHON = sys.executable | 68 PYTHON = sys.executable |
37 hgpkg = None | |
38 | 69 |
39 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] | 70 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] |
40 | 71 |
41 defaults = { | 72 defaults = { |
42 'jobs': ('HGTEST_JOBS', 1), | 73 'jobs': ('HGTEST_JOBS', 1), |
79 parser.add_option("-v", "--verbose", action="store_true", | 110 parser.add_option("-v", "--verbose", action="store_true", |
80 help="output verbose messages") | 111 help="output verbose messages") |
81 parser.add_option("-n", "--nodiff", action="store_true", | 112 parser.add_option("-n", "--nodiff", action="store_true", |
82 help="skip showing test changes") | 113 help="skip showing test changes") |
83 parser.add_option("--with-hg", type="string", | 114 parser.add_option("--with-hg", type="string", |
84 help="test existing install at given location") | 115 metavar="HG", |
116 help="test using specified hg script rather than a " | |
117 "temporary installation") | |
118 parser.add_option("--local", action="store_true", | |
119 help="shortcut for --with-hg=<testdir>/../hg") | |
85 parser.add_option("--pure", action="store_true", | 120 parser.add_option("--pure", action="store_true", |
86 help="use pure Python code instead of C extensions") | 121 help="use pure Python code instead of C extensions") |
87 | 122 |
88 for option, default in defaults.items(): | 123 for option, default in defaults.items(): |
89 defaults[option] = int(os.environ.get(*default)) | 124 defaults[option] = int(os.environ.get(*default)) |
90 parser.set_defaults(**defaults) | 125 parser.set_defaults(**defaults) |
91 (options, args) = parser.parse_args() | 126 (options, args) = parser.parse_args() |
92 | 127 |
93 global vlog | 128 if options.with_hg: |
129 if not (os.path.isfile(options.with_hg) and | |
130 os.access(options.with_hg, os.X_OK)): | |
131 parser.error('--with-hg must specify an executable hg script') | |
132 if not os.path.basename(options.with_hg) == 'hg': | |
133 sys.stderr.write('warning: --with-hg should specify an hg script') | |
134 if options.local: | |
135 testdir = os.path.dirname(os.path.realpath(sys.argv[0])) | |
136 hgbin = os.path.join(os.path.dirname(testdir), 'hg') | |
137 if not os.access(hgbin, os.X_OK): | |
138 parser.error('--local specified, but %r not found or not executable' | |
139 % hgbin) | |
140 options.with_hg = hgbin | |
141 | |
94 options.anycoverage = (options.cover or | 142 options.anycoverage = (options.cover or |
95 options.cover_stdlib or | 143 options.cover_stdlib or |
96 options.annotate) | 144 options.annotate) |
97 | 145 |
146 if options.anycoverage and options.with_hg: | |
147 # I'm not sure if this is a fundamental limitation or just a | |
148 # bug. But I don't want to waste people's time and energy doing | |
149 # test runs that don't give the results they want. | |
150 parser.error("sorry, coverage options do not work when --with-hg " | |
151 "or --local specified") | |
152 | |
153 global vlog | |
98 if options.verbose: | 154 if options.verbose: |
99 if options.jobs > 1 or options.child is not None: | 155 if options.jobs > 1 or options.child is not None: |
100 pid = "[%d]" % os.getpid() | 156 pid = "[%d]" % os.getpid() |
101 else: | 157 else: |
102 pid = None | 158 pid = None |
224 for line in f: | 280 for line in f: |
225 print line, | 281 print line, |
226 f.close() | 282 f.close() |
227 sys.exit(1) | 283 sys.exit(1) |
228 os.chdir(TESTDIR) | 284 os.chdir(TESTDIR) |
229 | |
230 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"]) | |
231 | |
232 pydir = os.pathsep.join([PYTHONDIR, TESTDIR]) | |
233 pythonpath = os.environ.get("PYTHONPATH") | |
234 if pythonpath: | |
235 pythonpath = pydir + os.pathsep + pythonpath | |
236 else: | |
237 pythonpath = pydir | |
238 os.environ["PYTHONPATH"] = pythonpath | |
239 | 285 |
240 usecorrectpython() | 286 usecorrectpython() |
241 | 287 |
242 vlog("# Installing dummy diffstat") | 288 vlog("# Installing dummy diffstat") |
243 f = open(os.path.join(BINDIR, 'diffstat'), 'w') | 289 f = open(os.path.join(BINDIR, 'diffstat'), 'w') |
510 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n' | 556 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n' |
511 ' (expected %s)\n' | 557 ' (expected %s)\n' |
512 % (verb, actualhg, expecthg)) | 558 % (verb, actualhg, expecthg)) |
513 | 559 |
514 def runchildren(options, tests): | 560 def runchildren(options, tests): |
515 if not options.with_hg: | 561 if INST: |
516 installhg(options) | 562 installhg(options) |
517 _checkhglib("Testing") | 563 _checkhglib("Testing") |
518 | 564 |
519 optcopy = dict(options.__dict__) | 565 optcopy = dict(options.__dict__) |
520 optcopy['jobs'] = 1 | 566 optcopy['jobs'] = 1 |
521 optcopy['with_hg'] = INST | 567 if optcopy['with_hg'] is None: |
568 optcopy['with_hg'] = os.path.join(BINDIR, "hg") | |
522 opts = [] | 569 opts = [] |
523 for opt, value in optcopy.iteritems(): | 570 for opt, value in optcopy.iteritems(): |
524 name = '--' + opt.replace('_', '-') | 571 name = '--' + opt.replace('_', '-') |
525 if value is True: | 572 if value is True: |
526 opts.append(name) | 573 opts.append(name) |
577 global DAEMON_PIDS, HGRCPATH | 624 global DAEMON_PIDS, HGRCPATH |
578 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') | 625 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids') |
579 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc') | 626 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc') |
580 | 627 |
581 try: | 628 try: |
582 if not options.with_hg: | 629 if INST: |
583 installhg(options) | 630 installhg(options) |
584 _checkhglib("Testing") | 631 _checkhglib("Testing") |
585 | 632 |
586 if options.timeout > 0: | 633 if options.timeout > 0: |
587 try: | 634 try: |
685 os.environ["HGPORT"] = str(options.port) | 732 os.environ["HGPORT"] = str(options.port) |
686 os.environ["HGPORT1"] = str(options.port + 1) | 733 os.environ["HGPORT1"] = str(options.port + 1) |
687 os.environ["HGPORT2"] = str(options.port + 2) | 734 os.environ["HGPORT2"] = str(options.port + 2) |
688 | 735 |
689 if options.with_hg: | 736 if options.with_hg: |
690 INST = options.with_hg | 737 INST = None |
738 BINDIR = os.path.dirname(os.path.realpath(options.with_hg)) | |
739 | |
740 # This looks redundant with how Python initializes sys.path from | |
741 # the location of the script being executed. Needed because the | |
742 # "hg" specified by --with-hg is not the only Python script | |
743 # executed in the test suite that needs to import 'mercurial' | |
744 # ... which means it's not really redundant at all. | |
745 PYTHONDIR = BINDIR | |
691 else: | 746 else: |
692 INST = os.path.join(HGTMP, "install") | 747 INST = os.path.join(HGTMP, "install") |
693 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin") | 748 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin") |
694 PYTHONDIR = os.path.join(INST, "lib", "python") | 749 PYTHONDIR = os.path.join(INST, "lib", "python") |
750 | |
751 os.environ["BINDIR"] = BINDIR | |
752 os.environ["PYTHON"] = PYTHON | |
753 | |
754 if not options.child: | |
755 path = [BINDIR] + os.environ["PATH"].split(os.pathsep) | |
756 os.environ["PATH"] = os.pathsep.join(path) | |
757 | |
758 # Deliberately override existing PYTHONPATH: do not want success | |
759 # to depend on what happens to be in caller's environment. | |
760 os.environ["PYTHONPATH"] = PYTHONDIR | |
761 | |
695 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") | 762 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage") |
696 | 763 |
697 if len(args) == 0: | 764 if len(args) == 0: |
698 args = os.listdir(".") | 765 args = os.listdir(".") |
699 args.sort() | 766 args.sort() |
708 print "# Ran 0 tests, 0 skipped, 0 failed." | 775 print "# Ran 0 tests, 0 skipped, 0 failed." |
709 return | 776 return |
710 | 777 |
711 vlog("# Using TESTDIR", TESTDIR) | 778 vlog("# Using TESTDIR", TESTDIR) |
712 vlog("# Using HGTMP", HGTMP) | 779 vlog("# Using HGTMP", HGTMP) |
780 vlog("# Using PATH", os.environ["PATH"]) | |
781 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"]) | |
713 | 782 |
714 try: | 783 try: |
715 if len(tests) > 1 and options.jobs > 1: | 784 if len(tests) > 1 and options.jobs > 1: |
716 runchildren(options, tests) | 785 runchildren(options, tests) |
717 else: | 786 else: |