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