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