##// END OF EJS Templates
tests: make hghave and run-tests exit on unknown feature requirements
Mads Kiilerich -
r18229:77d06793 default
parent child Browse files
Show More
@@ -1,77 +1,77 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Test the running system for features availability. Exit with zero
2 """Test the running system for features availability. Exit with zero
3 if all features are there, non-zero otherwise. If a feature name is
3 if all features are there, non-zero otherwise. If a feature name is
4 prefixed with "no-", the absence of feature is tested.
4 prefixed with "no-", the absence of feature is tested.
5 """
5 """
6 import optparse
6 import optparse
7 import sys
7 import sys
8 import hghave
8 import hghave
9
9
10 checks = hghave.checks
10 checks = hghave.checks
11
11
12 def list_features():
12 def list_features():
13 for name, feature in checks.iteritems():
13 for name, feature in checks.iteritems():
14 desc = feature[1]
14 desc = feature[1]
15 print name + ':', desc
15 print name + ':', desc
16
16
17 def test_features():
17 def test_features():
18 failed = 0
18 failed = 0
19 for name, feature in checks.iteritems():
19 for name, feature in checks.iteritems():
20 check, _ = feature
20 check, _ = feature
21 try:
21 try:
22 check()
22 check()
23 except Exception, e:
23 except Exception, e:
24 print "feature %s failed: %s" % (name, e)
24 print "feature %s failed: %s" % (name, e)
25 failed += 1
25 failed += 1
26 return failed
26 return failed
27
27
28 parser = optparse.OptionParser("%prog [options] [features]")
28 parser = optparse.OptionParser("%prog [options] [features]")
29 parser.add_option("--test-features", action="store_true",
29 parser.add_option("--test-features", action="store_true",
30 help="test available features")
30 help="test available features")
31 parser.add_option("--list-features", action="store_true",
31 parser.add_option("--list-features", action="store_true",
32 help="list available features")
32 help="list available features")
33 parser.add_option("-q", "--quiet", action="store_true",
33 parser.add_option("-q", "--quiet", action="store_true",
34 help="check features silently")
34 help="check features silently")
35
35
36 if __name__ == '__main__':
36 if __name__ == '__main__':
37 options, args = parser.parse_args()
37 options, args = parser.parse_args()
38 if options.list_features:
38 if options.list_features:
39 list_features()
39 list_features()
40 sys.exit(0)
40 sys.exit(0)
41
41
42 if options.test_features:
42 if options.test_features:
43 sys.exit(test_features())
43 sys.exit(test_features())
44
44
45 quiet = options.quiet
45 quiet = options.quiet
46
46
47 failures = 0
47 failures = 0
48
48
49 def error(msg):
49 def error(msg):
50 global failures
50 global failures
51 if not quiet:
51 if not quiet:
52 sys.stderr.write(msg + '\n')
52 sys.stderr.write(msg + '\n')
53 failures += 1
53 failures += 1
54
54
55 for feature in args:
55 for feature in args:
56 negate = feature.startswith('no-')
56 negate = feature.startswith('no-')
57 if negate:
57 if negate:
58 feature = feature[3:]
58 feature = feature[3:]
59
59
60 if feature not in checks:
60 if feature not in checks:
61 error('skipped: unknown feature: ' + feature)
61 error('skipped: unknown feature: ' + feature)
62 continue
62 sys.exit(2)
63
63
64 check, desc = checks[feature]
64 check, desc = checks[feature]
65 try:
65 try:
66 available = check()
66 available = check()
67 except Exception, e:
67 except Exception, e:
68 error('hghave check failed: ' + feature)
68 error('hghave check failed: ' + feature)
69 continue
69 continue
70
70
71 if not negate and not available:
71 if not negate and not available:
72 error('skipped: missing feature: ' + desc)
72 error('skipped: missing feature: ' + desc)
73 elif negate and available:
73 elif negate and available:
74 error('skipped: system supports %s' % desc)
74 error('skipped: system supports %s' % desc)
75
75
76 if failures != 0:
76 if failures != 0:
77 sys.exit(1)
77 sys.exit(1)
@@ -1,1349 +1,1352 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # run-tests.py - Run a set of tests on Mercurial
3 # run-tests.py - Run a set of tests on Mercurial
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 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 # Modifying this script is tricky because it has many modes:
10 # Modifying this script is tricky because it has many modes:
11 # - serial (default) vs parallel (-jN, N > 1)
11 # - serial (default) vs parallel (-jN, N > 1)
12 # - no coverage (default) vs coverage (-c, -C, -s)
12 # - no coverage (default) vs coverage (-c, -C, -s)
13 # - temp install (default) vs specific hg script (--with-hg, --local)
13 # - temp install (default) vs specific hg script (--with-hg, --local)
14 # - tests are a mix of shell scripts and Python scripts
14 # - tests are a mix of shell scripts and Python scripts
15 #
15 #
16 # If you change this script, it is recommended that you ensure you
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
17 # haven't broken it by running it in various modes with a representative
18 # sample of test scripts. For example:
18 # sample of test scripts. For example:
19 #
19 #
20 # 1) serial, no coverage, temp install:
20 # 1) serial, no coverage, temp install:
21 # ./run-tests.py test-s*
21 # ./run-tests.py test-s*
22 # 2) serial, no coverage, local hg:
22 # 2) serial, no coverage, local hg:
23 # ./run-tests.py --local test-s*
23 # ./run-tests.py --local test-s*
24 # 3) serial, coverage, temp install:
24 # 3) serial, coverage, temp install:
25 # ./run-tests.py -c test-s*
25 # ./run-tests.py -c test-s*
26 # 4) serial, coverage, local hg:
26 # 4) serial, coverage, local hg:
27 # ./run-tests.py -c --local test-s* # unsupported
27 # ./run-tests.py -c --local test-s* # unsupported
28 # 5) parallel, no coverage, temp install:
28 # 5) parallel, no coverage, temp install:
29 # ./run-tests.py -j2 test-s*
29 # ./run-tests.py -j2 test-s*
30 # 6) parallel, no coverage, local hg:
30 # 6) parallel, no coverage, local hg:
31 # ./run-tests.py -j2 --local test-s*
31 # ./run-tests.py -j2 --local test-s*
32 # 7) parallel, coverage, temp install:
32 # 7) parallel, coverage, temp install:
33 # ./run-tests.py -j2 -c test-s* # currently broken
33 # ./run-tests.py -j2 -c test-s* # currently broken
34 # 8) parallel, coverage, local install:
34 # 8) parallel, coverage, local install:
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
35 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
36 # 9) parallel, custom tmp dir:
36 # 9) parallel, custom tmp dir:
37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
37 # ./run-tests.py -j2 --tmpdir /tmp/myhgtests
38 #
38 #
39 # (You could use any subset of the tests: test-s* happens to match
39 # (You could use any subset of the tests: test-s* happens to match
40 # enough that it's worth doing parallel runs, few enough that it
40 # enough that it's worth doing parallel runs, few enough that it
41 # completes fairly quickly, includes both shell and Python scripts, and
41 # completes fairly quickly, includes both shell and Python scripts, and
42 # includes some scripts that run daemon processes.)
42 # includes some scripts that run daemon processes.)
43
43
44 from distutils import version
44 from distutils import version
45 import difflib
45 import difflib
46 import errno
46 import errno
47 import optparse
47 import optparse
48 import os
48 import os
49 import shutil
49 import shutil
50 import subprocess
50 import subprocess
51 import signal
51 import signal
52 import sys
52 import sys
53 import tempfile
53 import tempfile
54 import time
54 import time
55 import re
55 import re
56 import threading
56 import threading
57 import killdaemons as killmod
57 import killdaemons as killmod
58 import cPickle as pickle
58 import cPickle as pickle
59 import Queue as queue
59 import Queue as queue
60
60
61 processlock = threading.Lock()
61 processlock = threading.Lock()
62
62
63 closefds = os.name == 'posix'
63 closefds = os.name == 'posix'
64 def Popen4(cmd, wd, timeout):
64 def Popen4(cmd, wd, timeout):
65 processlock.acquire()
65 processlock.acquire()
66 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd,
66 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd,
67 close_fds=closefds,
67 close_fds=closefds,
68 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
68 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
69 stderr=subprocess.STDOUT)
69 stderr=subprocess.STDOUT)
70 processlock.release()
70 processlock.release()
71
71
72 p.fromchild = p.stdout
72 p.fromchild = p.stdout
73 p.tochild = p.stdin
73 p.tochild = p.stdin
74 p.childerr = p.stderr
74 p.childerr = p.stderr
75
75
76 p.timeout = False
76 p.timeout = False
77 if timeout:
77 if timeout:
78 def t():
78 def t():
79 start = time.time()
79 start = time.time()
80 while time.time() - start < timeout and p.returncode is None:
80 while time.time() - start < timeout and p.returncode is None:
81 time.sleep(.1)
81 time.sleep(.1)
82 p.timeout = True
82 p.timeout = True
83 if p.returncode is None:
83 if p.returncode is None:
84 terminate(p)
84 terminate(p)
85 threading.Thread(target=t).start()
85 threading.Thread(target=t).start()
86
86
87 return p
87 return p
88
88
89 # reserved exit code to skip test (used by hghave)
89 # reserved exit code to skip test (used by hghave)
90 SKIPPED_STATUS = 80
90 SKIPPED_STATUS = 80
91 SKIPPED_PREFIX = 'skipped: '
91 SKIPPED_PREFIX = 'skipped: '
92 FAILED_PREFIX = 'hghave check failed: '
92 FAILED_PREFIX = 'hghave check failed: '
93 PYTHON = sys.executable.replace('\\', '/')
93 PYTHON = sys.executable.replace('\\', '/')
94 IMPL_PATH = 'PYTHONPATH'
94 IMPL_PATH = 'PYTHONPATH'
95 if 'java' in sys.platform:
95 if 'java' in sys.platform:
96 IMPL_PATH = 'JYTHONPATH'
96 IMPL_PATH = 'JYTHONPATH'
97
97
98 requiredtools = [os.path.basename(sys.executable), "diff", "grep", "unzip",
98 requiredtools = [os.path.basename(sys.executable), "diff", "grep", "unzip",
99 "gunzip", "bunzip2", "sed"]
99 "gunzip", "bunzip2", "sed"]
100
100
101 defaults = {
101 defaults = {
102 'jobs': ('HGTEST_JOBS', 1),
102 'jobs': ('HGTEST_JOBS', 1),
103 'timeout': ('HGTEST_TIMEOUT', 180),
103 'timeout': ('HGTEST_TIMEOUT', 180),
104 'port': ('HGTEST_PORT', 20059),
104 'port': ('HGTEST_PORT', 20059),
105 'shell': ('HGTEST_SHELL', 'sh'),
105 'shell': ('HGTEST_SHELL', 'sh'),
106 }
106 }
107
107
108 def parselistfiles(files, listtype, warn=True):
108 def parselistfiles(files, listtype, warn=True):
109 entries = dict()
109 entries = dict()
110 for filename in files:
110 for filename in files:
111 try:
111 try:
112 path = os.path.expanduser(os.path.expandvars(filename))
112 path = os.path.expanduser(os.path.expandvars(filename))
113 f = open(path, "r")
113 f = open(path, "r")
114 except IOError, err:
114 except IOError, err:
115 if err.errno != errno.ENOENT:
115 if err.errno != errno.ENOENT:
116 raise
116 raise
117 if warn:
117 if warn:
118 print "warning: no such %s file: %s" % (listtype, filename)
118 print "warning: no such %s file: %s" % (listtype, filename)
119 continue
119 continue
120
120
121 for line in f.readlines():
121 for line in f.readlines():
122 line = line.split('#', 1)[0].strip()
122 line = line.split('#', 1)[0].strip()
123 if line:
123 if line:
124 entries[line] = filename
124 entries[line] = filename
125
125
126 f.close()
126 f.close()
127 return entries
127 return entries
128
128
129 def parseargs():
129 def parseargs():
130 parser = optparse.OptionParser("%prog [options] [tests]")
130 parser = optparse.OptionParser("%prog [options] [tests]")
131
131
132 # keep these sorted
132 # keep these sorted
133 parser.add_option("--blacklist", action="append",
133 parser.add_option("--blacklist", action="append",
134 help="skip tests listed in the specified blacklist file")
134 help="skip tests listed in the specified blacklist file")
135 parser.add_option("--whitelist", action="append",
135 parser.add_option("--whitelist", action="append",
136 help="always run tests listed in the specified whitelist file")
136 help="always run tests listed in the specified whitelist file")
137 parser.add_option("-C", "--annotate", action="store_true",
137 parser.add_option("-C", "--annotate", action="store_true",
138 help="output files annotated with coverage")
138 help="output files annotated with coverage")
139 parser.add_option("--child", type="int",
139 parser.add_option("--child", type="int",
140 help="run as child process, summary to given fd")
140 help="run as child process, summary to given fd")
141 parser.add_option("-c", "--cover", action="store_true",
141 parser.add_option("-c", "--cover", action="store_true",
142 help="print a test coverage report")
142 help="print a test coverage report")
143 parser.add_option("-d", "--debug", action="store_true",
143 parser.add_option("-d", "--debug", action="store_true",
144 help="debug mode: write output of test scripts to console"
144 help="debug mode: write output of test scripts to console"
145 " rather than capturing and diff'ing it (disables timeout)")
145 " rather than capturing and diff'ing it (disables timeout)")
146 parser.add_option("-f", "--first", action="store_true",
146 parser.add_option("-f", "--first", action="store_true",
147 help="exit on the first test failure")
147 help="exit on the first test failure")
148 parser.add_option("-H", "--htmlcov", action="store_true",
148 parser.add_option("-H", "--htmlcov", action="store_true",
149 help="create an HTML report of the coverage of the files")
149 help="create an HTML report of the coverage of the files")
150 parser.add_option("--inotify", action="store_true",
150 parser.add_option("--inotify", action="store_true",
151 help="enable inotify extension when running tests")
151 help="enable inotify extension when running tests")
152 parser.add_option("-i", "--interactive", action="store_true",
152 parser.add_option("-i", "--interactive", action="store_true",
153 help="prompt to accept changed output")
153 help="prompt to accept changed output")
154 parser.add_option("-j", "--jobs", type="int",
154 parser.add_option("-j", "--jobs", type="int",
155 help="number of jobs to run in parallel"
155 help="number of jobs to run in parallel"
156 " (default: $%s or %d)" % defaults['jobs'])
156 " (default: $%s or %d)" % defaults['jobs'])
157 parser.add_option("--keep-tmpdir", action="store_true",
157 parser.add_option("--keep-tmpdir", action="store_true",
158 help="keep temporary directory after running tests")
158 help="keep temporary directory after running tests")
159 parser.add_option("-k", "--keywords",
159 parser.add_option("-k", "--keywords",
160 help="run tests matching keywords")
160 help="run tests matching keywords")
161 parser.add_option("-l", "--local", action="store_true",
161 parser.add_option("-l", "--local", action="store_true",
162 help="shortcut for --with-hg=<testdir>/../hg")
162 help="shortcut for --with-hg=<testdir>/../hg")
163 parser.add_option("-n", "--nodiff", action="store_true",
163 parser.add_option("-n", "--nodiff", action="store_true",
164 help="skip showing test changes")
164 help="skip showing test changes")
165 parser.add_option("-p", "--port", type="int",
165 parser.add_option("-p", "--port", type="int",
166 help="port on which servers should listen"
166 help="port on which servers should listen"
167 " (default: $%s or %d)" % defaults['port'])
167 " (default: $%s or %d)" % defaults['port'])
168 parser.add_option("--compiler", type="string",
168 parser.add_option("--compiler", type="string",
169 help="compiler to build with")
169 help="compiler to build with")
170 parser.add_option("--pure", action="store_true",
170 parser.add_option("--pure", action="store_true",
171 help="use pure Python code instead of C extensions")
171 help="use pure Python code instead of C extensions")
172 parser.add_option("-R", "--restart", action="store_true",
172 parser.add_option("-R", "--restart", action="store_true",
173 help="restart at last error")
173 help="restart at last error")
174 parser.add_option("-r", "--retest", action="store_true",
174 parser.add_option("-r", "--retest", action="store_true",
175 help="retest failed tests")
175 help="retest failed tests")
176 parser.add_option("-S", "--noskips", action="store_true",
176 parser.add_option("-S", "--noskips", action="store_true",
177 help="don't report skip tests verbosely")
177 help="don't report skip tests verbosely")
178 parser.add_option("--shell", type="string",
178 parser.add_option("--shell", type="string",
179 help="shell to use (default: $%s or %s)" % defaults['shell'])
179 help="shell to use (default: $%s or %s)" % defaults['shell'])
180 parser.add_option("-t", "--timeout", type="int",
180 parser.add_option("-t", "--timeout", type="int",
181 help="kill errant tests after TIMEOUT seconds"
181 help="kill errant tests after TIMEOUT seconds"
182 " (default: $%s or %d)" % defaults['timeout'])
182 " (default: $%s or %d)" % defaults['timeout'])
183 parser.add_option("--time", action="store_true",
183 parser.add_option("--time", action="store_true",
184 help="time how long each test takes")
184 help="time how long each test takes")
185 parser.add_option("--tmpdir", type="string",
185 parser.add_option("--tmpdir", type="string",
186 help="run tests in the given temporary directory"
186 help="run tests in the given temporary directory"
187 " (implies --keep-tmpdir)")
187 " (implies --keep-tmpdir)")
188 parser.add_option("-v", "--verbose", action="store_true",
188 parser.add_option("-v", "--verbose", action="store_true",
189 help="output verbose messages")
189 help="output verbose messages")
190 parser.add_option("--view", type="string",
190 parser.add_option("--view", type="string",
191 help="external diff viewer")
191 help="external diff viewer")
192 parser.add_option("--with-hg", type="string",
192 parser.add_option("--with-hg", type="string",
193 metavar="HG",
193 metavar="HG",
194 help="test using specified hg script rather than a "
194 help="test using specified hg script rather than a "
195 "temporary installation")
195 "temporary installation")
196 parser.add_option("-3", "--py3k-warnings", action="store_true",
196 parser.add_option("-3", "--py3k-warnings", action="store_true",
197 help="enable Py3k warnings on Python 2.6+")
197 help="enable Py3k warnings on Python 2.6+")
198 parser.add_option('--extra-config-opt', action="append",
198 parser.add_option('--extra-config-opt', action="append",
199 help='set the given config opt in the test hgrc')
199 help='set the given config opt in the test hgrc')
200
200
201 for option, (envvar, default) in defaults.items():
201 for option, (envvar, default) in defaults.items():
202 defaults[option] = type(default)(os.environ.get(envvar, default))
202 defaults[option] = type(default)(os.environ.get(envvar, default))
203 parser.set_defaults(**defaults)
203 parser.set_defaults(**defaults)
204 (options, args) = parser.parse_args()
204 (options, args) = parser.parse_args()
205
205
206 # jython is always pure
206 # jython is always pure
207 if 'java' in sys.platform or '__pypy__' in sys.modules:
207 if 'java' in sys.platform or '__pypy__' in sys.modules:
208 options.pure = True
208 options.pure = True
209
209
210 if options.with_hg:
210 if options.with_hg:
211 options.with_hg = os.path.expanduser(options.with_hg)
211 options.with_hg = os.path.expanduser(options.with_hg)
212 if not (os.path.isfile(options.with_hg) and
212 if not (os.path.isfile(options.with_hg) and
213 os.access(options.with_hg, os.X_OK)):
213 os.access(options.with_hg, os.X_OK)):
214 parser.error('--with-hg must specify an executable hg script')
214 parser.error('--with-hg must specify an executable hg script')
215 if not os.path.basename(options.with_hg) == 'hg':
215 if not os.path.basename(options.with_hg) == 'hg':
216 sys.stderr.write('warning: --with-hg should specify an hg script\n')
216 sys.stderr.write('warning: --with-hg should specify an hg script\n')
217 if options.local:
217 if options.local:
218 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
218 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
219 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
219 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
220 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
220 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
221 parser.error('--local specified, but %r not found or not executable'
221 parser.error('--local specified, but %r not found or not executable'
222 % hgbin)
222 % hgbin)
223 options.with_hg = hgbin
223 options.with_hg = hgbin
224
224
225 options.anycoverage = options.cover or options.annotate or options.htmlcov
225 options.anycoverage = options.cover or options.annotate or options.htmlcov
226 if options.anycoverage:
226 if options.anycoverage:
227 try:
227 try:
228 import coverage
228 import coverage
229 covver = version.StrictVersion(coverage.__version__).version
229 covver = version.StrictVersion(coverage.__version__).version
230 if covver < (3, 3):
230 if covver < (3, 3):
231 parser.error('coverage options require coverage 3.3 or later')
231 parser.error('coverage options require coverage 3.3 or later')
232 except ImportError:
232 except ImportError:
233 parser.error('coverage options now require the coverage package')
233 parser.error('coverage options now require the coverage package')
234
234
235 if options.anycoverage and options.local:
235 if options.anycoverage and options.local:
236 # this needs some path mangling somewhere, I guess
236 # this needs some path mangling somewhere, I guess
237 parser.error("sorry, coverage options do not work when --local "
237 parser.error("sorry, coverage options do not work when --local "
238 "is specified")
238 "is specified")
239
239
240 global vlog
240 global vlog
241 if options.verbose:
241 if options.verbose:
242 if options.jobs > 1 or options.child is not None:
242 if options.jobs > 1 or options.child is not None:
243 pid = "[%d]" % os.getpid()
243 pid = "[%d]" % os.getpid()
244 else:
244 else:
245 pid = None
245 pid = None
246 def vlog(*msg):
246 def vlog(*msg):
247 iolock.acquire()
247 iolock.acquire()
248 if pid:
248 if pid:
249 print pid,
249 print pid,
250 for m in msg:
250 for m in msg:
251 print m,
251 print m,
252 print
252 print
253 sys.stdout.flush()
253 sys.stdout.flush()
254 iolock.release()
254 iolock.release()
255 else:
255 else:
256 vlog = lambda *msg: None
256 vlog = lambda *msg: None
257
257
258 if options.tmpdir:
258 if options.tmpdir:
259 options.tmpdir = os.path.expanduser(options.tmpdir)
259 options.tmpdir = os.path.expanduser(options.tmpdir)
260
260
261 if options.jobs < 1:
261 if options.jobs < 1:
262 parser.error('--jobs must be positive')
262 parser.error('--jobs must be positive')
263 if options.interactive and options.jobs > 1:
263 if options.interactive and options.jobs > 1:
264 print '(--interactive overrides --jobs)'
264 print '(--interactive overrides --jobs)'
265 options.jobs = 1
265 options.jobs = 1
266 if options.interactive and options.debug:
266 if options.interactive and options.debug:
267 parser.error("-i/--interactive and -d/--debug are incompatible")
267 parser.error("-i/--interactive and -d/--debug are incompatible")
268 if options.debug:
268 if options.debug:
269 if options.timeout != defaults['timeout']:
269 if options.timeout != defaults['timeout']:
270 sys.stderr.write(
270 sys.stderr.write(
271 'warning: --timeout option ignored with --debug\n')
271 'warning: --timeout option ignored with --debug\n')
272 options.timeout = 0
272 options.timeout = 0
273 if options.time:
273 if options.time:
274 sys.stderr.write(
274 sys.stderr.write(
275 'warning: --time option ignored with --debug\n')
275 'warning: --time option ignored with --debug\n')
276 options.time = False
276 options.time = False
277 if options.py3k_warnings:
277 if options.py3k_warnings:
278 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
278 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
279 parser.error('--py3k-warnings can only be used on Python 2.6+')
279 parser.error('--py3k-warnings can only be used on Python 2.6+')
280 if options.blacklist:
280 if options.blacklist:
281 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
281 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
282 if options.whitelist:
282 if options.whitelist:
283 options.whitelisted = parselistfiles(options.whitelist, 'whitelist',
283 options.whitelisted = parselistfiles(options.whitelist, 'whitelist',
284 warn=options.child is None)
284 warn=options.child is None)
285 else:
285 else:
286 options.whitelisted = {}
286 options.whitelisted = {}
287
287
288 return (options, args)
288 return (options, args)
289
289
290 def rename(src, dst):
290 def rename(src, dst):
291 """Like os.rename(), trade atomicity and opened files friendliness
291 """Like os.rename(), trade atomicity and opened files friendliness
292 for existing destination support.
292 for existing destination support.
293 """
293 """
294 shutil.copy(src, dst)
294 shutil.copy(src, dst)
295 os.remove(src)
295 os.remove(src)
296
296
297 def parsehghaveoutput(lines):
297 def parsehghaveoutput(lines):
298 '''Parse hghave log lines.
298 '''Parse hghave log lines.
299 Return tuple of lists (missing, failed):
299 Return tuple of lists (missing, failed):
300 * the missing/unknown features
300 * the missing/unknown features
301 * the features for which existence check failed'''
301 * the features for which existence check failed'''
302 missing = []
302 missing = []
303 failed = []
303 failed = []
304 for line in lines:
304 for line in lines:
305 if line.startswith(SKIPPED_PREFIX):
305 if line.startswith(SKIPPED_PREFIX):
306 line = line.splitlines()[0]
306 line = line.splitlines()[0]
307 missing.append(line[len(SKIPPED_PREFIX):])
307 missing.append(line[len(SKIPPED_PREFIX):])
308 elif line.startswith(FAILED_PREFIX):
308 elif line.startswith(FAILED_PREFIX):
309 line = line.splitlines()[0]
309 line = line.splitlines()[0]
310 failed.append(line[len(FAILED_PREFIX):])
310 failed.append(line[len(FAILED_PREFIX):])
311
311
312 return missing, failed
312 return missing, failed
313
313
314 def showdiff(expected, output, ref, err):
314 def showdiff(expected, output, ref, err):
315 print
315 print
316 for line in difflib.unified_diff(expected, output, ref, err):
316 for line in difflib.unified_diff(expected, output, ref, err):
317 sys.stdout.write(line)
317 sys.stdout.write(line)
318
318
319 def findprogram(program):
319 def findprogram(program):
320 """Search PATH for a executable program"""
320 """Search PATH for a executable program"""
321 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
321 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
322 name = os.path.join(p, program)
322 name = os.path.join(p, program)
323 if os.name == 'nt' or os.access(name, os.X_OK):
323 if os.name == 'nt' or os.access(name, os.X_OK):
324 return name
324 return name
325 return None
325 return None
326
326
327 def checktools():
327 def checktools():
328 # Before we go any further, check for pre-requisite tools
328 # Before we go any further, check for pre-requisite tools
329 # stuff from coreutils (cat, rm, etc) are not tested
329 # stuff from coreutils (cat, rm, etc) are not tested
330 for p in requiredtools:
330 for p in requiredtools:
331 if os.name == 'nt' and not p.endswith('.exe'):
331 if os.name == 'nt' and not p.endswith('.exe'):
332 p += '.exe'
332 p += '.exe'
333 found = findprogram(p)
333 found = findprogram(p)
334 if found:
334 if found:
335 vlog("# Found prerequisite", p, "at", found)
335 vlog("# Found prerequisite", p, "at", found)
336 else:
336 else:
337 print "WARNING: Did not find prerequisite tool: "+p
337 print "WARNING: Did not find prerequisite tool: "+p
338
338
339 def terminate(proc):
339 def terminate(proc):
340 """Terminate subprocess (with fallback for Python versions < 2.6)"""
340 """Terminate subprocess (with fallback for Python versions < 2.6)"""
341 vlog('# Terminating process %d' % proc.pid)
341 vlog('# Terminating process %d' % proc.pid)
342 try:
342 try:
343 getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
343 getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
344 except OSError:
344 except OSError:
345 pass
345 pass
346
346
347 def killdaemons():
347 def killdaemons():
348 return killmod.killdaemons(DAEMON_PIDS, tryhard=False, remove=True,
348 return killmod.killdaemons(DAEMON_PIDS, tryhard=False, remove=True,
349 logfn=vlog)
349 logfn=vlog)
350
350
351 def cleanup(options):
351 def cleanup(options):
352 if not options.keep_tmpdir:
352 if not options.keep_tmpdir:
353 vlog("# Cleaning up HGTMP", HGTMP)
353 vlog("# Cleaning up HGTMP", HGTMP)
354 shutil.rmtree(HGTMP, True)
354 shutil.rmtree(HGTMP, True)
355
355
356 def usecorrectpython():
356 def usecorrectpython():
357 # some tests run python interpreter. they must use same
357 # some tests run python interpreter. they must use same
358 # interpreter we use or bad things will happen.
358 # interpreter we use or bad things will happen.
359 exedir, exename = os.path.split(sys.executable)
359 exedir, exename = os.path.split(sys.executable)
360 if exename in ('python', 'python.exe'):
360 if exename in ('python', 'python.exe'):
361 path = findprogram(exename)
361 path = findprogram(exename)
362 if os.path.dirname(path) == exedir:
362 if os.path.dirname(path) == exedir:
363 return
363 return
364 else:
364 else:
365 exename = 'python'
365 exename = 'python'
366 if sys.platform == 'win32':
366 if sys.platform == 'win32':
367 exename = 'python.exe'
367 exename = 'python.exe'
368 if getattr(os, 'symlink', None):
368 if getattr(os, 'symlink', None):
369 vlog("# Making python executable in test path a symlink to '%s'" %
369 vlog("# Making python executable in test path a symlink to '%s'" %
370 sys.executable)
370 sys.executable)
371 mypython = os.path.join(BINDIR, exename)
371 mypython = os.path.join(BINDIR, exename)
372 try:
372 try:
373 os.symlink(sys.executable, mypython)
373 os.symlink(sys.executable, mypython)
374 except OSError, err:
374 except OSError, err:
375 # child processes may race, which is harmless
375 # child processes may race, which is harmless
376 if err.errno != errno.EEXIST:
376 if err.errno != errno.EEXIST:
377 raise
377 raise
378 else:
378 else:
379 vlog("# Modifying search path to find %s in '%s'" % (exename, exedir))
379 vlog("# Modifying search path to find %s in '%s'" % (exename, exedir))
380 path = os.environ['PATH'].split(os.pathsep)
380 path = os.environ['PATH'].split(os.pathsep)
381 while exedir in path:
381 while exedir in path:
382 path.remove(exedir)
382 path.remove(exedir)
383 os.environ['PATH'] = os.pathsep.join([exedir] + path)
383 os.environ['PATH'] = os.pathsep.join([exedir] + path)
384 if not findprogram(exename):
384 if not findprogram(exename):
385 print "WARNING: Cannot find %s in search path" % exename
385 print "WARNING: Cannot find %s in search path" % exename
386
386
387 def installhg(options):
387 def installhg(options):
388 vlog("# Performing temporary installation of HG")
388 vlog("# Performing temporary installation of HG")
389 installerrs = os.path.join("tests", "install.err")
389 installerrs = os.path.join("tests", "install.err")
390 compiler = ''
390 compiler = ''
391 if options.compiler:
391 if options.compiler:
392 compiler = '--compiler ' + options.compiler
392 compiler = '--compiler ' + options.compiler
393 pure = options.pure and "--pure" or ""
393 pure = options.pure and "--pure" or ""
394
394
395 # Run installer in hg root
395 # Run installer in hg root
396 script = os.path.realpath(sys.argv[0])
396 script = os.path.realpath(sys.argv[0])
397 hgroot = os.path.dirname(os.path.dirname(script))
397 hgroot = os.path.dirname(os.path.dirname(script))
398 os.chdir(hgroot)
398 os.chdir(hgroot)
399 nohome = '--home=""'
399 nohome = '--home=""'
400 if os.name == 'nt':
400 if os.name == 'nt':
401 # The --home="" trick works only on OS where os.sep == '/'
401 # The --home="" trick works only on OS where os.sep == '/'
402 # because of a distutils convert_path() fast-path. Avoid it at
402 # because of a distutils convert_path() fast-path. Avoid it at
403 # least on Windows for now, deal with .pydistutils.cfg bugs
403 # least on Windows for now, deal with .pydistutils.cfg bugs
404 # when they happen.
404 # when they happen.
405 nohome = ''
405 nohome = ''
406 cmd = ('%(exe)s setup.py %(pure)s clean --all'
406 cmd = ('%(exe)s setup.py %(pure)s clean --all'
407 ' build %(compiler)s --build-base="%(base)s"'
407 ' build %(compiler)s --build-base="%(base)s"'
408 ' install --force --prefix="%(prefix)s" --install-lib="%(libdir)s"'
408 ' install --force --prefix="%(prefix)s" --install-lib="%(libdir)s"'
409 ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
409 ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
410 % dict(exe=sys.executable, pure=pure, compiler=compiler,
410 % dict(exe=sys.executable, pure=pure, compiler=compiler,
411 base=os.path.join(HGTMP, "build"),
411 base=os.path.join(HGTMP, "build"),
412 prefix=INST, libdir=PYTHONDIR, bindir=BINDIR,
412 prefix=INST, libdir=PYTHONDIR, bindir=BINDIR,
413 nohome=nohome, logfile=installerrs))
413 nohome=nohome, logfile=installerrs))
414 vlog("# Running", cmd)
414 vlog("# Running", cmd)
415 if os.system(cmd) == 0:
415 if os.system(cmd) == 0:
416 if not options.verbose:
416 if not options.verbose:
417 os.remove(installerrs)
417 os.remove(installerrs)
418 else:
418 else:
419 f = open(installerrs)
419 f = open(installerrs)
420 for line in f:
420 for line in f:
421 print line,
421 print line,
422 f.close()
422 f.close()
423 sys.exit(1)
423 sys.exit(1)
424 os.chdir(TESTDIR)
424 os.chdir(TESTDIR)
425
425
426 usecorrectpython()
426 usecorrectpython()
427
427
428 vlog("# Installing dummy diffstat")
428 vlog("# Installing dummy diffstat")
429 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
429 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
430 f.write('#!' + sys.executable + '\n'
430 f.write('#!' + sys.executable + '\n'
431 'import sys\n'
431 'import sys\n'
432 'files = 0\n'
432 'files = 0\n'
433 'for line in sys.stdin:\n'
433 'for line in sys.stdin:\n'
434 ' if line.startswith("diff "):\n'
434 ' if line.startswith("diff "):\n'
435 ' files += 1\n'
435 ' files += 1\n'
436 'sys.stdout.write("files patched: %d\\n" % files)\n')
436 'sys.stdout.write("files patched: %d\\n" % files)\n')
437 f.close()
437 f.close()
438 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
438 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
439
439
440 if options.py3k_warnings and not options.anycoverage:
440 if options.py3k_warnings and not options.anycoverage:
441 vlog("# Updating hg command to enable Py3k Warnings switch")
441 vlog("# Updating hg command to enable Py3k Warnings switch")
442 f = open(os.path.join(BINDIR, 'hg'), 'r')
442 f = open(os.path.join(BINDIR, 'hg'), 'r')
443 lines = [line.rstrip() for line in f]
443 lines = [line.rstrip() for line in f]
444 lines[0] += ' -3'
444 lines[0] += ' -3'
445 f.close()
445 f.close()
446 f = open(os.path.join(BINDIR, 'hg'), 'w')
446 f = open(os.path.join(BINDIR, 'hg'), 'w')
447 for line in lines:
447 for line in lines:
448 f.write(line + '\n')
448 f.write(line + '\n')
449 f.close()
449 f.close()
450
450
451 hgbat = os.path.join(BINDIR, 'hg.bat')
451 hgbat = os.path.join(BINDIR, 'hg.bat')
452 if os.path.isfile(hgbat):
452 if os.path.isfile(hgbat):
453 # hg.bat expects to be put in bin/scripts while run-tests.py
453 # hg.bat expects to be put in bin/scripts while run-tests.py
454 # installation layout put it in bin/ directly. Fix it
454 # installation layout put it in bin/ directly. Fix it
455 f = open(hgbat, 'rb')
455 f = open(hgbat, 'rb')
456 data = f.read()
456 data = f.read()
457 f.close()
457 f.close()
458 if '"%~dp0..\python" "%~dp0hg" %*' in data:
458 if '"%~dp0..\python" "%~dp0hg" %*' in data:
459 data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
459 data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
460 '"%~dp0python" "%~dp0hg" %*')
460 '"%~dp0python" "%~dp0hg" %*')
461 f = open(hgbat, 'wb')
461 f = open(hgbat, 'wb')
462 f.write(data)
462 f.write(data)
463 f.close()
463 f.close()
464 else:
464 else:
465 print 'WARNING: cannot fix hg.bat reference to python.exe'
465 print 'WARNING: cannot fix hg.bat reference to python.exe'
466
466
467 if options.anycoverage:
467 if options.anycoverage:
468 custom = os.path.join(TESTDIR, 'sitecustomize.py')
468 custom = os.path.join(TESTDIR, 'sitecustomize.py')
469 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
469 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
470 vlog('# Installing coverage trigger to %s' % target)
470 vlog('# Installing coverage trigger to %s' % target)
471 shutil.copyfile(custom, target)
471 shutil.copyfile(custom, target)
472 rc = os.path.join(TESTDIR, '.coveragerc')
472 rc = os.path.join(TESTDIR, '.coveragerc')
473 vlog('# Installing coverage rc to %s' % rc)
473 vlog('# Installing coverage rc to %s' % rc)
474 os.environ['COVERAGE_PROCESS_START'] = rc
474 os.environ['COVERAGE_PROCESS_START'] = rc
475 fn = os.path.join(INST, '..', '.coverage')
475 fn = os.path.join(INST, '..', '.coverage')
476 os.environ['COVERAGE_FILE'] = fn
476 os.environ['COVERAGE_FILE'] = fn
477
477
478 def outputtimes(options):
478 def outputtimes(options):
479 vlog('# Producing time report')
479 vlog('# Producing time report')
480 times.sort(key=lambda t: (t[1], t[0]), reverse=True)
480 times.sort(key=lambda t: (t[1], t[0]), reverse=True)
481 cols = '%7.3f %s'
481 cols = '%7.3f %s'
482 print '\n%-7s %s' % ('Time', 'Test')
482 print '\n%-7s %s' % ('Time', 'Test')
483 for test, timetaken in times:
483 for test, timetaken in times:
484 print cols % (timetaken, test)
484 print cols % (timetaken, test)
485
485
486 def outputcoverage(options):
486 def outputcoverage(options):
487
487
488 vlog('# Producing coverage report')
488 vlog('# Producing coverage report')
489 os.chdir(PYTHONDIR)
489 os.chdir(PYTHONDIR)
490
490
491 def covrun(*args):
491 def covrun(*args):
492 cmd = 'coverage %s' % ' '.join(args)
492 cmd = 'coverage %s' % ' '.join(args)
493 vlog('# Running: %s' % cmd)
493 vlog('# Running: %s' % cmd)
494 os.system(cmd)
494 os.system(cmd)
495
495
496 if options.child:
496 if options.child:
497 return
497 return
498
498
499 covrun('-c')
499 covrun('-c')
500 omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
500 omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
501 covrun('-i', '-r', '"--omit=%s"' % omit) # report
501 covrun('-i', '-r', '"--omit=%s"' % omit) # report
502 if options.htmlcov:
502 if options.htmlcov:
503 htmldir = os.path.join(TESTDIR, 'htmlcov')
503 htmldir = os.path.join(TESTDIR, 'htmlcov')
504 covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
504 covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
505 if options.annotate:
505 if options.annotate:
506 adir = os.path.join(TESTDIR, 'annotated')
506 adir = os.path.join(TESTDIR, 'annotated')
507 if not os.path.isdir(adir):
507 if not os.path.isdir(adir):
508 os.mkdir(adir)
508 os.mkdir(adir)
509 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
509 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
510
510
511 def pytest(test, wd, options, replacements):
511 def pytest(test, wd, options, replacements):
512 py3kswitch = options.py3k_warnings and ' -3' or ''
512 py3kswitch = options.py3k_warnings and ' -3' or ''
513 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
513 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
514 vlog("# Running", cmd)
514 vlog("# Running", cmd)
515 if os.name == 'nt':
515 if os.name == 'nt':
516 replacements.append((r'\r\n', '\n'))
516 replacements.append((r'\r\n', '\n'))
517 return run(cmd, wd, options, replacements)
517 return run(cmd, wd, options, replacements)
518
518
519 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
519 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
520 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
520 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
521 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
521 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
522 escapemap.update({'\\': '\\\\', '\r': r'\r'})
522 escapemap.update({'\\': '\\\\', '\r': r'\r'})
523 def escapef(m):
523 def escapef(m):
524 return escapemap[m.group(0)]
524 return escapemap[m.group(0)]
525 def stringescape(s):
525 def stringescape(s):
526 return escapesub(escapef, s)
526 return escapesub(escapef, s)
527
527
528 def rematch(el, l):
528 def rematch(el, l):
529 try:
529 try:
530 # use \Z to ensure that the regex matches to the end of the string
530 # use \Z to ensure that the regex matches to the end of the string
531 if os.name == 'nt':
531 if os.name == 'nt':
532 return re.match(el + r'\r?\n\Z', l)
532 return re.match(el + r'\r?\n\Z', l)
533 return re.match(el + r'\n\Z', l)
533 return re.match(el + r'\n\Z', l)
534 except re.error:
534 except re.error:
535 # el is an invalid regex
535 # el is an invalid regex
536 return False
536 return False
537
537
538 def globmatch(el, l):
538 def globmatch(el, l):
539 # The only supported special characters are * and ? plus / which also
539 # The only supported special characters are * and ? plus / which also
540 # matches \ on windows. Escaping of these caracters is supported.
540 # matches \ on windows. Escaping of these caracters is supported.
541 i, n = 0, len(el)
541 i, n = 0, len(el)
542 res = ''
542 res = ''
543 while i < n:
543 while i < n:
544 c = el[i]
544 c = el[i]
545 i += 1
545 i += 1
546 if c == '\\' and el[i] in '*?\\/':
546 if c == '\\' and el[i] in '*?\\/':
547 res += el[i - 1:i + 1]
547 res += el[i - 1:i + 1]
548 i += 1
548 i += 1
549 elif c == '*':
549 elif c == '*':
550 res += '.*'
550 res += '.*'
551 elif c == '?':
551 elif c == '?':
552 res += '.'
552 res += '.'
553 elif c == '/' and os.name == 'nt':
553 elif c == '/' and os.name == 'nt':
554 res += '[/\\\\]'
554 res += '[/\\\\]'
555 else:
555 else:
556 res += re.escape(c)
556 res += re.escape(c)
557 return rematch(res, l)
557 return rematch(res, l)
558
558
559 def linematch(el, l):
559 def linematch(el, l):
560 if el == l: # perfect match (fast)
560 if el == l: # perfect match (fast)
561 return True
561 return True
562 if el:
562 if el:
563 if el.endswith(" (esc)\n"):
563 if el.endswith(" (esc)\n"):
564 el = el[:-7].decode('string-escape') + '\n'
564 el = el[:-7].decode('string-escape') + '\n'
565 if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l:
565 if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l:
566 return True
566 return True
567 if (el.endswith(" (re)\n") and rematch(el[:-6], l) or
567 if (el.endswith(" (re)\n") and rematch(el[:-6], l) or
568 el.endswith(" (glob)\n") and globmatch(el[:-8], l)):
568 el.endswith(" (glob)\n") and globmatch(el[:-8], l)):
569 return True
569 return True
570 return False
570 return False
571
571
572 def tsttest(test, wd, options, replacements):
572 def tsttest(test, wd, options, replacements):
573 # We generate a shell script which outputs unique markers to line
573 # We generate a shell script which outputs unique markers to line
574 # up script results with our source. These markers include input
574 # up script results with our source. These markers include input
575 # line number and the last return code
575 # line number and the last return code
576 salt = "SALT" + str(time.time())
576 salt = "SALT" + str(time.time())
577 def addsalt(line, inpython):
577 def addsalt(line, inpython):
578 if inpython:
578 if inpython:
579 script.append('%s %d 0\n' % (salt, line))
579 script.append('%s %d 0\n' % (salt, line))
580 else:
580 else:
581 script.append('echo %s %s $?\n' % (salt, line))
581 script.append('echo %s %s $?\n' % (salt, line))
582
582
583 # After we run the shell script, we re-unify the script output
583 # After we run the shell script, we re-unify the script output
584 # with non-active parts of the source, with synchronization by our
584 # with non-active parts of the source, with synchronization by our
585 # SALT line number markers. The after table contains the
585 # SALT line number markers. The after table contains the
586 # non-active components, ordered by line number
586 # non-active components, ordered by line number
587 after = {}
587 after = {}
588 pos = prepos = -1
588 pos = prepos = -1
589
589
590 # Expected shellscript output
590 # Expected shellscript output
591 expected = {}
591 expected = {}
592
592
593 # We keep track of whether or not we're in a Python block so we
593 # We keep track of whether or not we're in a Python block so we
594 # can generate the surrounding doctest magic
594 # can generate the surrounding doctest magic
595 inpython = False
595 inpython = False
596
596
597 # True or False when in a true or false conditional section
597 # True or False when in a true or false conditional section
598 skipping = None
598 skipping = None
599
599
600 def hghave(reqs):
600 def hghave(reqs):
601 # TODO: do something smarter when all other uses of hghave is gone
601 # TODO: do something smarter when all other uses of hghave is gone
602 tdir = TESTDIR.replace('\\', '/')
602 tdir = TESTDIR.replace('\\', '/')
603 proc = Popen4('%s -c "%s/hghave %s"' %
603 proc = Popen4('%s -c "%s/hghave %s"' %
604 (options.shell, tdir, ' '.join(reqs)), wd, 0)
604 (options.shell, tdir, ' '.join(reqs)), wd, 0)
605 proc.communicate()
605 stdout, stderr = proc.communicate()
606 ret = proc.wait()
606 ret = proc.wait()
607 if wifexited(ret):
607 if wifexited(ret):
608 ret = os.WEXITSTATUS(ret)
608 ret = os.WEXITSTATUS(ret)
609 if ret == 2:
610 print stdout
611 sys.exit(1)
609 return ret == 0
612 return ret == 0
610
613
611 f = open(test)
614 f = open(test)
612 t = f.readlines()
615 t = f.readlines()
613 f.close()
616 f.close()
614
617
615 script = []
618 script = []
616 if options.debug:
619 if options.debug:
617 script.append('set -x\n')
620 script.append('set -x\n')
618 if os.getenv('MSYSTEM'):
621 if os.getenv('MSYSTEM'):
619 script.append('alias pwd="pwd -W"\n')
622 script.append('alias pwd="pwd -W"\n')
620 for n, l in enumerate(t):
623 for n, l in enumerate(t):
621 if not l.endswith('\n'):
624 if not l.endswith('\n'):
622 l += '\n'
625 l += '\n'
623 if l.startswith('#if'):
626 if l.startswith('#if'):
624 if skipping is not None:
627 if skipping is not None:
625 after.setdefault(pos, []).append(' !!! nested #if\n')
628 after.setdefault(pos, []).append(' !!! nested #if\n')
626 skipping = not hghave(l.split()[1:])
629 skipping = not hghave(l.split()[1:])
627 after.setdefault(pos, []).append(l)
630 after.setdefault(pos, []).append(l)
628 elif l.startswith('#else'):
631 elif l.startswith('#else'):
629 if skipping is None:
632 if skipping is None:
630 after.setdefault(pos, []).append(' !!! missing #if\n')
633 after.setdefault(pos, []).append(' !!! missing #if\n')
631 skipping = not skipping
634 skipping = not skipping
632 after.setdefault(pos, []).append(l)
635 after.setdefault(pos, []).append(l)
633 elif l.startswith('#endif'):
636 elif l.startswith('#endif'):
634 if skipping is None:
637 if skipping is None:
635 after.setdefault(pos, []).append(' !!! missing #if\n')
638 after.setdefault(pos, []).append(' !!! missing #if\n')
636 skipping = None
639 skipping = None
637 after.setdefault(pos, []).append(l)
640 after.setdefault(pos, []).append(l)
638 elif skipping:
641 elif skipping:
639 after.setdefault(pos, []).append(l)
642 after.setdefault(pos, []).append(l)
640 elif l.startswith(' >>> '): # python inlines
643 elif l.startswith(' >>> '): # python inlines
641 after.setdefault(pos, []).append(l)
644 after.setdefault(pos, []).append(l)
642 prepos = pos
645 prepos = pos
643 pos = n
646 pos = n
644 if not inpython:
647 if not inpython:
645 # we've just entered a Python block, add the header
648 # we've just entered a Python block, add the header
646 inpython = True
649 inpython = True
647 addsalt(prepos, False) # make sure we report the exit code
650 addsalt(prepos, False) # make sure we report the exit code
648 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
651 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
649 addsalt(n, True)
652 addsalt(n, True)
650 script.append(l[2:])
653 script.append(l[2:])
651 elif l.startswith(' ... '): # python inlines
654 elif l.startswith(' ... '): # python inlines
652 after.setdefault(prepos, []).append(l)
655 after.setdefault(prepos, []).append(l)
653 script.append(l[2:])
656 script.append(l[2:])
654 elif l.startswith(' $ '): # commands
657 elif l.startswith(' $ '): # commands
655 if inpython:
658 if inpython:
656 script.append("EOF\n")
659 script.append("EOF\n")
657 inpython = False
660 inpython = False
658 after.setdefault(pos, []).append(l)
661 after.setdefault(pos, []).append(l)
659 prepos = pos
662 prepos = pos
660 pos = n
663 pos = n
661 addsalt(n, False)
664 addsalt(n, False)
662 cmd = l[4:].split()
665 cmd = l[4:].split()
663 if len(cmd) == 2 and cmd[0] == 'cd':
666 if len(cmd) == 2 and cmd[0] == 'cd':
664 l = ' $ cd %s || exit 1\n' % cmd[1]
667 l = ' $ cd %s || exit 1\n' % cmd[1]
665 script.append(l[4:])
668 script.append(l[4:])
666 elif l.startswith(' > '): # continuations
669 elif l.startswith(' > '): # continuations
667 after.setdefault(prepos, []).append(l)
670 after.setdefault(prepos, []).append(l)
668 script.append(l[4:])
671 script.append(l[4:])
669 elif l.startswith(' '): # results
672 elif l.startswith(' '): # results
670 # queue up a list of expected results
673 # queue up a list of expected results
671 expected.setdefault(pos, []).append(l[2:])
674 expected.setdefault(pos, []).append(l[2:])
672 else:
675 else:
673 if inpython:
676 if inpython:
674 script.append("EOF\n")
677 script.append("EOF\n")
675 inpython = False
678 inpython = False
676 # non-command/result - queue up for merged output
679 # non-command/result - queue up for merged output
677 after.setdefault(pos, []).append(l)
680 after.setdefault(pos, []).append(l)
678
681
679 if inpython:
682 if inpython:
680 script.append("EOF\n")
683 script.append("EOF\n")
681 if skipping is not None:
684 if skipping is not None:
682 after.setdefault(pos, []).append(' !!! missing #endif\n')
685 after.setdefault(pos, []).append(' !!! missing #endif\n')
683 addsalt(n + 1, False)
686 addsalt(n + 1, False)
684
687
685 # Write out the script and execute it
688 # Write out the script and execute it
686 fd, name = tempfile.mkstemp(suffix='hg-tst')
689 fd, name = tempfile.mkstemp(suffix='hg-tst')
687 try:
690 try:
688 for l in script:
691 for l in script:
689 os.write(fd, l)
692 os.write(fd, l)
690 os.close(fd)
693 os.close(fd)
691
694
692 cmd = '%s "%s"' % (options.shell, name)
695 cmd = '%s "%s"' % (options.shell, name)
693 vlog("# Running", cmd)
696 vlog("# Running", cmd)
694 exitcode, output = run(cmd, wd, options, replacements)
697 exitcode, output = run(cmd, wd, options, replacements)
695 # do not merge output if skipped, return hghave message instead
698 # do not merge output if skipped, return hghave message instead
696 # similarly, with --debug, output is None
699 # similarly, with --debug, output is None
697 if exitcode == SKIPPED_STATUS or output is None:
700 if exitcode == SKIPPED_STATUS or output is None:
698 return exitcode, output
701 return exitcode, output
699 finally:
702 finally:
700 os.remove(name)
703 os.remove(name)
701
704
702 # Merge the script output back into a unified test
705 # Merge the script output back into a unified test
703
706
704 pos = -1
707 pos = -1
705 postout = []
708 postout = []
706 ret = 0
709 ret = 0
707 for l in output:
710 for l in output:
708 lout, lcmd = l, None
711 lout, lcmd = l, None
709 if salt in l:
712 if salt in l:
710 lout, lcmd = l.split(salt, 1)
713 lout, lcmd = l.split(salt, 1)
711
714
712 if lout:
715 if lout:
713 if not lout.endswith('\n'):
716 if not lout.endswith('\n'):
714 lout += ' (no-eol)\n'
717 lout += ' (no-eol)\n'
715
718
716 # find the expected output at the current position
719 # find the expected output at the current position
717 el = None
720 el = None
718 if pos in expected and expected[pos]:
721 if pos in expected and expected[pos]:
719 el = expected[pos].pop(0)
722 el = expected[pos].pop(0)
720
723
721 if linematch(el, lout):
724 if linematch(el, lout):
722 postout.append(" " + el)
725 postout.append(" " + el)
723 else:
726 else:
724 if needescape(lout):
727 if needescape(lout):
725 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
728 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
726 postout.append(" " + lout) # let diff deal with it
729 postout.append(" " + lout) # let diff deal with it
727
730
728 if lcmd:
731 if lcmd:
729 # add on last return code
732 # add on last return code
730 ret = int(lcmd.split()[1])
733 ret = int(lcmd.split()[1])
731 if ret != 0:
734 if ret != 0:
732 postout.append(" [%s]\n" % ret)
735 postout.append(" [%s]\n" % ret)
733 if pos in after:
736 if pos in after:
734 # merge in non-active test bits
737 # merge in non-active test bits
735 postout += after.pop(pos)
738 postout += after.pop(pos)
736 pos = int(lcmd.split()[0])
739 pos = int(lcmd.split()[0])
737
740
738 if pos in after:
741 if pos in after:
739 postout += after.pop(pos)
742 postout += after.pop(pos)
740
743
741 return exitcode, postout
744 return exitcode, postout
742
745
743 wifexited = getattr(os, "WIFEXITED", lambda x: False)
746 wifexited = getattr(os, "WIFEXITED", lambda x: False)
744 def run(cmd, wd, options, replacements):
747 def run(cmd, wd, options, replacements):
745 """Run command in a sub-process, capturing the output (stdout and stderr).
748 """Run command in a sub-process, capturing the output (stdout and stderr).
746 Return a tuple (exitcode, output). output is None in debug mode."""
749 Return a tuple (exitcode, output). output is None in debug mode."""
747 # TODO: Use subprocess.Popen if we're running on Python 2.4
750 # TODO: Use subprocess.Popen if we're running on Python 2.4
748 if options.debug:
751 if options.debug:
749 proc = subprocess.Popen(cmd, shell=True, cwd=wd)
752 proc = subprocess.Popen(cmd, shell=True, cwd=wd)
750 ret = proc.wait()
753 ret = proc.wait()
751 return (ret, None)
754 return (ret, None)
752
755
753 proc = Popen4(cmd, wd, options.timeout)
756 proc = Popen4(cmd, wd, options.timeout)
754 def cleanup():
757 def cleanup():
755 terminate(proc)
758 terminate(proc)
756 ret = proc.wait()
759 ret = proc.wait()
757 if ret == 0:
760 if ret == 0:
758 ret = signal.SIGTERM << 8
761 ret = signal.SIGTERM << 8
759 killdaemons()
762 killdaemons()
760 return ret
763 return ret
761
764
762 output = ''
765 output = ''
763 proc.tochild.close()
766 proc.tochild.close()
764
767
765 try:
768 try:
766 output = proc.fromchild.read()
769 output = proc.fromchild.read()
767 except KeyboardInterrupt:
770 except KeyboardInterrupt:
768 vlog('# Handling keyboard interrupt')
771 vlog('# Handling keyboard interrupt')
769 cleanup()
772 cleanup()
770 raise
773 raise
771
774
772 ret = proc.wait()
775 ret = proc.wait()
773 if wifexited(ret):
776 if wifexited(ret):
774 ret = os.WEXITSTATUS(ret)
777 ret = os.WEXITSTATUS(ret)
775
778
776 if proc.timeout:
779 if proc.timeout:
777 ret = 'timeout'
780 ret = 'timeout'
778
781
779 if ret:
782 if ret:
780 killdaemons()
783 killdaemons()
781
784
782 for s, r in replacements:
785 for s, r in replacements:
783 output = re.sub(s, r, output)
786 output = re.sub(s, r, output)
784 return ret, output.splitlines(True)
787 return ret, output.splitlines(True)
785
788
786 def runone(options, test):
789 def runone(options, test):
787 '''tristate output:
790 '''tristate output:
788 None -> skipped
791 None -> skipped
789 True -> passed
792 True -> passed
790 False -> failed'''
793 False -> failed'''
791
794
792 global results, resultslock, iolock
795 global results, resultslock, iolock
793
796
794 testpath = os.path.join(TESTDIR, test)
797 testpath = os.path.join(TESTDIR, test)
795
798
796 def result(l, e):
799 def result(l, e):
797 resultslock.acquire()
800 resultslock.acquire()
798 results[l].append(e)
801 results[l].append(e)
799 resultslock.release()
802 resultslock.release()
800
803
801 def skip(msg):
804 def skip(msg):
802 if not options.verbose:
805 if not options.verbose:
803 result('s', (test, msg))
806 result('s', (test, msg))
804 else:
807 else:
805 iolock.acquire()
808 iolock.acquire()
806 print "\nSkipping %s: %s" % (testpath, msg)
809 print "\nSkipping %s: %s" % (testpath, msg)
807 iolock.release()
810 iolock.release()
808 return None
811 return None
809
812
810 def fail(msg, ret):
813 def fail(msg, ret):
811 if not options.nodiff:
814 if not options.nodiff:
812 iolock.acquire()
815 iolock.acquire()
813 print "\nERROR: %s %s" % (testpath, msg)
816 print "\nERROR: %s %s" % (testpath, msg)
814 iolock.release()
817 iolock.release()
815 if (not ret and options.interactive
818 if (not ret and options.interactive
816 and os.path.exists(testpath + ".err")):
819 and os.path.exists(testpath + ".err")):
817 iolock.acquire()
820 iolock.acquire()
818 print "Accept this change? [n] ",
821 print "Accept this change? [n] ",
819 answer = sys.stdin.readline().strip()
822 answer = sys.stdin.readline().strip()
820 iolock.release()
823 iolock.release()
821 if answer.lower() in "y yes".split():
824 if answer.lower() in "y yes".split():
822 if test.endswith(".t"):
825 if test.endswith(".t"):
823 rename(testpath + ".err", testpath)
826 rename(testpath + ".err", testpath)
824 else:
827 else:
825 rename(testpath + ".err", testpath + ".out")
828 rename(testpath + ".err", testpath + ".out")
826 result('p', test)
829 result('p', test)
827 return
830 return
828 result('f', (test, msg))
831 result('f', (test, msg))
829
832
830 def success():
833 def success():
831 result('p', test)
834 result('p', test)
832
835
833 def ignore(msg):
836 def ignore(msg):
834 result('i', (test, msg))
837 result('i', (test, msg))
835
838
836 if (os.path.basename(test).startswith("test-") and '~' not in test and
839 if (os.path.basename(test).startswith("test-") and '~' not in test and
837 ('.' not in test or test.endswith('.py') or
840 ('.' not in test or test.endswith('.py') or
838 test.endswith('.bat') or test.endswith('.t'))):
841 test.endswith('.bat') or test.endswith('.t'))):
839 if not os.path.exists(test):
842 if not os.path.exists(test):
840 skip("doesn't exist")
843 skip("doesn't exist")
841 return None
844 return None
842 else:
845 else:
843 vlog('# Test file', test, 'not supported, ignoring')
846 vlog('# Test file', test, 'not supported, ignoring')
844 return None # not a supported test, don't record
847 return None # not a supported test, don't record
845
848
846 if not (options.whitelisted and test in options.whitelisted):
849 if not (options.whitelisted and test in options.whitelisted):
847 if options.blacklist and test in options.blacklist:
850 if options.blacklist and test in options.blacklist:
848 skip("blacklisted")
851 skip("blacklisted")
849 return None
852 return None
850
853
851 if options.retest and not os.path.exists(test + ".err"):
854 if options.retest and not os.path.exists(test + ".err"):
852 ignore("not retesting")
855 ignore("not retesting")
853 return None
856 return None
854
857
855 if options.keywords:
858 if options.keywords:
856 fp = open(test)
859 fp = open(test)
857 t = fp.read().lower() + test.lower()
860 t = fp.read().lower() + test.lower()
858 fp.close()
861 fp.close()
859 for k in options.keywords.lower().split():
862 for k in options.keywords.lower().split():
860 if k in t:
863 if k in t:
861 break
864 break
862 else:
865 else:
863 ignore("doesn't match keyword")
866 ignore("doesn't match keyword")
864 return None
867 return None
865
868
866 vlog("# Test", test)
869 vlog("# Test", test)
867
870
868 # create a fresh hgrc
871 # create a fresh hgrc
869 hgrc = open(HGRCPATH, 'w+')
872 hgrc = open(HGRCPATH, 'w+')
870 hgrc.write('[ui]\n')
873 hgrc.write('[ui]\n')
871 hgrc.write('slash = True\n')
874 hgrc.write('slash = True\n')
872 hgrc.write('[defaults]\n')
875 hgrc.write('[defaults]\n')
873 hgrc.write('backout = -d "0 0"\n')
876 hgrc.write('backout = -d "0 0"\n')
874 hgrc.write('commit = -d "0 0"\n')
877 hgrc.write('commit = -d "0 0"\n')
875 hgrc.write('tag = -d "0 0"\n')
878 hgrc.write('tag = -d "0 0"\n')
876 if options.inotify:
879 if options.inotify:
877 hgrc.write('[extensions]\n')
880 hgrc.write('[extensions]\n')
878 hgrc.write('inotify=\n')
881 hgrc.write('inotify=\n')
879 hgrc.write('[inotify]\n')
882 hgrc.write('[inotify]\n')
880 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
883 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
881 hgrc.write('appendpid=True\n')
884 hgrc.write('appendpid=True\n')
882 if options.extra_config_opt:
885 if options.extra_config_opt:
883 for opt in options.extra_config_opt:
886 for opt in options.extra_config_opt:
884 section, key = opt.split('.', 1)
887 section, key = opt.split('.', 1)
885 assert '=' in key, ('extra config opt %s must '
888 assert '=' in key, ('extra config opt %s must '
886 'have an = for assignment' % opt)
889 'have an = for assignment' % opt)
887 hgrc.write('[%s]\n%s\n' % (section, key))
890 hgrc.write('[%s]\n%s\n' % (section, key))
888 hgrc.close()
891 hgrc.close()
889
892
890 ref = os.path.join(TESTDIR, test+".out")
893 ref = os.path.join(TESTDIR, test+".out")
891 err = os.path.join(TESTDIR, test+".err")
894 err = os.path.join(TESTDIR, test+".err")
892 if os.path.exists(err):
895 if os.path.exists(err):
893 os.remove(err) # Remove any previous output files
896 os.remove(err) # Remove any previous output files
894 try:
897 try:
895 tf = open(testpath)
898 tf = open(testpath)
896 firstline = tf.readline().rstrip()
899 firstline = tf.readline().rstrip()
897 tf.close()
900 tf.close()
898 except IOError:
901 except IOError:
899 firstline = ''
902 firstline = ''
900 lctest = test.lower()
903 lctest = test.lower()
901
904
902 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
905 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
903 runner = pytest
906 runner = pytest
904 elif lctest.endswith('.t'):
907 elif lctest.endswith('.t'):
905 runner = tsttest
908 runner = tsttest
906 ref = testpath
909 ref = testpath
907 else:
910 else:
908 return skip("unknown test type")
911 return skip("unknown test type")
909
912
910 # Make a tmp subdirectory to work in
913 # Make a tmp subdirectory to work in
911 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
914 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
912 os.path.join(HGTMP, os.path.basename(test))
915 os.path.join(HGTMP, os.path.basename(test))
913
916
914 replacements = [
917 replacements = [
915 (r':%s\b' % options.port, ':$HGPORT'),
918 (r':%s\b' % options.port, ':$HGPORT'),
916 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
919 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
917 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
920 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
918 ]
921 ]
919 if os.name == 'nt':
922 if os.name == 'nt':
920 replacements.append(
923 replacements.append(
921 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
924 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
922 c in '/\\' and r'[/\\]' or
925 c in '/\\' and r'[/\\]' or
923 c.isdigit() and c or
926 c.isdigit() and c or
924 '\\' + c
927 '\\' + c
925 for c in testtmp), '$TESTTMP'))
928 for c in testtmp), '$TESTTMP'))
926 else:
929 else:
927 replacements.append((re.escape(testtmp), '$TESTTMP'))
930 replacements.append((re.escape(testtmp), '$TESTTMP'))
928
931
929 os.mkdir(testtmp)
932 os.mkdir(testtmp)
930 if options.time:
933 if options.time:
931 starttime = time.time()
934 starttime = time.time()
932 ret, out = runner(testpath, testtmp, options, replacements)
935 ret, out = runner(testpath, testtmp, options, replacements)
933 if options.time:
936 if options.time:
934 endtime = time.time()
937 endtime = time.time()
935 times.append((test, endtime - starttime))
938 times.append((test, endtime - starttime))
936 vlog("# Ret was:", ret)
939 vlog("# Ret was:", ret)
937
940
938 killdaemons()
941 killdaemons()
939
942
940 mark = '.'
943 mark = '.'
941
944
942 skipped = (ret == SKIPPED_STATUS)
945 skipped = (ret == SKIPPED_STATUS)
943
946
944 # If we're not in --debug mode and reference output file exists,
947 # If we're not in --debug mode and reference output file exists,
945 # check test output against it.
948 # check test output against it.
946 if options.debug:
949 if options.debug:
947 refout = None # to match "out is None"
950 refout = None # to match "out is None"
948 elif os.path.exists(ref):
951 elif os.path.exists(ref):
949 f = open(ref, "r")
952 f = open(ref, "r")
950 refout = f.read().splitlines(True)
953 refout = f.read().splitlines(True)
951 f.close()
954 f.close()
952 else:
955 else:
953 refout = []
956 refout = []
954
957
955 if (ret != 0 or out != refout) and not skipped and not options.debug:
958 if (ret != 0 or out != refout) and not skipped and not options.debug:
956 # Save errors to a file for diagnosis
959 # Save errors to a file for diagnosis
957 f = open(err, "wb")
960 f = open(err, "wb")
958 for line in out:
961 for line in out:
959 f.write(line)
962 f.write(line)
960 f.close()
963 f.close()
961
964
962 def describe(ret):
965 def describe(ret):
963 if ret < 0:
966 if ret < 0:
964 return 'killed by signal %d' % -ret
967 return 'killed by signal %d' % -ret
965 return 'returned error code %d' % ret
968 return 'returned error code %d' % ret
966
969
967 if skipped:
970 if skipped:
968 mark = 's'
971 mark = 's'
969 if out is None: # debug mode: nothing to parse
972 if out is None: # debug mode: nothing to parse
970 missing = ['unknown']
973 missing = ['unknown']
971 failed = None
974 failed = None
972 else:
975 else:
973 missing, failed = parsehghaveoutput(out)
976 missing, failed = parsehghaveoutput(out)
974 if not missing:
977 if not missing:
975 missing = ['irrelevant']
978 missing = ['irrelevant']
976 if failed:
979 if failed:
977 fail("hghave failed checking for %s" % failed[-1], ret)
980 fail("hghave failed checking for %s" % failed[-1], ret)
978 skipped = False
981 skipped = False
979 else:
982 else:
980 skip(missing[-1])
983 skip(missing[-1])
981 elif ret == 'timeout':
984 elif ret == 'timeout':
982 mark = 't'
985 mark = 't'
983 fail("timed out", ret)
986 fail("timed out", ret)
984 elif out != refout:
987 elif out != refout:
985 mark = '!'
988 mark = '!'
986 if not options.nodiff:
989 if not options.nodiff:
987 iolock.acquire()
990 iolock.acquire()
988 if options.view:
991 if options.view:
989 os.system("%s %s %s" % (options.view, ref, err))
992 os.system("%s %s %s" % (options.view, ref, err))
990 else:
993 else:
991 showdiff(refout, out, ref, err)
994 showdiff(refout, out, ref, err)
992 iolock.release()
995 iolock.release()
993 if ret:
996 if ret:
994 fail("output changed and " + describe(ret), ret)
997 fail("output changed and " + describe(ret), ret)
995 else:
998 else:
996 fail("output changed", ret)
999 fail("output changed", ret)
997 ret = 1
1000 ret = 1
998 elif ret:
1001 elif ret:
999 mark = '!'
1002 mark = '!'
1000 fail(describe(ret), ret)
1003 fail(describe(ret), ret)
1001 else:
1004 else:
1002 success()
1005 success()
1003
1006
1004 if not options.verbose:
1007 if not options.verbose:
1005 iolock.acquire()
1008 iolock.acquire()
1006 sys.stdout.write(mark)
1009 sys.stdout.write(mark)
1007 sys.stdout.flush()
1010 sys.stdout.flush()
1008 iolock.release()
1011 iolock.release()
1009
1012
1010 if not options.keep_tmpdir:
1013 if not options.keep_tmpdir:
1011 shutil.rmtree(testtmp, True)
1014 shutil.rmtree(testtmp, True)
1012 if skipped:
1015 if skipped:
1013 return None
1016 return None
1014 return ret == 0
1017 return ret == 0
1015
1018
1016 _hgpath = None
1019 _hgpath = None
1017
1020
1018 def _gethgpath():
1021 def _gethgpath():
1019 """Return the path to the mercurial package that is actually found by
1022 """Return the path to the mercurial package that is actually found by
1020 the current Python interpreter."""
1023 the current Python interpreter."""
1021 global _hgpath
1024 global _hgpath
1022 if _hgpath is not None:
1025 if _hgpath is not None:
1023 return _hgpath
1026 return _hgpath
1024
1027
1025 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
1028 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
1026 pipe = os.popen(cmd % PYTHON)
1029 pipe = os.popen(cmd % PYTHON)
1027 try:
1030 try:
1028 _hgpath = pipe.read().strip()
1031 _hgpath = pipe.read().strip()
1029 finally:
1032 finally:
1030 pipe.close()
1033 pipe.close()
1031 return _hgpath
1034 return _hgpath
1032
1035
1033 def _checkhglib(verb):
1036 def _checkhglib(verb):
1034 """Ensure that the 'mercurial' package imported by python is
1037 """Ensure that the 'mercurial' package imported by python is
1035 the one we expect it to be. If not, print a warning to stderr."""
1038 the one we expect it to be. If not, print a warning to stderr."""
1036 expecthg = os.path.join(PYTHONDIR, 'mercurial')
1039 expecthg = os.path.join(PYTHONDIR, 'mercurial')
1037 actualhg = _gethgpath()
1040 actualhg = _gethgpath()
1038 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
1041 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
1039 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
1042 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
1040 ' (expected %s)\n'
1043 ' (expected %s)\n'
1041 % (verb, actualhg, expecthg))
1044 % (verb, actualhg, expecthg))
1042
1045
1043 def runchildren(options, tests):
1046 def runchildren(options, tests):
1044 if INST:
1047 if INST:
1045 installhg(options)
1048 installhg(options)
1046 _checkhglib("Testing")
1049 _checkhglib("Testing")
1047 else:
1050 else:
1048 usecorrectpython()
1051 usecorrectpython()
1049
1052
1050 optcopy = dict(options.__dict__)
1053 optcopy = dict(options.__dict__)
1051 optcopy['jobs'] = 1
1054 optcopy['jobs'] = 1
1052
1055
1053 # Because whitelist has to override keyword matches, we have to
1056 # Because whitelist has to override keyword matches, we have to
1054 # actually load the whitelist in the children as well, so we allow
1057 # actually load the whitelist in the children as well, so we allow
1055 # the list of whitelist files to pass through and be parsed in the
1058 # the list of whitelist files to pass through and be parsed in the
1056 # children, but not the dict of whitelisted tests resulting from
1059 # children, but not the dict of whitelisted tests resulting from
1057 # the parse, used here to override blacklisted tests.
1060 # the parse, used here to override blacklisted tests.
1058 whitelist = optcopy['whitelisted'] or []
1061 whitelist = optcopy['whitelisted'] or []
1059 del optcopy['whitelisted']
1062 del optcopy['whitelisted']
1060
1063
1061 blacklist = optcopy['blacklist'] or []
1064 blacklist = optcopy['blacklist'] or []
1062 del optcopy['blacklist']
1065 del optcopy['blacklist']
1063 blacklisted = []
1066 blacklisted = []
1064
1067
1065 if optcopy['with_hg'] is None:
1068 if optcopy['with_hg'] is None:
1066 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
1069 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
1067 optcopy.pop('anycoverage', None)
1070 optcopy.pop('anycoverage', None)
1068
1071
1069 opts = []
1072 opts = []
1070 for opt, value in optcopy.iteritems():
1073 for opt, value in optcopy.iteritems():
1071 name = '--' + opt.replace('_', '-')
1074 name = '--' + opt.replace('_', '-')
1072 if value is True:
1075 if value is True:
1073 opts.append(name)
1076 opts.append(name)
1074 elif isinstance(value, list):
1077 elif isinstance(value, list):
1075 for v in value:
1078 for v in value:
1076 opts.append(name + '=' + str(v))
1079 opts.append(name + '=' + str(v))
1077 elif value is not None:
1080 elif value is not None:
1078 opts.append(name + '=' + str(value))
1081 opts.append(name + '=' + str(value))
1079
1082
1080 tests.reverse()
1083 tests.reverse()
1081 jobs = [[] for j in xrange(options.jobs)]
1084 jobs = [[] for j in xrange(options.jobs)]
1082 while tests:
1085 while tests:
1083 for job in jobs:
1086 for job in jobs:
1084 if not tests:
1087 if not tests:
1085 break
1088 break
1086 test = tests.pop()
1089 test = tests.pop()
1087 if test not in whitelist and test in blacklist:
1090 if test not in whitelist and test in blacklist:
1088 blacklisted.append(test)
1091 blacklisted.append(test)
1089 else:
1092 else:
1090 job.append(test)
1093 job.append(test)
1091
1094
1092 waitq = queue.Queue()
1095 waitq = queue.Queue()
1093
1096
1094 # windows lacks os.wait, so we must emulate it
1097 # windows lacks os.wait, so we must emulate it
1095 def waitfor(proc, rfd):
1098 def waitfor(proc, rfd):
1096 fp = os.fdopen(rfd, 'rb')
1099 fp = os.fdopen(rfd, 'rb')
1097 return lambda: waitq.put((proc.pid, proc.wait(), fp))
1100 return lambda: waitq.put((proc.pid, proc.wait(), fp))
1098
1101
1099 for j, job in enumerate(jobs):
1102 for j, job in enumerate(jobs):
1100 if not job:
1103 if not job:
1101 continue
1104 continue
1102 rfd, wfd = os.pipe()
1105 rfd, wfd = os.pipe()
1103 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
1106 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
1104 childtmp = os.path.join(HGTMP, 'child%d' % j)
1107 childtmp = os.path.join(HGTMP, 'child%d' % j)
1105 childopts += ['--tmpdir', childtmp]
1108 childopts += ['--tmpdir', childtmp]
1106 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
1109 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
1107 vlog(' '.join(cmdline))
1110 vlog(' '.join(cmdline))
1108 proc = subprocess.Popen(cmdline, executable=cmdline[0])
1111 proc = subprocess.Popen(cmdline, executable=cmdline[0])
1109 threading.Thread(target=waitfor(proc, rfd)).start()
1112 threading.Thread(target=waitfor(proc, rfd)).start()
1110 os.close(wfd)
1113 os.close(wfd)
1111 signal.signal(signal.SIGINT, signal.SIG_IGN)
1114 signal.signal(signal.SIGINT, signal.SIG_IGN)
1112 failures = 0
1115 failures = 0
1113 passed, skipped, failed = 0, 0, 0
1116 passed, skipped, failed = 0, 0, 0
1114 skips = []
1117 skips = []
1115 fails = []
1118 fails = []
1116 for job in jobs:
1119 for job in jobs:
1117 if not job:
1120 if not job:
1118 continue
1121 continue
1119 pid, status, fp = waitq.get()
1122 pid, status, fp = waitq.get()
1120 try:
1123 try:
1121 childresults = pickle.load(fp)
1124 childresults = pickle.load(fp)
1122 except pickle.UnpicklingError:
1125 except pickle.UnpicklingError:
1123 pass
1126 pass
1124 else:
1127 else:
1125 passed += len(childresults['p'])
1128 passed += len(childresults['p'])
1126 skipped += len(childresults['s'])
1129 skipped += len(childresults['s'])
1127 failed += len(childresults['f'])
1130 failed += len(childresults['f'])
1128 skips.extend(childresults['s'])
1131 skips.extend(childresults['s'])
1129 fails.extend(childresults['f'])
1132 fails.extend(childresults['f'])
1130 if options.time:
1133 if options.time:
1131 childtimes = pickle.load(fp)
1134 childtimes = pickle.load(fp)
1132 times.extend(childtimes)
1135 times.extend(childtimes)
1133
1136
1134 vlog('pid %d exited, status %d' % (pid, status))
1137 vlog('pid %d exited, status %d' % (pid, status))
1135 failures |= status
1138 failures |= status
1136 print
1139 print
1137 skipped += len(blacklisted)
1140 skipped += len(blacklisted)
1138 if not options.noskips:
1141 if not options.noskips:
1139 for s in skips:
1142 for s in skips:
1140 print "Skipped %s: %s" % (s[0], s[1])
1143 print "Skipped %s: %s" % (s[0], s[1])
1141 for s in blacklisted:
1144 for s in blacklisted:
1142 print "Skipped %s: blacklisted" % s
1145 print "Skipped %s: blacklisted" % s
1143 for s in fails:
1146 for s in fails:
1144 print "Failed %s: %s" % (s[0], s[1])
1147 print "Failed %s: %s" % (s[0], s[1])
1145
1148
1146 _checkhglib("Tested")
1149 _checkhglib("Tested")
1147 print "# Ran %d tests, %d skipped, %d failed." % (
1150 print "# Ran %d tests, %d skipped, %d failed." % (
1148 passed + failed, skipped, failed)
1151 passed + failed, skipped, failed)
1149
1152
1150 if options.time:
1153 if options.time:
1151 outputtimes(options)
1154 outputtimes(options)
1152 if options.anycoverage:
1155 if options.anycoverage:
1153 outputcoverage(options)
1156 outputcoverage(options)
1154 sys.exit(failures != 0)
1157 sys.exit(failures != 0)
1155
1158
1156 results = dict(p=[], f=[], s=[], i=[])
1159 results = dict(p=[], f=[], s=[], i=[])
1157 resultslock = threading.Lock()
1160 resultslock = threading.Lock()
1158 times = []
1161 times = []
1159 iolock = threading.Lock()
1162 iolock = threading.Lock()
1160
1163
1161 def runqueue(options, tests):
1164 def runqueue(options, tests):
1162 for test in tests:
1165 for test in tests:
1163 ret = runone(options, test)
1166 ret = runone(options, test)
1164 if options.first and ret is not None and not ret:
1167 if options.first and ret is not None and not ret:
1165 break
1168 break
1166
1169
1167 def runtests(options, tests):
1170 def runtests(options, tests):
1168 global DAEMON_PIDS, HGRCPATH
1171 global DAEMON_PIDS, HGRCPATH
1169 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1172 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1170 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1173 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1171
1174
1172 try:
1175 try:
1173 if INST:
1176 if INST:
1174 installhg(options)
1177 installhg(options)
1175 _checkhglib("Testing")
1178 _checkhglib("Testing")
1176 else:
1179 else:
1177 usecorrectpython()
1180 usecorrectpython()
1178
1181
1179 if options.restart:
1182 if options.restart:
1180 orig = list(tests)
1183 orig = list(tests)
1181 while tests:
1184 while tests:
1182 if os.path.exists(tests[0] + ".err"):
1185 if os.path.exists(tests[0] + ".err"):
1183 break
1186 break
1184 tests.pop(0)
1187 tests.pop(0)
1185 if not tests:
1188 if not tests:
1186 print "running all tests"
1189 print "running all tests"
1187 tests = orig
1190 tests = orig
1188
1191
1189 runqueue(options, tests)
1192 runqueue(options, tests)
1190
1193
1191 failed = len(results['f'])
1194 failed = len(results['f'])
1192 tested = len(results['p']) + failed
1195 tested = len(results['p']) + failed
1193 skipped = len(results['s'])
1196 skipped = len(results['s'])
1194 ignored = len(results['i'])
1197 ignored = len(results['i'])
1195
1198
1196 if options.child:
1199 if options.child:
1197 fp = os.fdopen(options.child, 'wb')
1200 fp = os.fdopen(options.child, 'wb')
1198 pickle.dump(results, fp, pickle.HIGHEST_PROTOCOL)
1201 pickle.dump(results, fp, pickle.HIGHEST_PROTOCOL)
1199 if options.time:
1202 if options.time:
1200 pickle.dump(times, fp, pickle.HIGHEST_PROTOCOL)
1203 pickle.dump(times, fp, pickle.HIGHEST_PROTOCOL)
1201 fp.close()
1204 fp.close()
1202 else:
1205 else:
1203 print
1206 print
1204 for s in results['s']:
1207 for s in results['s']:
1205 print "Skipped %s: %s" % s
1208 print "Skipped %s: %s" % s
1206 for s in results['f']:
1209 for s in results['f']:
1207 print "Failed %s: %s" % s
1210 print "Failed %s: %s" % s
1208 _checkhglib("Tested")
1211 _checkhglib("Tested")
1209 print "# Ran %d tests, %d skipped, %d failed." % (
1212 print "# Ran %d tests, %d skipped, %d failed." % (
1210 tested, skipped + ignored, failed)
1213 tested, skipped + ignored, failed)
1211 if options.time:
1214 if options.time:
1212 outputtimes(options)
1215 outputtimes(options)
1213
1216
1214 if options.anycoverage:
1217 if options.anycoverage:
1215 outputcoverage(options)
1218 outputcoverage(options)
1216 except KeyboardInterrupt:
1219 except KeyboardInterrupt:
1217 failed = True
1220 failed = True
1218 print "\ninterrupted!"
1221 print "\ninterrupted!"
1219
1222
1220 if failed:
1223 if failed:
1221 sys.exit(1)
1224 sys.exit(1)
1222
1225
1223 def main():
1226 def main():
1224 (options, args) = parseargs()
1227 (options, args) = parseargs()
1225 if not options.child:
1228 if not options.child:
1226 os.umask(022)
1229 os.umask(022)
1227
1230
1228 checktools()
1231 checktools()
1229
1232
1230 if len(args) == 0:
1233 if len(args) == 0:
1231 args = os.listdir(".")
1234 args = os.listdir(".")
1232 args.sort()
1235 args.sort()
1233
1236
1234 tests = args
1237 tests = args
1235
1238
1236 # Reset some environment variables to well-known values so that
1239 # Reset some environment variables to well-known values so that
1237 # the tests produce repeatable output.
1240 # the tests produce repeatable output.
1238 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1241 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1239 os.environ['TZ'] = 'GMT'
1242 os.environ['TZ'] = 'GMT'
1240 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1243 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1241 os.environ['CDPATH'] = ''
1244 os.environ['CDPATH'] = ''
1242 os.environ['COLUMNS'] = '80'
1245 os.environ['COLUMNS'] = '80'
1243 os.environ['GREP_OPTIONS'] = ''
1246 os.environ['GREP_OPTIONS'] = ''
1244 os.environ['http_proxy'] = ''
1247 os.environ['http_proxy'] = ''
1245 os.environ['no_proxy'] = ''
1248 os.environ['no_proxy'] = ''
1246 os.environ['NO_PROXY'] = ''
1249 os.environ['NO_PROXY'] = ''
1247 os.environ['TERM'] = 'xterm'
1250 os.environ['TERM'] = 'xterm'
1248
1251
1249 # unset env related to hooks
1252 # unset env related to hooks
1250 for k in os.environ.keys():
1253 for k in os.environ.keys():
1251 if k.startswith('HG_'):
1254 if k.startswith('HG_'):
1252 # can't remove on solaris
1255 # can't remove on solaris
1253 os.environ[k] = ''
1256 os.environ[k] = ''
1254 del os.environ[k]
1257 del os.environ[k]
1255 if 'HG' in os.environ:
1258 if 'HG' in os.environ:
1256 # can't remove on solaris
1259 # can't remove on solaris
1257 os.environ['HG'] = ''
1260 os.environ['HG'] = ''
1258 del os.environ['HG']
1261 del os.environ['HG']
1259
1262
1260 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1263 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1261 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1264 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1262 if options.tmpdir:
1265 if options.tmpdir:
1263 options.keep_tmpdir = True
1266 options.keep_tmpdir = True
1264 tmpdir = options.tmpdir
1267 tmpdir = options.tmpdir
1265 if os.path.exists(tmpdir):
1268 if os.path.exists(tmpdir):
1266 # Meaning of tmpdir has changed since 1.3: we used to create
1269 # Meaning of tmpdir has changed since 1.3: we used to create
1267 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1270 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1268 # tmpdir already exists.
1271 # tmpdir already exists.
1269 sys.exit("error: temp dir %r already exists" % tmpdir)
1272 sys.exit("error: temp dir %r already exists" % tmpdir)
1270
1273
1271 # Automatically removing tmpdir sounds convenient, but could
1274 # Automatically removing tmpdir sounds convenient, but could
1272 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1275 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1273 # or "--tmpdir=$HOME".
1276 # or "--tmpdir=$HOME".
1274 #vlog("# Removing temp dir", tmpdir)
1277 #vlog("# Removing temp dir", tmpdir)
1275 #shutil.rmtree(tmpdir)
1278 #shutil.rmtree(tmpdir)
1276 os.makedirs(tmpdir)
1279 os.makedirs(tmpdir)
1277 else:
1280 else:
1278 d = None
1281 d = None
1279 if os.name == 'nt':
1282 if os.name == 'nt':
1280 # without this, we get the default temp dir location, but
1283 # without this, we get the default temp dir location, but
1281 # in all lowercase, which causes troubles with paths (issue3490)
1284 # in all lowercase, which causes troubles with paths (issue3490)
1282 d = os.getenv('TMP')
1285 d = os.getenv('TMP')
1283 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1286 tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
1284 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1287 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1285 DAEMON_PIDS = None
1288 DAEMON_PIDS = None
1286 HGRCPATH = None
1289 HGRCPATH = None
1287
1290
1288 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1291 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1289 os.environ["HGMERGE"] = "internal:merge"
1292 os.environ["HGMERGE"] = "internal:merge"
1290 os.environ["HGUSER"] = "test"
1293 os.environ["HGUSER"] = "test"
1291 os.environ["HGENCODING"] = "ascii"
1294 os.environ["HGENCODING"] = "ascii"
1292 os.environ["HGENCODINGMODE"] = "strict"
1295 os.environ["HGENCODINGMODE"] = "strict"
1293 os.environ["HGPORT"] = str(options.port)
1296 os.environ["HGPORT"] = str(options.port)
1294 os.environ["HGPORT1"] = str(options.port + 1)
1297 os.environ["HGPORT1"] = str(options.port + 1)
1295 os.environ["HGPORT2"] = str(options.port + 2)
1298 os.environ["HGPORT2"] = str(options.port + 2)
1296
1299
1297 if options.with_hg:
1300 if options.with_hg:
1298 INST = None
1301 INST = None
1299 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1302 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1300
1303
1301 # This looks redundant with how Python initializes sys.path from
1304 # This looks redundant with how Python initializes sys.path from
1302 # the location of the script being executed. Needed because the
1305 # the location of the script being executed. Needed because the
1303 # "hg" specified by --with-hg is not the only Python script
1306 # "hg" specified by --with-hg is not the only Python script
1304 # executed in the test suite that needs to import 'mercurial'
1307 # executed in the test suite that needs to import 'mercurial'
1305 # ... which means it's not really redundant at all.
1308 # ... which means it's not really redundant at all.
1306 PYTHONDIR = BINDIR
1309 PYTHONDIR = BINDIR
1307 else:
1310 else:
1308 INST = os.path.join(HGTMP, "install")
1311 INST = os.path.join(HGTMP, "install")
1309 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1312 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1310 PYTHONDIR = os.path.join(INST, "lib", "python")
1313 PYTHONDIR = os.path.join(INST, "lib", "python")
1311
1314
1312 os.environ["BINDIR"] = BINDIR
1315 os.environ["BINDIR"] = BINDIR
1313 os.environ["PYTHON"] = PYTHON
1316 os.environ["PYTHON"] = PYTHON
1314
1317
1315 if not options.child:
1318 if not options.child:
1316 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1319 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1317 os.environ["PATH"] = os.pathsep.join(path)
1320 os.environ["PATH"] = os.pathsep.join(path)
1318
1321
1319 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1322 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1320 # can run .../tests/run-tests.py test-foo where test-foo
1323 # can run .../tests/run-tests.py test-foo where test-foo
1321 # adds an extension to HGRC
1324 # adds an extension to HGRC
1322 pypath = [PYTHONDIR, TESTDIR]
1325 pypath = [PYTHONDIR, TESTDIR]
1323 # We have to augment PYTHONPATH, rather than simply replacing
1326 # We have to augment PYTHONPATH, rather than simply replacing
1324 # it, in case external libraries are only available via current
1327 # it, in case external libraries are only available via current
1325 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1328 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1326 # are in /opt/subversion.)
1329 # are in /opt/subversion.)
1327 oldpypath = os.environ.get(IMPL_PATH)
1330 oldpypath = os.environ.get(IMPL_PATH)
1328 if oldpypath:
1331 if oldpypath:
1329 pypath.append(oldpypath)
1332 pypath.append(oldpypath)
1330 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1333 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1331
1334
1332 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1335 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1333
1336
1334 vlog("# Using TESTDIR", TESTDIR)
1337 vlog("# Using TESTDIR", TESTDIR)
1335 vlog("# Using HGTMP", HGTMP)
1338 vlog("# Using HGTMP", HGTMP)
1336 vlog("# Using PATH", os.environ["PATH"])
1339 vlog("# Using PATH", os.environ["PATH"])
1337 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1340 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1338
1341
1339 try:
1342 try:
1340 if len(tests) > 1 and options.jobs > 1:
1343 if len(tests) > 1 and options.jobs > 1:
1341 runchildren(options, tests)
1344 runchildren(options, tests)
1342 else:
1345 else:
1343 runtests(options, tests)
1346 runtests(options, tests)
1344 finally:
1347 finally:
1345 time.sleep(.1)
1348 time.sleep(.1)
1346 cleanup(options)
1349 cleanup(options)
1347
1350
1348 if __name__ == '__main__':
1351 if __name__ == '__main__':
1349 main()
1352 main()
General Comments 0
You need to be logged in to leave comments. Login now