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