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