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