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