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