##// END OF EJS Templates
run-tests: fix hg.bat python reference...
Patrick Mezard -
r14336:0cbf0d1f default
parent child Browse files
Show More
@@ -1,1167 +1,1183 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.name == 'nt' or 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 in ('python', 'python.exe'):
369 if exename in ('python', 'python.exe'):
370 path = findprogram(exename)
370 path = findprogram(exename)
371 if os.path.dirname(path) == exedir:
371 if os.path.dirname(path) == exedir:
372 return
372 return
373 else:
373 else:
374 exename = 'python'
374 exename = 'python'
375 vlog('# Making python executable in test path use correct Python')
375 vlog('# Making python executable in test path use correct Python')
376 mypython = os.path.join(BINDIR, exename)
376 mypython = os.path.join(BINDIR, exename)
377 try:
377 try:
378 os.symlink(sys.executable, mypython)
378 os.symlink(sys.executable, mypython)
379 except AttributeError:
379 except AttributeError:
380 # windows fallback
380 # windows fallback
381 shutil.copyfile(sys.executable, mypython)
381 shutil.copyfile(sys.executable, mypython)
382 shutil.copymode(sys.executable, mypython)
382 shutil.copymode(sys.executable, mypython)
383
383
384 def installhg(options):
384 def installhg(options):
385 vlog("# Performing temporary installation of HG")
385 vlog("# Performing temporary installation of HG")
386 installerrs = os.path.join("tests", "install.err")
386 installerrs = os.path.join("tests", "install.err")
387 pure = options.pure and "--pure" or ""
387 pure = options.pure and "--pure" or ""
388
388
389 # Run installer in hg root
389 # Run installer in hg root
390 script = os.path.realpath(sys.argv[0])
390 script = os.path.realpath(sys.argv[0])
391 hgroot = os.path.dirname(os.path.dirname(script))
391 hgroot = os.path.dirname(os.path.dirname(script))
392 os.chdir(hgroot)
392 os.chdir(hgroot)
393 nohome = '--home=""'
393 nohome = '--home=""'
394 if os.name == 'nt':
394 if os.name == 'nt':
395 # The --home="" trick works only on OS where os.sep == '/'
395 # The --home="" trick works only on OS where os.sep == '/'
396 # because of a distutils convert_path() fast-path. Avoid it at
396 # because of a distutils convert_path() fast-path. Avoid it at
397 # least on Windows for now, deal with .pydistutils.cfg bugs
397 # least on Windows for now, deal with .pydistutils.cfg bugs
398 # when they happen.
398 # when they happen.
399 nohome = ''
399 nohome = ''
400 cmd = ('%s setup.py %s clean --all'
400 cmd = ('%s setup.py %s clean --all'
401 ' build --build-base="%s"'
401 ' build --build-base="%s"'
402 ' install --force --prefix="%s" --install-lib="%s"'
402 ' install --force --prefix="%s" --install-lib="%s"'
403 ' --install-scripts="%s" %s >%s 2>&1'
403 ' --install-scripts="%s" %s >%s 2>&1'
404 % (sys.executable, pure, os.path.join(HGTMP, "build"),
404 % (sys.executable, pure, os.path.join(HGTMP, "build"),
405 INST, PYTHONDIR, BINDIR, nohome, installerrs))
405 INST, PYTHONDIR, BINDIR, nohome, installerrs))
406 vlog("# Running", cmd)
406 vlog("# Running", cmd)
407 if os.system(cmd) == 0:
407 if os.system(cmd) == 0:
408 if not options.verbose:
408 if not options.verbose:
409 os.remove(installerrs)
409 os.remove(installerrs)
410 else:
410 else:
411 f = open(installerrs)
411 f = open(installerrs)
412 for line in f:
412 for line in f:
413 print line,
413 print line,
414 f.close()
414 f.close()
415 sys.exit(1)
415 sys.exit(1)
416 os.chdir(TESTDIR)
416 os.chdir(TESTDIR)
417
417
418 usecorrectpython()
418 usecorrectpython()
419
419
420 vlog("# Installing dummy diffstat")
420 vlog("# Installing dummy diffstat")
421 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
421 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
422 f.write('#!' + sys.executable + '\n'
422 f.write('#!' + sys.executable + '\n'
423 'import sys\n'
423 'import sys\n'
424 'files = 0\n'
424 'files = 0\n'
425 'for line in sys.stdin:\n'
425 'for line in sys.stdin:\n'
426 ' if line.startswith("diff "):\n'
426 ' if line.startswith("diff "):\n'
427 ' files += 1\n'
427 ' files += 1\n'
428 'sys.stdout.write("files patched: %d\\n" % files)\n')
428 'sys.stdout.write("files patched: %d\\n" % files)\n')
429 f.close()
429 f.close()
430 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
430 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
431
431
432 if options.py3k_warnings and not options.anycoverage:
432 if options.py3k_warnings and not options.anycoverage:
433 vlog("# Updating hg command to enable Py3k Warnings switch")
433 vlog("# Updating hg command to enable Py3k Warnings switch")
434 f = open(os.path.join(BINDIR, 'hg'), 'r')
434 f = open(os.path.join(BINDIR, 'hg'), 'r')
435 lines = [line.rstrip() for line in f]
435 lines = [line.rstrip() for line in f]
436 lines[0] += ' -3'
436 lines[0] += ' -3'
437 f.close()
437 f.close()
438 f = open(os.path.join(BINDIR, 'hg'), 'w')
438 f = open(os.path.join(BINDIR, 'hg'), 'w')
439 for line in lines:
439 for line in lines:
440 f.write(line + '\n')
440 f.write(line + '\n')
441 f.close()
441 f.close()
442
442
443 hgbat = os.path.join(BINDIR, 'hg.bat')
444 if os.path.isfile(hgbat):
445 # hg.bat expects to be put in bin/scripts while run-tests.py
446 # installation layout put it in bin/ directly. Fix it
447 f = open(hgbat, 'rb')
448 data = f.read()
449 f.close()
450 if '"%~dp0..\python" "%~dp0hg" %*' in data:
451 data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
452 '"%~dp0python" "%~dp0hg" %*')
453 f = open(hgbat, 'wb')
454 f.write(data)
455 f.close()
456 else:
457 print 'WARNING: cannot fix hg.bat reference to python.exe'
458
443 if options.anycoverage:
459 if options.anycoverage:
444 custom = os.path.join(TESTDIR, 'sitecustomize.py')
460 custom = os.path.join(TESTDIR, 'sitecustomize.py')
445 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
461 target = os.path.join(PYTHONDIR, 'sitecustomize.py')
446 vlog('# Installing coverage trigger to %s' % target)
462 vlog('# Installing coverage trigger to %s' % target)
447 shutil.copyfile(custom, target)
463 shutil.copyfile(custom, target)
448 rc = os.path.join(TESTDIR, '.coveragerc')
464 rc = os.path.join(TESTDIR, '.coveragerc')
449 vlog('# Installing coverage rc to %s' % rc)
465 vlog('# Installing coverage rc to %s' % rc)
450 os.environ['COVERAGE_PROCESS_START'] = rc
466 os.environ['COVERAGE_PROCESS_START'] = rc
451 fn = os.path.join(INST, '..', '.coverage')
467 fn = os.path.join(INST, '..', '.coverage')
452 os.environ['COVERAGE_FILE'] = fn
468 os.environ['COVERAGE_FILE'] = fn
453
469
454 def outputcoverage(options):
470 def outputcoverage(options):
455
471
456 vlog('# Producing coverage report')
472 vlog('# Producing coverage report')
457 os.chdir(PYTHONDIR)
473 os.chdir(PYTHONDIR)
458
474
459 def covrun(*args):
475 def covrun(*args):
460 cmd = 'coverage %s' % ' '.join(args)
476 cmd = 'coverage %s' % ' '.join(args)
461 vlog('# Running: %s' % cmd)
477 vlog('# Running: %s' % cmd)
462 os.system(cmd)
478 os.system(cmd)
463
479
464 if options.child:
480 if options.child:
465 return
481 return
466
482
467 covrun('-c')
483 covrun('-c')
468 omit = ','.join([BINDIR, TESTDIR])
484 omit = ','.join([BINDIR, TESTDIR])
469 covrun('-i', '-r', '"--omit=%s"' % omit) # report
485 covrun('-i', '-r', '"--omit=%s"' % omit) # report
470 if options.annotate:
486 if options.annotate:
471 adir = os.path.join(TESTDIR, 'annotated')
487 adir = os.path.join(TESTDIR, 'annotated')
472 if not os.path.isdir(adir):
488 if not os.path.isdir(adir):
473 os.mkdir(adir)
489 os.mkdir(adir)
474 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
490 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
475
491
476 def pytest(test, wd, options, replacements):
492 def pytest(test, wd, options, replacements):
477 py3kswitch = options.py3k_warnings and ' -3' or ''
493 py3kswitch = options.py3k_warnings and ' -3' or ''
478 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
494 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
479 vlog("# Running", cmd)
495 vlog("# Running", cmd)
480 return run(cmd, wd, options, replacements)
496 return run(cmd, wd, options, replacements)
481
497
482 def shtest(test, wd, options, replacements):
498 def shtest(test, wd, options, replacements):
483 cmd = '"%s"' % test
499 cmd = '"%s"' % test
484 vlog("# Running", cmd)
500 vlog("# Running", cmd)
485 return run(cmd, wd, options, replacements)
501 return run(cmd, wd, options, replacements)
486
502
487 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
503 needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
488 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
504 escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
489 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
505 escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
490 escapemap.update({'\\': '\\\\', '\r': r'\r'})
506 escapemap.update({'\\': '\\\\', '\r': r'\r'})
491 def escapef(m):
507 def escapef(m):
492 return escapemap[m.group(0)]
508 return escapemap[m.group(0)]
493 def stringescape(s):
509 def stringescape(s):
494 return escapesub(escapef, s)
510 return escapesub(escapef, s)
495
511
496 def tsttest(test, wd, options, replacements):
512 def tsttest(test, wd, options, replacements):
497 t = open(test)
513 t = open(test)
498 out = []
514 out = []
499 script = []
515 script = []
500 salt = "SALT" + str(time.time())
516 salt = "SALT" + str(time.time())
501
517
502 pos = prepos = -1
518 pos = prepos = -1
503 after = {}
519 after = {}
504 expected = {}
520 expected = {}
505 for n, l in enumerate(t):
521 for n, l in enumerate(t):
506 if not l.endswith('\n'):
522 if not l.endswith('\n'):
507 l += '\n'
523 l += '\n'
508 if l.startswith(' $ '): # commands
524 if l.startswith(' $ '): # commands
509 after.setdefault(pos, []).append(l)
525 after.setdefault(pos, []).append(l)
510 prepos = pos
526 prepos = pos
511 pos = n
527 pos = n
512 script.append('echo %s %s $?\n' % (salt, n))
528 script.append('echo %s %s $?\n' % (salt, n))
513 script.append(l[4:])
529 script.append(l[4:])
514 elif l.startswith(' > '): # continuations
530 elif l.startswith(' > '): # continuations
515 after.setdefault(prepos, []).append(l)
531 after.setdefault(prepos, []).append(l)
516 script.append(l[4:])
532 script.append(l[4:])
517 elif l.startswith(' '): # results
533 elif l.startswith(' '): # results
518 # queue up a list of expected results
534 # queue up a list of expected results
519 expected.setdefault(pos, []).append(l[2:])
535 expected.setdefault(pos, []).append(l[2:])
520 else:
536 else:
521 # non-command/result - queue up for merged output
537 # non-command/result - queue up for merged output
522 after.setdefault(pos, []).append(l)
538 after.setdefault(pos, []).append(l)
523
539
524 t.close()
540 t.close()
525
541
526 script.append('echo %s %s $?\n' % (salt, n + 1))
542 script.append('echo %s %s $?\n' % (salt, n + 1))
527
543
528 fd, name = tempfile.mkstemp(suffix='hg-tst')
544 fd, name = tempfile.mkstemp(suffix='hg-tst')
529
545
530 try:
546 try:
531 for l in script:
547 for l in script:
532 os.write(fd, l)
548 os.write(fd, l)
533 os.close(fd)
549 os.close(fd)
534
550
535 cmd = '"%s" "%s"' % (options.shell, name)
551 cmd = '"%s" "%s"' % (options.shell, name)
536 vlog("# Running", cmd)
552 vlog("# Running", cmd)
537 exitcode, output = run(cmd, wd, options, replacements)
553 exitcode, output = run(cmd, wd, options, replacements)
538 # do not merge output if skipped, return hghave message instead
554 # do not merge output if skipped, return hghave message instead
539 # similarly, with --debug, output is None
555 # similarly, with --debug, output is None
540 if exitcode == SKIPPED_STATUS or output is None:
556 if exitcode == SKIPPED_STATUS or output is None:
541 return exitcode, output
557 return exitcode, output
542 finally:
558 finally:
543 os.remove(name)
559 os.remove(name)
544
560
545 def rematch(el, l):
561 def rematch(el, l):
546 try:
562 try:
547 # ensure that the regex matches to the end of the string
563 # ensure that the regex matches to the end of the string
548 return re.match(el + r'\Z', l)
564 return re.match(el + r'\Z', l)
549 except re.error:
565 except re.error:
550 # el is an invalid regex
566 # el is an invalid regex
551 return False
567 return False
552
568
553 def globmatch(el, l):
569 def globmatch(el, l):
554 # The only supported special characters are * and ?. Escaping is
570 # The only supported special characters are * and ?. Escaping is
555 # supported.
571 # supported.
556 i, n = 0, len(el)
572 i, n = 0, len(el)
557 res = ''
573 res = ''
558 while i < n:
574 while i < n:
559 c = el[i]
575 c = el[i]
560 i += 1
576 i += 1
561 if c == '\\' and el[i] in '*?\\':
577 if c == '\\' and el[i] in '*?\\':
562 res += el[i - 1:i + 1]
578 res += el[i - 1:i + 1]
563 i += 1
579 i += 1
564 elif c == '*':
580 elif c == '*':
565 res += '.*'
581 res += '.*'
566 elif c == '?':
582 elif c == '?':
567 res += '.'
583 res += '.'
568 else:
584 else:
569 res += re.escape(c)
585 res += re.escape(c)
570 return rematch(res, l)
586 return rematch(res, l)
571
587
572 pos = -1
588 pos = -1
573 postout = []
589 postout = []
574 ret = 0
590 ret = 0
575 for n, l in enumerate(output):
591 for n, l in enumerate(output):
576 lout, lcmd = l, None
592 lout, lcmd = l, None
577 if salt in l:
593 if salt in l:
578 lout, lcmd = l.split(salt, 1)
594 lout, lcmd = l.split(salt, 1)
579
595
580 if lout:
596 if lout:
581 if lcmd:
597 if lcmd:
582 lout += ' (no-eol)\n'
598 lout += ' (no-eol)\n'
583
599
584 el = None
600 el = None
585 if pos in expected and expected[pos]:
601 if pos in expected and expected[pos]:
586 el = expected[pos].pop(0)
602 el = expected[pos].pop(0)
587
603
588 if el == lout: # perfect match (fast)
604 if el == lout: # perfect match (fast)
589 postout.append(" " + lout)
605 postout.append(" " + lout)
590 elif (el and
606 elif (el and
591 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', lout) or
607 (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', lout) or
592 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', lout)
608 el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', lout)
593 or el.endswith(" (esc)\n") and
609 or el.endswith(" (esc)\n") and
594 el.decode('string-escape') == l)):
610 el.decode('string-escape') == l)):
595 postout.append(" " + el) # fallback regex/glob/esc match
611 postout.append(" " + el) # fallback regex/glob/esc match
596 else:
612 else:
597 if needescape(lout):
613 if needescape(lout):
598 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
614 lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
599 postout.append(" " + lout) # let diff deal with it
615 postout.append(" " + lout) # let diff deal with it
600
616
601 if lcmd:
617 if lcmd:
602 # add on last return code
618 # add on last return code
603 ret = int(lcmd.split()[1])
619 ret = int(lcmd.split()[1])
604 if ret != 0:
620 if ret != 0:
605 postout.append(" [%s]\n" % ret)
621 postout.append(" [%s]\n" % ret)
606 if pos in after:
622 if pos in after:
607 postout += after.pop(pos)
623 postout += after.pop(pos)
608 pos = int(lcmd.split()[0])
624 pos = int(lcmd.split()[0])
609
625
610 if pos in after:
626 if pos in after:
611 postout += after.pop(pos)
627 postout += after.pop(pos)
612
628
613 return exitcode, postout
629 return exitcode, postout
614
630
615 wifexited = getattr(os, "WIFEXITED", lambda x: False)
631 wifexited = getattr(os, "WIFEXITED", lambda x: False)
616 def run(cmd, wd, options, replacements):
632 def run(cmd, wd, options, replacements):
617 """Run command in a sub-process, capturing the output (stdout and stderr).
633 """Run command in a sub-process, capturing the output (stdout and stderr).
618 Return a tuple (exitcode, output). output is None in debug mode."""
634 Return a tuple (exitcode, output). output is None in debug mode."""
619 # TODO: Use subprocess.Popen if we're running on Python 2.4
635 # TODO: Use subprocess.Popen if we're running on Python 2.4
620 if options.debug:
636 if options.debug:
621 proc = subprocess.Popen(cmd, shell=True)
637 proc = subprocess.Popen(cmd, shell=True)
622 ret = proc.wait()
638 ret = proc.wait()
623 return (ret, None)
639 return (ret, None)
624
640
625 if os.name == 'nt' or sys.platform.startswith('java'):
641 if os.name == 'nt' or sys.platform.startswith('java'):
626 tochild, fromchild = os.popen4(cmd)
642 tochild, fromchild = os.popen4(cmd)
627 tochild.close()
643 tochild.close()
628 output = fromchild.read()
644 output = fromchild.read()
629 ret = fromchild.close()
645 ret = fromchild.close()
630 if ret is None:
646 if ret is None:
631 ret = 0
647 ret = 0
632 else:
648 else:
633 proc = Popen4(cmd, wd, options.timeout)
649 proc = Popen4(cmd, wd, options.timeout)
634 def cleanup():
650 def cleanup():
635 try:
651 try:
636 proc.terminate()
652 proc.terminate()
637 except OSError:
653 except OSError:
638 pass
654 pass
639 ret = proc.wait()
655 ret = proc.wait()
640 if ret == 0:
656 if ret == 0:
641 ret = signal.SIGTERM << 8
657 ret = signal.SIGTERM << 8
642 killdaemons()
658 killdaemons()
643 return ret
659 return ret
644
660
645 output = ''
661 output = ''
646 proc.tochild.close()
662 proc.tochild.close()
647
663
648 try:
664 try:
649 output = proc.fromchild.read()
665 output = proc.fromchild.read()
650 except KeyboardInterrupt:
666 except KeyboardInterrupt:
651 vlog('# Handling keyboard interrupt')
667 vlog('# Handling keyboard interrupt')
652 cleanup()
668 cleanup()
653 raise
669 raise
654
670
655 ret = proc.wait()
671 ret = proc.wait()
656 if wifexited(ret):
672 if wifexited(ret):
657 ret = os.WEXITSTATUS(ret)
673 ret = os.WEXITSTATUS(ret)
658
674
659 if proc.timeout:
675 if proc.timeout:
660 ret = 'timeout'
676 ret = 'timeout'
661
677
662 if ret:
678 if ret:
663 killdaemons()
679 killdaemons()
664
680
665 for s, r in replacements:
681 for s, r in replacements:
666 output = re.sub(s, r, output)
682 output = re.sub(s, r, output)
667 return ret, splitnewlines(output)
683 return ret, splitnewlines(output)
668
684
669 def runone(options, test):
685 def runone(options, test):
670 '''tristate output:
686 '''tristate output:
671 None -> skipped
687 None -> skipped
672 True -> passed
688 True -> passed
673 False -> failed'''
689 False -> failed'''
674
690
675 global results, resultslock, iolock
691 global results, resultslock, iolock
676
692
677 testpath = os.path.join(TESTDIR, test)
693 testpath = os.path.join(TESTDIR, test)
678
694
679 def result(l, e):
695 def result(l, e):
680 resultslock.acquire()
696 resultslock.acquire()
681 results[l].append(e)
697 results[l].append(e)
682 resultslock.release()
698 resultslock.release()
683
699
684 def skip(msg):
700 def skip(msg):
685 if not options.verbose:
701 if not options.verbose:
686 result('s', (test, msg))
702 result('s', (test, msg))
687 else:
703 else:
688 iolock.acquire()
704 iolock.acquire()
689 print "\nSkipping %s: %s" % (testpath, msg)
705 print "\nSkipping %s: %s" % (testpath, msg)
690 iolock.release()
706 iolock.release()
691 return None
707 return None
692
708
693 def fail(msg, ret):
709 def fail(msg, ret):
694 if not options.nodiff:
710 if not options.nodiff:
695 iolock.acquire()
711 iolock.acquire()
696 print "\nERROR: %s %s" % (testpath, msg)
712 print "\nERROR: %s %s" % (testpath, msg)
697 iolock.release()
713 iolock.release()
698 if (not ret and options.interactive
714 if (not ret and options.interactive
699 and os.path.exists(testpath + ".err")):
715 and os.path.exists(testpath + ".err")):
700 iolock.acquire()
716 iolock.acquire()
701 print "Accept this change? [n] ",
717 print "Accept this change? [n] ",
702 answer = sys.stdin.readline().strip()
718 answer = sys.stdin.readline().strip()
703 iolock.release()
719 iolock.release()
704 if answer.lower() in "y yes".split():
720 if answer.lower() in "y yes".split():
705 if test.endswith(".t"):
721 if test.endswith(".t"):
706 rename(testpath + ".err", testpath)
722 rename(testpath + ".err", testpath)
707 else:
723 else:
708 rename(testpath + ".err", testpath + ".out")
724 rename(testpath + ".err", testpath + ".out")
709 return
725 return
710 result('f', (test, msg))
726 result('f', (test, msg))
711
727
712 def success():
728 def success():
713 result('p', test)
729 result('p', test)
714
730
715 def ignore(msg):
731 def ignore(msg):
716 result('i', (test, msg))
732 result('i', (test, msg))
717
733
718 if (os.path.basename(test).startswith("test-") and '~' not in test and
734 if (os.path.basename(test).startswith("test-") and '~' not in test and
719 ('.' not in test or test.endswith('.py') or
735 ('.' not in test or test.endswith('.py') or
720 test.endswith('.bat') or test.endswith('.t'))):
736 test.endswith('.bat') or test.endswith('.t'))):
721 if not os.path.exists(test):
737 if not os.path.exists(test):
722 skip("doesn't exist")
738 skip("doesn't exist")
723 return None
739 return None
724 else:
740 else:
725 return None # not a supported test, don't record
741 return None # not a supported test, don't record
726
742
727 if options.blacklist:
743 if options.blacklist:
728 filename = options.blacklist.get(test)
744 filename = options.blacklist.get(test)
729 if filename is not None:
745 if filename is not None:
730 skip("blacklisted")
746 skip("blacklisted")
731 return None
747 return None
732
748
733 if options.retest and not os.path.exists(test + ".err"):
749 if options.retest and not os.path.exists(test + ".err"):
734 ignore("not retesting")
750 ignore("not retesting")
735 return None
751 return None
736
752
737 if options.keywords:
753 if options.keywords:
738 fp = open(test)
754 fp = open(test)
739 t = fp.read().lower() + test.lower()
755 t = fp.read().lower() + test.lower()
740 fp.close()
756 fp.close()
741 for k in options.keywords.lower().split():
757 for k in options.keywords.lower().split():
742 if k in t:
758 if k in t:
743 break
759 break
744 else:
760 else:
745 ignore("doesn't match keyword")
761 ignore("doesn't match keyword")
746 return None
762 return None
747
763
748 vlog("# Test", test)
764 vlog("# Test", test)
749
765
750 # create a fresh hgrc
766 # create a fresh hgrc
751 hgrc = open(HGRCPATH, 'w+')
767 hgrc = open(HGRCPATH, 'w+')
752 hgrc.write('[ui]\n')
768 hgrc.write('[ui]\n')
753 hgrc.write('slash = True\n')
769 hgrc.write('slash = True\n')
754 hgrc.write('[defaults]\n')
770 hgrc.write('[defaults]\n')
755 hgrc.write('backout = -d "0 0"\n')
771 hgrc.write('backout = -d "0 0"\n')
756 hgrc.write('commit = -d "0 0"\n')
772 hgrc.write('commit = -d "0 0"\n')
757 hgrc.write('tag = -d "0 0"\n')
773 hgrc.write('tag = -d "0 0"\n')
758 if options.inotify:
774 if options.inotify:
759 hgrc.write('[extensions]\n')
775 hgrc.write('[extensions]\n')
760 hgrc.write('inotify=\n')
776 hgrc.write('inotify=\n')
761 hgrc.write('[inotify]\n')
777 hgrc.write('[inotify]\n')
762 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
778 hgrc.write('pidfile=%s\n' % DAEMON_PIDS)
763 hgrc.write('appendpid=True\n')
779 hgrc.write('appendpid=True\n')
764 if options.extra_config_opt:
780 if options.extra_config_opt:
765 for opt in options.extra_config_opt:
781 for opt in options.extra_config_opt:
766 section, key = opt.split('.', 1)
782 section, key = opt.split('.', 1)
767 assert '=' in key, ('extra config opt %s must '
783 assert '=' in key, ('extra config opt %s must '
768 'have an = for assignment' % opt)
784 'have an = for assignment' % opt)
769 hgrc.write('[%s]\n%s\n' % (section, key))
785 hgrc.write('[%s]\n%s\n' % (section, key))
770 hgrc.close()
786 hgrc.close()
771
787
772 ref = os.path.join(TESTDIR, test+".out")
788 ref = os.path.join(TESTDIR, test+".out")
773 err = os.path.join(TESTDIR, test+".err")
789 err = os.path.join(TESTDIR, test+".err")
774 if os.path.exists(err):
790 if os.path.exists(err):
775 os.remove(err) # Remove any previous output files
791 os.remove(err) # Remove any previous output files
776 try:
792 try:
777 tf = open(testpath)
793 tf = open(testpath)
778 firstline = tf.readline().rstrip()
794 firstline = tf.readline().rstrip()
779 tf.close()
795 tf.close()
780 except:
796 except:
781 firstline = ''
797 firstline = ''
782 lctest = test.lower()
798 lctest = test.lower()
783
799
784 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
800 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
785 runner = pytest
801 runner = pytest
786 elif lctest.endswith('.t'):
802 elif lctest.endswith('.t'):
787 runner = tsttest
803 runner = tsttest
788 ref = testpath
804 ref = testpath
789 else:
805 else:
790 # do not try to run non-executable programs
806 # do not try to run non-executable programs
791 if not os.access(testpath, os.X_OK):
807 if not os.access(testpath, os.X_OK):
792 return skip("not executable")
808 return skip("not executable")
793 runner = shtest
809 runner = shtest
794
810
795 # Make a tmp subdirectory to work in
811 # Make a tmp subdirectory to work in
796 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
812 testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \
797 os.path.join(HGTMP, os.path.basename(test))
813 os.path.join(HGTMP, os.path.basename(test))
798
814
799 os.mkdir(testtmp)
815 os.mkdir(testtmp)
800 ret, out = runner(testpath, testtmp, options, [
816 ret, out = runner(testpath, testtmp, options, [
801 (re.escape(testtmp), '$TESTTMP'),
817 (re.escape(testtmp), '$TESTTMP'),
802 (r':%s\b' % options.port, ':$HGPORT'),
818 (r':%s\b' % options.port, ':$HGPORT'),
803 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
819 (r':%s\b' % (options.port + 1), ':$HGPORT1'),
804 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
820 (r':%s\b' % (options.port + 2), ':$HGPORT2'),
805 ])
821 ])
806 vlog("# Ret was:", ret)
822 vlog("# Ret was:", ret)
807
823
808 mark = '.'
824 mark = '.'
809
825
810 skipped = (ret == SKIPPED_STATUS)
826 skipped = (ret == SKIPPED_STATUS)
811
827
812 # If we're not in --debug mode and reference output file exists,
828 # If we're not in --debug mode and reference output file exists,
813 # check test output against it.
829 # check test output against it.
814 if options.debug:
830 if options.debug:
815 refout = None # to match "out is None"
831 refout = None # to match "out is None"
816 elif os.path.exists(ref):
832 elif os.path.exists(ref):
817 f = open(ref, "r")
833 f = open(ref, "r")
818 refout = splitnewlines(f.read())
834 refout = splitnewlines(f.read())
819 f.close()
835 f.close()
820 else:
836 else:
821 refout = []
837 refout = []
822
838
823 if (ret != 0 or out != refout) and not skipped and not options.debug:
839 if (ret != 0 or out != refout) and not skipped and not options.debug:
824 # Save errors to a file for diagnosis
840 # Save errors to a file for diagnosis
825 f = open(err, "wb")
841 f = open(err, "wb")
826 for line in out:
842 for line in out:
827 f.write(line)
843 f.write(line)
828 f.close()
844 f.close()
829
845
830 if skipped:
846 if skipped:
831 mark = 's'
847 mark = 's'
832 if out is None: # debug mode: nothing to parse
848 if out is None: # debug mode: nothing to parse
833 missing = ['unknown']
849 missing = ['unknown']
834 failed = None
850 failed = None
835 else:
851 else:
836 missing, failed = parsehghaveoutput(out)
852 missing, failed = parsehghaveoutput(out)
837 if not missing:
853 if not missing:
838 missing = ['irrelevant']
854 missing = ['irrelevant']
839 if failed:
855 if failed:
840 fail("hghave failed checking for %s" % failed[-1], ret)
856 fail("hghave failed checking for %s" % failed[-1], ret)
841 skipped = False
857 skipped = False
842 else:
858 else:
843 skip(missing[-1])
859 skip(missing[-1])
844 elif ret == 'timeout':
860 elif ret == 'timeout':
845 mark = 't'
861 mark = 't'
846 fail("timed out", ret)
862 fail("timed out", ret)
847 elif out != refout:
863 elif out != refout:
848 mark = '!'
864 mark = '!'
849 if not options.nodiff:
865 if not options.nodiff:
850 iolock.acquire()
866 iolock.acquire()
851 if options.view:
867 if options.view:
852 os.system("%s %s %s" % (options.view, ref, err))
868 os.system("%s %s %s" % (options.view, ref, err))
853 else:
869 else:
854 showdiff(refout, out, ref, err)
870 showdiff(refout, out, ref, err)
855 iolock.release()
871 iolock.release()
856 if ret:
872 if ret:
857 fail("output changed and returned error code %d" % ret, ret)
873 fail("output changed and returned error code %d" % ret, ret)
858 else:
874 else:
859 fail("output changed", ret)
875 fail("output changed", ret)
860 ret = 1
876 ret = 1
861 elif ret:
877 elif ret:
862 mark = '!'
878 mark = '!'
863 fail("returned error code %d" % ret, ret)
879 fail("returned error code %d" % ret, ret)
864 else:
880 else:
865 success()
881 success()
866
882
867 if not options.verbose:
883 if not options.verbose:
868 iolock.acquire()
884 iolock.acquire()
869 sys.stdout.write(mark)
885 sys.stdout.write(mark)
870 sys.stdout.flush()
886 sys.stdout.flush()
871 iolock.release()
887 iolock.release()
872
888
873 killdaemons()
889 killdaemons()
874
890
875 if not options.keep_tmpdir:
891 if not options.keep_tmpdir:
876 shutil.rmtree(testtmp, True)
892 shutil.rmtree(testtmp, True)
877 if skipped:
893 if skipped:
878 return None
894 return None
879 return ret == 0
895 return ret == 0
880
896
881 _hgpath = None
897 _hgpath = None
882
898
883 def _gethgpath():
899 def _gethgpath():
884 """Return the path to the mercurial package that is actually found by
900 """Return the path to the mercurial package that is actually found by
885 the current Python interpreter."""
901 the current Python interpreter."""
886 global _hgpath
902 global _hgpath
887 if _hgpath is not None:
903 if _hgpath is not None:
888 return _hgpath
904 return _hgpath
889
905
890 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
906 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
891 pipe = os.popen(cmd % PYTHON)
907 pipe = os.popen(cmd % PYTHON)
892 try:
908 try:
893 _hgpath = pipe.read().strip()
909 _hgpath = pipe.read().strip()
894 finally:
910 finally:
895 pipe.close()
911 pipe.close()
896 return _hgpath
912 return _hgpath
897
913
898 def _checkhglib(verb):
914 def _checkhglib(verb):
899 """Ensure that the 'mercurial' package imported by python is
915 """Ensure that the 'mercurial' package imported by python is
900 the one we expect it to be. If not, print a warning to stderr."""
916 the one we expect it to be. If not, print a warning to stderr."""
901 expecthg = os.path.join(PYTHONDIR, 'mercurial')
917 expecthg = os.path.join(PYTHONDIR, 'mercurial')
902 actualhg = _gethgpath()
918 actualhg = _gethgpath()
903 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
919 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
904 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
920 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
905 ' (expected %s)\n'
921 ' (expected %s)\n'
906 % (verb, actualhg, expecthg))
922 % (verb, actualhg, expecthg))
907
923
908 def runchildren(options, tests):
924 def runchildren(options, tests):
909 if INST:
925 if INST:
910 installhg(options)
926 installhg(options)
911 _checkhglib("Testing")
927 _checkhglib("Testing")
912
928
913 optcopy = dict(options.__dict__)
929 optcopy = dict(options.__dict__)
914 optcopy['jobs'] = 1
930 optcopy['jobs'] = 1
915 del optcopy['blacklist']
931 del optcopy['blacklist']
916 if optcopy['with_hg'] is None:
932 if optcopy['with_hg'] is None:
917 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
933 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
918 optcopy.pop('anycoverage', None)
934 optcopy.pop('anycoverage', None)
919
935
920 opts = []
936 opts = []
921 for opt, value in optcopy.iteritems():
937 for opt, value in optcopy.iteritems():
922 name = '--' + opt.replace('_', '-')
938 name = '--' + opt.replace('_', '-')
923 if value is True:
939 if value is True:
924 opts.append(name)
940 opts.append(name)
925 elif isinstance(value, list):
941 elif isinstance(value, list):
926 for v in value:
942 for v in value:
927 opts.append(name + '=' + str(v))
943 opts.append(name + '=' + str(v))
928 elif value is not None:
944 elif value is not None:
929 opts.append(name + '=' + str(value))
945 opts.append(name + '=' + str(value))
930
946
931 tests.reverse()
947 tests.reverse()
932 jobs = [[] for j in xrange(options.jobs)]
948 jobs = [[] for j in xrange(options.jobs)]
933 while tests:
949 while tests:
934 for job in jobs:
950 for job in jobs:
935 if not tests:
951 if not tests:
936 break
952 break
937 job.append(tests.pop())
953 job.append(tests.pop())
938 fps = {}
954 fps = {}
939
955
940 for j, job in enumerate(jobs):
956 for j, job in enumerate(jobs):
941 if not job:
957 if not job:
942 continue
958 continue
943 rfd, wfd = os.pipe()
959 rfd, wfd = os.pipe()
944 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
960 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
945 childtmp = os.path.join(HGTMP, 'child%d' % j)
961 childtmp = os.path.join(HGTMP, 'child%d' % j)
946 childopts += ['--tmpdir', childtmp]
962 childopts += ['--tmpdir', childtmp]
947 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
963 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
948 vlog(' '.join(cmdline))
964 vlog(' '.join(cmdline))
949 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
965 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
950 os.close(wfd)
966 os.close(wfd)
951 signal.signal(signal.SIGINT, signal.SIG_IGN)
967 signal.signal(signal.SIGINT, signal.SIG_IGN)
952 failures = 0
968 failures = 0
953 tested, skipped, failed = 0, 0, 0
969 tested, skipped, failed = 0, 0, 0
954 skips = []
970 skips = []
955 fails = []
971 fails = []
956 while fps:
972 while fps:
957 pid, status = os.wait()
973 pid, status = os.wait()
958 fp = fps.pop(pid)
974 fp = fps.pop(pid)
959 l = fp.read().splitlines()
975 l = fp.read().splitlines()
960 try:
976 try:
961 test, skip, fail = map(int, l[:3])
977 test, skip, fail = map(int, l[:3])
962 except ValueError:
978 except ValueError:
963 test, skip, fail = 0, 0, 0
979 test, skip, fail = 0, 0, 0
964 split = -fail or len(l)
980 split = -fail or len(l)
965 for s in l[3:split]:
981 for s in l[3:split]:
966 skips.append(s.split(" ", 1))
982 skips.append(s.split(" ", 1))
967 for s in l[split:]:
983 for s in l[split:]:
968 fails.append(s.split(" ", 1))
984 fails.append(s.split(" ", 1))
969 tested += test
985 tested += test
970 skipped += skip
986 skipped += skip
971 failed += fail
987 failed += fail
972 vlog('pid %d exited, status %d' % (pid, status))
988 vlog('pid %d exited, status %d' % (pid, status))
973 failures |= status
989 failures |= status
974 print
990 print
975 if not options.noskips:
991 if not options.noskips:
976 for s in skips:
992 for s in skips:
977 print "Skipped %s: %s" % (s[0], s[1])
993 print "Skipped %s: %s" % (s[0], s[1])
978 for s in fails:
994 for s in fails:
979 print "Failed %s: %s" % (s[0], s[1])
995 print "Failed %s: %s" % (s[0], s[1])
980
996
981 _checkhglib("Tested")
997 _checkhglib("Tested")
982 print "# Ran %d tests, %d skipped, %d failed." % (
998 print "# Ran %d tests, %d skipped, %d failed." % (
983 tested, skipped, failed)
999 tested, skipped, failed)
984
1000
985 if options.anycoverage:
1001 if options.anycoverage:
986 outputcoverage(options)
1002 outputcoverage(options)
987 sys.exit(failures != 0)
1003 sys.exit(failures != 0)
988
1004
989 results = dict(p=[], f=[], s=[], i=[])
1005 results = dict(p=[], f=[], s=[], i=[])
990 resultslock = threading.Lock()
1006 resultslock = threading.Lock()
991 iolock = threading.Lock()
1007 iolock = threading.Lock()
992
1008
993 def runqueue(options, tests, results):
1009 def runqueue(options, tests, results):
994 for test in tests:
1010 for test in tests:
995 ret = runone(options, test)
1011 ret = runone(options, test)
996 if options.first and ret is not None and not ret:
1012 if options.first and ret is not None and not ret:
997 break
1013 break
998
1014
999 def runtests(options, tests):
1015 def runtests(options, tests):
1000 global DAEMON_PIDS, HGRCPATH
1016 global DAEMON_PIDS, HGRCPATH
1001 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1017 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
1002 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1018 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
1003
1019
1004 try:
1020 try:
1005 if INST:
1021 if INST:
1006 installhg(options)
1022 installhg(options)
1007 _checkhglib("Testing")
1023 _checkhglib("Testing")
1008
1024
1009 if options.restart:
1025 if options.restart:
1010 orig = list(tests)
1026 orig = list(tests)
1011 while tests:
1027 while tests:
1012 if os.path.exists(tests[0] + ".err"):
1028 if os.path.exists(tests[0] + ".err"):
1013 break
1029 break
1014 tests.pop(0)
1030 tests.pop(0)
1015 if not tests:
1031 if not tests:
1016 print "running all tests"
1032 print "running all tests"
1017 tests = orig
1033 tests = orig
1018
1034
1019 runqueue(options, tests, results)
1035 runqueue(options, tests, results)
1020
1036
1021 failed = len(results['f'])
1037 failed = len(results['f'])
1022 tested = len(results['p']) + failed
1038 tested = len(results['p']) + failed
1023 skipped = len(results['s'])
1039 skipped = len(results['s'])
1024 ignored = len(results['i'])
1040 ignored = len(results['i'])
1025
1041
1026 if options.child:
1042 if options.child:
1027 fp = os.fdopen(options.child, 'w')
1043 fp = os.fdopen(options.child, 'w')
1028 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1044 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
1029 for s in results['s']:
1045 for s in results['s']:
1030 fp.write("%s %s\n" % s)
1046 fp.write("%s %s\n" % s)
1031 for s in results['f']:
1047 for s in results['f']:
1032 fp.write("%s %s\n" % s)
1048 fp.write("%s %s\n" % s)
1033 fp.close()
1049 fp.close()
1034 else:
1050 else:
1035 print
1051 print
1036 for s in results['s']:
1052 for s in results['s']:
1037 print "Skipped %s: %s" % s
1053 print "Skipped %s: %s" % s
1038 for s in results['f']:
1054 for s in results['f']:
1039 print "Failed %s: %s" % s
1055 print "Failed %s: %s" % s
1040 _checkhglib("Tested")
1056 _checkhglib("Tested")
1041 print "# Ran %d tests, %d skipped, %d failed." % (
1057 print "# Ran %d tests, %d skipped, %d failed." % (
1042 tested, skipped + ignored, failed)
1058 tested, skipped + ignored, failed)
1043
1059
1044 if options.anycoverage:
1060 if options.anycoverage:
1045 outputcoverage(options)
1061 outputcoverage(options)
1046 except KeyboardInterrupt:
1062 except KeyboardInterrupt:
1047 failed = True
1063 failed = True
1048 print "\ninterrupted!"
1064 print "\ninterrupted!"
1049
1065
1050 if failed:
1066 if failed:
1051 sys.exit(1)
1067 sys.exit(1)
1052
1068
1053 def main():
1069 def main():
1054 (options, args) = parseargs()
1070 (options, args) = parseargs()
1055 if not options.child:
1071 if not options.child:
1056 os.umask(022)
1072 os.umask(022)
1057
1073
1058 checktools()
1074 checktools()
1059
1075
1060 if len(args) == 0:
1076 if len(args) == 0:
1061 args = os.listdir(".")
1077 args = os.listdir(".")
1062 args.sort()
1078 args.sort()
1063
1079
1064 tests = args
1080 tests = args
1065
1081
1066 # Reset some environment variables to well-known values so that
1082 # Reset some environment variables to well-known values so that
1067 # the tests produce repeatable output.
1083 # the tests produce repeatable output.
1068 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1084 os.environ['LANG'] = os.environ['LC_ALL'] = os.environ['LANGUAGE'] = 'C'
1069 os.environ['TZ'] = 'GMT'
1085 os.environ['TZ'] = 'GMT'
1070 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1086 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
1071 os.environ['CDPATH'] = ''
1087 os.environ['CDPATH'] = ''
1072 os.environ['COLUMNS'] = '80'
1088 os.environ['COLUMNS'] = '80'
1073 os.environ['GREP_OPTIONS'] = ''
1089 os.environ['GREP_OPTIONS'] = ''
1074 os.environ['http_proxy'] = ''
1090 os.environ['http_proxy'] = ''
1075
1091
1076 # unset env related to hooks
1092 # unset env related to hooks
1077 for k in os.environ.keys():
1093 for k in os.environ.keys():
1078 if k.startswith('HG_'):
1094 if k.startswith('HG_'):
1079 # can't remove on solaris
1095 # can't remove on solaris
1080 os.environ[k] = ''
1096 os.environ[k] = ''
1081 del os.environ[k]
1097 del os.environ[k]
1082
1098
1083 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1099 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
1084 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1100 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
1085 if options.tmpdir:
1101 if options.tmpdir:
1086 options.keep_tmpdir = True
1102 options.keep_tmpdir = True
1087 tmpdir = options.tmpdir
1103 tmpdir = options.tmpdir
1088 if os.path.exists(tmpdir):
1104 if os.path.exists(tmpdir):
1089 # Meaning of tmpdir has changed since 1.3: we used to create
1105 # Meaning of tmpdir has changed since 1.3: we used to create
1090 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1106 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1091 # tmpdir already exists.
1107 # tmpdir already exists.
1092 sys.exit("error: temp dir %r already exists" % tmpdir)
1108 sys.exit("error: temp dir %r already exists" % tmpdir)
1093
1109
1094 # Automatically removing tmpdir sounds convenient, but could
1110 # Automatically removing tmpdir sounds convenient, but could
1095 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1111 # really annoy anyone in the habit of using "--tmpdir=/tmp"
1096 # or "--tmpdir=$HOME".
1112 # or "--tmpdir=$HOME".
1097 #vlog("# Removing temp dir", tmpdir)
1113 #vlog("# Removing temp dir", tmpdir)
1098 #shutil.rmtree(tmpdir)
1114 #shutil.rmtree(tmpdir)
1099 os.makedirs(tmpdir)
1115 os.makedirs(tmpdir)
1100 else:
1116 else:
1101 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1117 tmpdir = tempfile.mkdtemp('', 'hgtests.')
1102 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1118 HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir)
1103 DAEMON_PIDS = None
1119 DAEMON_PIDS = None
1104 HGRCPATH = None
1120 HGRCPATH = None
1105
1121
1106 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1122 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
1107 os.environ["HGMERGE"] = "internal:merge"
1123 os.environ["HGMERGE"] = "internal:merge"
1108 os.environ["HGUSER"] = "test"
1124 os.environ["HGUSER"] = "test"
1109 os.environ["HGENCODING"] = "ascii"
1125 os.environ["HGENCODING"] = "ascii"
1110 os.environ["HGENCODINGMODE"] = "strict"
1126 os.environ["HGENCODINGMODE"] = "strict"
1111 os.environ["HGPORT"] = str(options.port)
1127 os.environ["HGPORT"] = str(options.port)
1112 os.environ["HGPORT1"] = str(options.port + 1)
1128 os.environ["HGPORT1"] = str(options.port + 1)
1113 os.environ["HGPORT2"] = str(options.port + 2)
1129 os.environ["HGPORT2"] = str(options.port + 2)
1114
1130
1115 if options.with_hg:
1131 if options.with_hg:
1116 INST = None
1132 INST = None
1117 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1133 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
1118
1134
1119 # This looks redundant with how Python initializes sys.path from
1135 # This looks redundant with how Python initializes sys.path from
1120 # the location of the script being executed. Needed because the
1136 # the location of the script being executed. Needed because the
1121 # "hg" specified by --with-hg is not the only Python script
1137 # "hg" specified by --with-hg is not the only Python script
1122 # executed in the test suite that needs to import 'mercurial'
1138 # executed in the test suite that needs to import 'mercurial'
1123 # ... which means it's not really redundant at all.
1139 # ... which means it's not really redundant at all.
1124 PYTHONDIR = BINDIR
1140 PYTHONDIR = BINDIR
1125 else:
1141 else:
1126 INST = os.path.join(HGTMP, "install")
1142 INST = os.path.join(HGTMP, "install")
1127 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1143 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
1128 PYTHONDIR = os.path.join(INST, "lib", "python")
1144 PYTHONDIR = os.path.join(INST, "lib", "python")
1129
1145
1130 os.environ["BINDIR"] = BINDIR
1146 os.environ["BINDIR"] = BINDIR
1131 os.environ["PYTHON"] = PYTHON
1147 os.environ["PYTHON"] = PYTHON
1132
1148
1133 if not options.child:
1149 if not options.child:
1134 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1150 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
1135 os.environ["PATH"] = os.pathsep.join(path)
1151 os.environ["PATH"] = os.pathsep.join(path)
1136
1152
1137 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1153 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
1138 # can run .../tests/run-tests.py test-foo where test-foo
1154 # can run .../tests/run-tests.py test-foo where test-foo
1139 # adds an extension to HGRC
1155 # adds an extension to HGRC
1140 pypath = [PYTHONDIR, TESTDIR]
1156 pypath = [PYTHONDIR, TESTDIR]
1141 # We have to augment PYTHONPATH, rather than simply replacing
1157 # We have to augment PYTHONPATH, rather than simply replacing
1142 # it, in case external libraries are only available via current
1158 # it, in case external libraries are only available via current
1143 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1159 # PYTHONPATH. (In particular, the Subversion bindings on OS X
1144 # are in /opt/subversion.)
1160 # are in /opt/subversion.)
1145 oldpypath = os.environ.get(IMPL_PATH)
1161 oldpypath = os.environ.get(IMPL_PATH)
1146 if oldpypath:
1162 if oldpypath:
1147 pypath.append(oldpypath)
1163 pypath.append(oldpypath)
1148 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1164 os.environ[IMPL_PATH] = os.pathsep.join(pypath)
1149
1165
1150 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1166 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
1151
1167
1152 vlog("# Using TESTDIR", TESTDIR)
1168 vlog("# Using TESTDIR", TESTDIR)
1153 vlog("# Using HGTMP", HGTMP)
1169 vlog("# Using HGTMP", HGTMP)
1154 vlog("# Using PATH", os.environ["PATH"])
1170 vlog("# Using PATH", os.environ["PATH"])
1155 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1171 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
1156
1172
1157 try:
1173 try:
1158 if len(tests) > 1 and options.jobs > 1:
1174 if len(tests) > 1 and options.jobs > 1:
1159 runchildren(options, tests)
1175 runchildren(options, tests)
1160 else:
1176 else:
1161 runtests(options, tests)
1177 runtests(options, tests)
1162 finally:
1178 finally:
1163 time.sleep(1)
1179 time.sleep(1)
1164 cleanup(options)
1180 cleanup(options)
1165
1181
1166 if __name__ == '__main__':
1182 if __name__ == '__main__':
1167 main()
1183 main()
General Comments 0
You need to be logged in to leave comments. Login now