##// END OF EJS Templates
tests: add -3 switch to run-tests.py
Alejandro Santos -
r9028:bea567ae default
parent child Browse files
Show More
@@ -1,806 +1,824 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, incorporated herein by reference.
8 # GNU General Public License version 2, incorporated herein by reference.
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 #
36 #
37 # (You could use any subset of the tests: test-s* happens to match
37 # (You could use any subset of the tests: test-s* happens to match
38 # enough that it's worth doing parallel runs, few enough that it
38 # enough that it's worth doing parallel runs, few enough that it
39 # completes fairly quickly, includes both shell and Python scripts, and
39 # completes fairly quickly, includes both shell and Python scripts, and
40 # includes some scripts that run daemon processes.)
40 # includes some scripts that run daemon processes.)
41
41
42 import difflib
42 import difflib
43 import errno
43 import errno
44 import optparse
44 import optparse
45 import os
45 import os
46 import subprocess
46 import subprocess
47 import shutil
47 import shutil
48 import signal
48 import signal
49 import sys
49 import sys
50 import tempfile
50 import tempfile
51 import time
51 import time
52
52
53 closefds = os.name == 'posix'
53 closefds = os.name == 'posix'
54 def Popen4(cmd, bufsize=-1):
54 def Popen4(cmd, bufsize=-1):
55 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
55 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
56 close_fds=closefds,
56 close_fds=closefds,
57 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
57 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
58 stderr=subprocess.STDOUT)
58 stderr=subprocess.STDOUT)
59 p.fromchild = p.stdout
59 p.fromchild = p.stdout
60 p.tochild = p.stdin
60 p.tochild = p.stdin
61 p.childerr = p.stderr
61 p.childerr = p.stderr
62 return p
62 return p
63
63
64 # reserved exit code to skip test (used by hghave)
64 # reserved exit code to skip test (used by hghave)
65 SKIPPED_STATUS = 80
65 SKIPPED_STATUS = 80
66 SKIPPED_PREFIX = 'skipped: '
66 SKIPPED_PREFIX = 'skipped: '
67 FAILED_PREFIX = 'hghave check failed: '
67 FAILED_PREFIX = 'hghave check failed: '
68 PYTHON = sys.executable
68 PYTHON = sys.executable
69
69
70 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
70 requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
71
71
72 defaults = {
72 defaults = {
73 'jobs': ('HGTEST_JOBS', 1),
73 'jobs': ('HGTEST_JOBS', 1),
74 'timeout': ('HGTEST_TIMEOUT', 180),
74 'timeout': ('HGTEST_TIMEOUT', 180),
75 'port': ('HGTEST_PORT', 20059),
75 'port': ('HGTEST_PORT', 20059),
76 }
76 }
77
77
78 def parseargs():
78 def parseargs():
79 parser = optparse.OptionParser("%prog [options] [tests]")
79 parser = optparse.OptionParser("%prog [options] [tests]")
80 parser.add_option("-C", "--annotate", action="store_true",
80 parser.add_option("-C", "--annotate", action="store_true",
81 help="output files annotated with coverage")
81 help="output files annotated with coverage")
82 parser.add_option("--child", type="int",
82 parser.add_option("--child", type="int",
83 help="run as child process, summary to given fd")
83 help="run as child process, summary to given fd")
84 parser.add_option("-c", "--cover", action="store_true",
84 parser.add_option("-c", "--cover", action="store_true",
85 help="print a test coverage report")
85 help="print a test coverage report")
86 parser.add_option("-f", "--first", action="store_true",
86 parser.add_option("-f", "--first", action="store_true",
87 help="exit on the first test failure")
87 help="exit on the first test failure")
88 parser.add_option("-i", "--interactive", action="store_true",
88 parser.add_option("-i", "--interactive", action="store_true",
89 help="prompt to accept changed output")
89 help="prompt to accept changed output")
90 parser.add_option("-j", "--jobs", type="int",
90 parser.add_option("-j", "--jobs", type="int",
91 help="number of jobs to run in parallel"
91 help="number of jobs to run in parallel"
92 " (default: $%s or %d)" % defaults['jobs'])
92 " (default: $%s or %d)" % defaults['jobs'])
93 parser.add_option("--keep-tmpdir", action="store_true",
93 parser.add_option("--keep-tmpdir", action="store_true",
94 help="keep temporary directory after running tests"
94 help="keep temporary directory after running tests"
95 " (best used with --tmpdir)")
95 " (best used with --tmpdir)")
96 parser.add_option("-R", "--restart", action="store_true",
96 parser.add_option("-R", "--restart", action="store_true",
97 help="restart at last error")
97 help="restart at last error")
98 parser.add_option("-p", "--port", type="int",
98 parser.add_option("-p", "--port", type="int",
99 help="port on which servers should listen"
99 help="port on which servers should listen"
100 " (default: $%s or %d)" % defaults['port'])
100 " (default: $%s or %d)" % defaults['port'])
101 parser.add_option("-r", "--retest", action="store_true",
101 parser.add_option("-r", "--retest", action="store_true",
102 help="retest failed tests")
102 help="retest failed tests")
103 parser.add_option("-s", "--cover_stdlib", action="store_true",
103 parser.add_option("-s", "--cover_stdlib", action="store_true",
104 help="print a test coverage report inc. standard libraries")
104 help="print a test coverage report inc. standard libraries")
105 parser.add_option("-t", "--timeout", type="int",
105 parser.add_option("-t", "--timeout", type="int",
106 help="kill errant tests after TIMEOUT seconds"
106 help="kill errant tests after TIMEOUT seconds"
107 " (default: $%s or %d)" % defaults['timeout'])
107 " (default: $%s or %d)" % defaults['timeout'])
108 parser.add_option("--tmpdir", type="string",
108 parser.add_option("--tmpdir", type="string",
109 help="run tests in the given temporary directory")
109 help="run tests in the given temporary directory")
110 parser.add_option("-v", "--verbose", action="store_true",
110 parser.add_option("-v", "--verbose", action="store_true",
111 help="output verbose messages")
111 help="output verbose messages")
112 parser.add_option("-n", "--nodiff", action="store_true",
112 parser.add_option("-n", "--nodiff", action="store_true",
113 help="skip showing test changes")
113 help="skip showing test changes")
114 parser.add_option("--with-hg", type="string",
114 parser.add_option("--with-hg", type="string",
115 metavar="HG",
115 metavar="HG",
116 help="test using specified hg script rather than a "
116 help="test using specified hg script rather than a "
117 "temporary installation")
117 "temporary installation")
118 parser.add_option("--local", action="store_true",
118 parser.add_option("--local", action="store_true",
119 help="shortcut for --with-hg=<testdir>/../hg")
119 help="shortcut for --with-hg=<testdir>/../hg")
120 parser.add_option("--pure", action="store_true",
120 parser.add_option("--pure", action="store_true",
121 help="use pure Python code instead of C extensions")
121 help="use pure Python code instead of C extensions")
122 parser.add_option("-3", "--py3k-warnings", action="store_true",
123 help="enable Py3k warnings on Python 2.6+")
122
124
123 for option, default in defaults.items():
125 for option, default in defaults.items():
124 defaults[option] = int(os.environ.get(*default))
126 defaults[option] = int(os.environ.get(*default))
125 parser.set_defaults(**defaults)
127 parser.set_defaults(**defaults)
126 (options, args) = parser.parse_args()
128 (options, args) = parser.parse_args()
127
129
128 if options.with_hg:
130 if options.with_hg:
129 if not (os.path.isfile(options.with_hg) and
131 if not (os.path.isfile(options.with_hg) and
130 os.access(options.with_hg, os.X_OK)):
132 os.access(options.with_hg, os.X_OK)):
131 parser.error('--with-hg must specify an executable hg script')
133 parser.error('--with-hg must specify an executable hg script')
132 if not os.path.basename(options.with_hg) == 'hg':
134 if not os.path.basename(options.with_hg) == 'hg':
133 sys.stderr.write('warning: --with-hg should specify an hg script')
135 sys.stderr.write('warning: --with-hg should specify an hg script')
134 if options.local:
136 if options.local:
135 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
137 testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
136 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
138 hgbin = os.path.join(os.path.dirname(testdir), 'hg')
137 if not os.access(hgbin, os.X_OK):
139 if not os.access(hgbin, os.X_OK):
138 parser.error('--local specified, but %r not found or not executable'
140 parser.error('--local specified, but %r not found or not executable'
139 % hgbin)
141 % hgbin)
140 options.with_hg = hgbin
142 options.with_hg = hgbin
141
143
142 options.anycoverage = (options.cover or
144 options.anycoverage = (options.cover or
143 options.cover_stdlib or
145 options.cover_stdlib or
144 options.annotate)
146 options.annotate)
145
147
146 if options.anycoverage and options.with_hg:
148 if options.anycoverage and options.with_hg:
147 # I'm not sure if this is a fundamental limitation or just a
149 # I'm not sure if this is a fundamental limitation or just a
148 # bug. But I don't want to waste people's time and energy doing
150 # bug. But I don't want to waste people's time and energy doing
149 # test runs that don't give the results they want.
151 # test runs that don't give the results they want.
150 parser.error("sorry, coverage options do not work when --with-hg "
152 parser.error("sorry, coverage options do not work when --with-hg "
151 "or --local specified")
153 "or --local specified")
152
154
153 global vlog
155 global vlog
154 if options.verbose:
156 if options.verbose:
155 if options.jobs > 1 or options.child is not None:
157 if options.jobs > 1 or options.child is not None:
156 pid = "[%d]" % os.getpid()
158 pid = "[%d]" % os.getpid()
157 else:
159 else:
158 pid = None
160 pid = None
159 def vlog(*msg):
161 def vlog(*msg):
160 if pid:
162 if pid:
161 print pid,
163 print pid,
162 for m in msg:
164 for m in msg:
163 print m,
165 print m,
164 print
166 print
165 else:
167 else:
166 vlog = lambda *msg: None
168 vlog = lambda *msg: None
167
169
168 if options.jobs < 1:
170 if options.jobs < 1:
169 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
171 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
170 sys.exit(1)
172 sys.exit(1)
171 if options.interactive and options.jobs > 1:
173 if options.interactive and options.jobs > 1:
172 print '(--interactive overrides --jobs)'
174 print '(--interactive overrides --jobs)'
173 options.jobs = 1
175 options.jobs = 1
176 if options.py3k_warnings:
177 if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
178 print 'ERROR: Py3k warnings switch can only be used on Python 2.6+'
179 sys.exit(1)
174
180
175 return (options, args)
181 return (options, args)
176
182
177 def rename(src, dst):
183 def rename(src, dst):
178 """Like os.rename(), trade atomicity and opened files friendliness
184 """Like os.rename(), trade atomicity and opened files friendliness
179 for existing destination support.
185 for existing destination support.
180 """
186 """
181 shutil.copy(src, dst)
187 shutil.copy(src, dst)
182 os.remove(src)
188 os.remove(src)
183
189
184 def splitnewlines(text):
190 def splitnewlines(text):
185 '''like str.splitlines, but only split on newlines.
191 '''like str.splitlines, but only split on newlines.
186 keep line endings.'''
192 keep line endings.'''
187 i = 0
193 i = 0
188 lines = []
194 lines = []
189 while True:
195 while True:
190 n = text.find('\n', i)
196 n = text.find('\n', i)
191 if n == -1:
197 if n == -1:
192 last = text[i:]
198 last = text[i:]
193 if last:
199 if last:
194 lines.append(last)
200 lines.append(last)
195 return lines
201 return lines
196 lines.append(text[i:n+1])
202 lines.append(text[i:n+1])
197 i = n + 1
203 i = n + 1
198
204
199 def parsehghaveoutput(lines):
205 def parsehghaveoutput(lines):
200 '''Parse hghave log lines.
206 '''Parse hghave log lines.
201 Return tuple of lists (missing, failed):
207 Return tuple of lists (missing, failed):
202 * the missing/unknown features
208 * the missing/unknown features
203 * the features for which existence check failed'''
209 * the features for which existence check failed'''
204 missing = []
210 missing = []
205 failed = []
211 failed = []
206 for line in lines:
212 for line in lines:
207 if line.startswith(SKIPPED_PREFIX):
213 if line.startswith(SKIPPED_PREFIX):
208 line = line.splitlines()[0]
214 line = line.splitlines()[0]
209 missing.append(line[len(SKIPPED_PREFIX):])
215 missing.append(line[len(SKIPPED_PREFIX):])
210 elif line.startswith(FAILED_PREFIX):
216 elif line.startswith(FAILED_PREFIX):
211 line = line.splitlines()[0]
217 line = line.splitlines()[0]
212 failed.append(line[len(FAILED_PREFIX):])
218 failed.append(line[len(FAILED_PREFIX):])
213
219
214 return missing, failed
220 return missing, failed
215
221
216 def showdiff(expected, output):
222 def showdiff(expected, output):
217 for line in difflib.unified_diff(expected, output,
223 for line in difflib.unified_diff(expected, output,
218 "Expected output", "Test output"):
224 "Expected output", "Test output"):
219 sys.stdout.write(line)
225 sys.stdout.write(line)
220
226
221 def findprogram(program):
227 def findprogram(program):
222 """Search PATH for a executable program"""
228 """Search PATH for a executable program"""
223 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
229 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
224 name = os.path.join(p, program)
230 name = os.path.join(p, program)
225 if os.access(name, os.X_OK):
231 if os.access(name, os.X_OK):
226 return name
232 return name
227 return None
233 return None
228
234
229 def checktools():
235 def checktools():
230 # Before we go any further, check for pre-requisite tools
236 # Before we go any further, check for pre-requisite tools
231 # stuff from coreutils (cat, rm, etc) are not tested
237 # stuff from coreutils (cat, rm, etc) are not tested
232 for p in requiredtools:
238 for p in requiredtools:
233 if os.name == 'nt':
239 if os.name == 'nt':
234 p += '.exe'
240 p += '.exe'
235 found = findprogram(p)
241 found = findprogram(p)
236 if found:
242 if found:
237 vlog("# Found prerequisite", p, "at", found)
243 vlog("# Found prerequisite", p, "at", found)
238 else:
244 else:
239 print "WARNING: Did not find prerequisite tool: "+p
245 print "WARNING: Did not find prerequisite tool: "+p
240
246
241 def cleanup(options):
247 def cleanup(options):
242 if not options.keep_tmpdir:
248 if not options.keep_tmpdir:
243 vlog("# Cleaning up HGTMP", HGTMP)
249 vlog("# Cleaning up HGTMP", HGTMP)
244 shutil.rmtree(HGTMP, True)
250 shutil.rmtree(HGTMP, True)
245
251
246 def usecorrectpython():
252 def usecorrectpython():
247 # some tests run python interpreter. they must use same
253 # some tests run python interpreter. they must use same
248 # interpreter we use or bad things will happen.
254 # interpreter we use or bad things will happen.
249 exedir, exename = os.path.split(sys.executable)
255 exedir, exename = os.path.split(sys.executable)
250 if exename == 'python':
256 if exename == 'python':
251 path = findprogram('python')
257 path = findprogram('python')
252 if os.path.dirname(path) == exedir:
258 if os.path.dirname(path) == exedir:
253 return
259 return
254 vlog('# Making python executable in test path use correct Python')
260 vlog('# Making python executable in test path use correct Python')
255 mypython = os.path.join(BINDIR, 'python')
261 mypython = os.path.join(BINDIR, 'python')
256 try:
262 try:
257 os.symlink(sys.executable, mypython)
263 os.symlink(sys.executable, mypython)
258 except AttributeError:
264 except AttributeError:
259 # windows fallback
265 # windows fallback
260 shutil.copyfile(sys.executable, mypython)
266 shutil.copyfile(sys.executable, mypython)
261 shutil.copymode(sys.executable, mypython)
267 shutil.copymode(sys.executable, mypython)
262
268
263 def installhg(options):
269 def installhg(options):
264 vlog("# Performing temporary installation of HG")
270 vlog("# Performing temporary installation of HG")
265 installerrs = os.path.join("tests", "install.err")
271 installerrs = os.path.join("tests", "install.err")
266 pure = options.pure and "--pure" or ""
272 pure = options.pure and "--pure" or ""
267
273
268 # Run installer in hg root
274 # Run installer in hg root
269 script = os.path.realpath(sys.argv[0])
275 script = os.path.realpath(sys.argv[0])
270 hgroot = os.path.dirname(os.path.dirname(script))
276 hgroot = os.path.dirname(os.path.dirname(script))
271 os.chdir(hgroot)
277 os.chdir(hgroot)
272 cmd = ('%s setup.py %s clean --all'
278 cmd = ('%s setup.py %s clean --all'
273 ' install --force --prefix="%s" --install-lib="%s"'
279 ' install --force --prefix="%s" --install-lib="%s"'
274 ' --install-scripts="%s" >%s 2>&1'
280 ' --install-scripts="%s" >%s 2>&1'
275 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
281 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
276 vlog("# Running", cmd)
282 vlog("# Running", cmd)
277 if os.system(cmd) == 0:
283 if os.system(cmd) == 0:
278 if not options.verbose:
284 if not options.verbose:
279 os.remove(installerrs)
285 os.remove(installerrs)
280 else:
286 else:
281 f = open(installerrs)
287 f = open(installerrs)
282 for line in f:
288 for line in f:
283 print line,
289 print line,
284 f.close()
290 f.close()
285 sys.exit(1)
291 sys.exit(1)
286 os.chdir(TESTDIR)
292 os.chdir(TESTDIR)
287
293
288 usecorrectpython()
294 usecorrectpython()
289
295
290 vlog("# Installing dummy diffstat")
296 vlog("# Installing dummy diffstat")
291 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
297 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
292 f.write('#!' + sys.executable + '\n'
298 f.write('#!' + sys.executable + '\n'
293 'import sys\n'
299 'import sys\n'
294 'files = 0\n'
300 'files = 0\n'
295 'for line in sys.stdin:\n'
301 'for line in sys.stdin:\n'
296 ' if line.startswith("diff "):\n'
302 ' if line.startswith("diff "):\n'
297 ' files += 1\n'
303 ' files += 1\n'
298 'sys.stdout.write("files patched: %d\\n" % files)\n')
304 'sys.stdout.write("files patched: %d\\n" % files)\n')
299 f.close()
305 f.close()
300 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
306 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
301
307
308 if options.py3k_warnings and not options.anycoverage:
309 vlog("# Updating hg command to enable Py3k Warnings switch")
310 f = open(os.path.join(BINDIR, 'hg'), 'r')
311 lines = [line.rstrip() for line in f]
312 lines[0] += ' -3'
313 f.close()
314 f = open(os.path.join(BINDIR, 'hg'), 'w')
315 for line in lines:
316 f.write(line + '\n')
317 f.close()
318
302 if options.anycoverage:
319 if options.anycoverage:
303 vlog("# Installing coverage wrapper")
320 vlog("# Installing coverage wrapper")
304 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
321 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
305 if os.path.exists(COVERAGE_FILE):
322 if os.path.exists(COVERAGE_FILE):
306 os.unlink(COVERAGE_FILE)
323 os.unlink(COVERAGE_FILE)
307 # Create a wrapper script to invoke hg via coverage.py
324 # Create a wrapper script to invoke hg via coverage.py
308 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
325 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
309 f = open(os.path.join(BINDIR, 'hg'), 'w')
326 f = open(os.path.join(BINDIR, 'hg'), 'w')
310 f.write('#!' + sys.executable + '\n')
327 f.write('#!' + sys.executable + '\n')
311 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
328 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
312 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
329 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
313 (os.path.join(TESTDIR, 'coverage.py'),
330 (os.path.join(TESTDIR, 'coverage.py'),
314 os.path.join(BINDIR, '_hg.py')))
331 os.path.join(BINDIR, '_hg.py')))
315 f.close()
332 f.close()
316 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
333 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
317
334
318 def outputcoverage(options):
335 def outputcoverage(options):
319
336
320 vlog('# Producing coverage report')
337 vlog('# Producing coverage report')
321 os.chdir(PYTHONDIR)
338 os.chdir(PYTHONDIR)
322
339
323 def covrun(*args):
340 def covrun(*args):
324 start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
341 start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
325 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
342 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
326 vlog('# Running: %s' % cmd)
343 vlog('# Running: %s' % cmd)
327 os.system(cmd)
344 os.system(cmd)
328
345
329 omit = [BINDIR, TESTDIR, PYTHONDIR]
346 omit = [BINDIR, TESTDIR, PYTHONDIR]
330 if not options.cover_stdlib:
347 if not options.cover_stdlib:
331 # Exclude as system paths (ignoring empty strings seen on win)
348 # Exclude as system paths (ignoring empty strings seen on win)
332 omit += [x for x in sys.path if x != '']
349 omit += [x for x in sys.path if x != '']
333 omit = ','.join(omit)
350 omit = ','.join(omit)
334
351
335 covrun('-c') # combine from parallel processes
352 covrun('-c') # combine from parallel processes
336 for fn in os.listdir(TESTDIR):
353 for fn in os.listdir(TESTDIR):
337 if fn.startswith('.coverage.'):
354 if fn.startswith('.coverage.'):
338 os.unlink(os.path.join(TESTDIR, fn))
355 os.unlink(os.path.join(TESTDIR, fn))
339
356
340 covrun('-i', '-r', '"--omit=%s"' % omit) # report
357 covrun('-i', '-r', '"--omit=%s"' % omit) # report
341 if options.annotate:
358 if options.annotate:
342 adir = os.path.join(TESTDIR, 'annotated')
359 adir = os.path.join(TESTDIR, 'annotated')
343 if not os.path.isdir(adir):
360 if not os.path.isdir(adir):
344 os.mkdir(adir)
361 os.mkdir(adir)
345 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
362 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
346
363
347 class Timeout(Exception):
364 class Timeout(Exception):
348 pass
365 pass
349
366
350 def alarmed(signum, frame):
367 def alarmed(signum, frame):
351 raise Timeout
368 raise Timeout
352
369
353 def run(cmd, options):
370 def run(cmd, options):
354 """Run command in a sub-process, capturing the output (stdout and stderr).
371 """Run command in a sub-process, capturing the output (stdout and stderr).
355 Return the exist code, and output."""
372 Return the exist code, and output."""
356 # TODO: Use subprocess.Popen if we're running on Python 2.4
373 # TODO: Use subprocess.Popen if we're running on Python 2.4
357 if os.name == 'nt' or sys.platform.startswith('java'):
374 if os.name == 'nt' or sys.platform.startswith('java'):
358 tochild, fromchild = os.popen4(cmd)
375 tochild, fromchild = os.popen4(cmd)
359 tochild.close()
376 tochild.close()
360 output = fromchild.read()
377 output = fromchild.read()
361 ret = fromchild.close()
378 ret = fromchild.close()
362 if ret == None:
379 if ret == None:
363 ret = 0
380 ret = 0
364 else:
381 else:
365 proc = Popen4(cmd)
382 proc = Popen4(cmd)
366 try:
383 try:
367 output = ''
384 output = ''
368 proc.tochild.close()
385 proc.tochild.close()
369 output = proc.fromchild.read()
386 output = proc.fromchild.read()
370 ret = proc.wait()
387 ret = proc.wait()
371 if os.WIFEXITED(ret):
388 if os.WIFEXITED(ret):
372 ret = os.WEXITSTATUS(ret)
389 ret = os.WEXITSTATUS(ret)
373 except Timeout:
390 except Timeout:
374 vlog('# Process %d timed out - killing it' % proc.pid)
391 vlog('# Process %d timed out - killing it' % proc.pid)
375 os.kill(proc.pid, signal.SIGTERM)
392 os.kill(proc.pid, signal.SIGTERM)
376 ret = proc.wait()
393 ret = proc.wait()
377 if ret == 0:
394 if ret == 0:
378 ret = signal.SIGTERM << 8
395 ret = signal.SIGTERM << 8
379 output += ("\n### Abort: timeout after %d seconds.\n"
396 output += ("\n### Abort: timeout after %d seconds.\n"
380 % options.timeout)
397 % options.timeout)
381 return ret, splitnewlines(output)
398 return ret, splitnewlines(output)
382
399
383 def runone(options, test, skips, fails):
400 def runone(options, test, skips, fails):
384 '''tristate output:
401 '''tristate output:
385 None -> skipped
402 None -> skipped
386 True -> passed
403 True -> passed
387 False -> failed'''
404 False -> failed'''
388
405
389 def skip(msg):
406 def skip(msg):
390 if not options.verbose:
407 if not options.verbose:
391 skips.append((test, msg))
408 skips.append((test, msg))
392 else:
409 else:
393 print "\nSkipping %s: %s" % (test, msg)
410 print "\nSkipping %s: %s" % (test, msg)
394 return None
411 return None
395
412
396 def fail(msg):
413 def fail(msg):
397 fails.append((test, msg))
414 fails.append((test, msg))
398 if not options.nodiff:
415 if not options.nodiff:
399 print "\nERROR: %s %s" % (test, msg)
416 print "\nERROR: %s %s" % (test, msg)
400 return None
417 return None
401
418
402 vlog("# Test", test)
419 vlog("# Test", test)
403
420
404 # create a fresh hgrc
421 # create a fresh hgrc
405 hgrc = file(HGRCPATH, 'w+')
422 hgrc = file(HGRCPATH, 'w+')
406 hgrc.write('[ui]\n')
423 hgrc.write('[ui]\n')
407 hgrc.write('slash = True\n')
424 hgrc.write('slash = True\n')
408 hgrc.write('[defaults]\n')
425 hgrc.write('[defaults]\n')
409 hgrc.write('backout = -d "0 0"\n')
426 hgrc.write('backout = -d "0 0"\n')
410 hgrc.write('commit = -d "0 0"\n')
427 hgrc.write('commit = -d "0 0"\n')
411 hgrc.write('tag = -d "0 0"\n')
428 hgrc.write('tag = -d "0 0"\n')
412 hgrc.close()
429 hgrc.close()
413
430
414 err = os.path.join(TESTDIR, test+".err")
431 err = os.path.join(TESTDIR, test+".err")
415 ref = os.path.join(TESTDIR, test+".out")
432 ref = os.path.join(TESTDIR, test+".out")
416 testpath = os.path.join(TESTDIR, test)
433 testpath = os.path.join(TESTDIR, test)
417
434
418 if os.path.exists(err):
435 if os.path.exists(err):
419 os.remove(err) # Remove any previous output files
436 os.remove(err) # Remove any previous output files
420
437
421 # Make a tmp subdirectory to work in
438 # Make a tmp subdirectory to work in
422 tmpd = os.path.join(HGTMP, test)
439 tmpd = os.path.join(HGTMP, test)
423 os.mkdir(tmpd)
440 os.mkdir(tmpd)
424 os.chdir(tmpd)
441 os.chdir(tmpd)
425
442
426 try:
443 try:
427 tf = open(testpath)
444 tf = open(testpath)
428 firstline = tf.readline().rstrip()
445 firstline = tf.readline().rstrip()
429 tf.close()
446 tf.close()
430 except:
447 except:
431 firstline = ''
448 firstline = ''
432 lctest = test.lower()
449 lctest = test.lower()
433
450
434 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
451 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
435 cmd = '%s "%s"' % (PYTHON, testpath)
452 py3kswitch = options.py3k_warnings and ' -3' or ''
453 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, testpath)
436 elif lctest.endswith('.bat'):
454 elif lctest.endswith('.bat'):
437 # do not run batch scripts on non-windows
455 # do not run batch scripts on non-windows
438 if os.name != 'nt':
456 if os.name != 'nt':
439 return skip("batch script")
457 return skip("batch script")
440 # To reliably get the error code from batch files on WinXP,
458 # To reliably get the error code from batch files on WinXP,
441 # the "cmd /c call" prefix is needed. Grrr
459 # the "cmd /c call" prefix is needed. Grrr
442 cmd = 'cmd /c call "%s"' % testpath
460 cmd = 'cmd /c call "%s"' % testpath
443 else:
461 else:
444 # do not run shell scripts on windows
462 # do not run shell scripts on windows
445 if os.name == 'nt':
463 if os.name == 'nt':
446 return skip("shell script")
464 return skip("shell script")
447 # do not try to run non-executable programs
465 # do not try to run non-executable programs
448 if not os.path.exists(testpath):
466 if not os.path.exists(testpath):
449 return fail("does not exist")
467 return fail("does not exist")
450 elif not os.access(testpath, os.X_OK):
468 elif not os.access(testpath, os.X_OK):
451 return skip("not executable")
469 return skip("not executable")
452 cmd = '"%s"' % testpath
470 cmd = '"%s"' % testpath
453
471
454 if options.timeout > 0:
472 if options.timeout > 0:
455 signal.alarm(options.timeout)
473 signal.alarm(options.timeout)
456
474
457 vlog("# Running", cmd)
475 vlog("# Running", cmd)
458 ret, out = run(cmd, options)
476 ret, out = run(cmd, options)
459 vlog("# Ret was:", ret)
477 vlog("# Ret was:", ret)
460
478
461 if options.timeout > 0:
479 if options.timeout > 0:
462 signal.alarm(0)
480 signal.alarm(0)
463
481
464 mark = '.'
482 mark = '.'
465
483
466 skipped = (ret == SKIPPED_STATUS)
484 skipped = (ret == SKIPPED_STATUS)
467 # If reference output file exists, check test output against it
485 # If reference output file exists, check test output against it
468 if os.path.exists(ref):
486 if os.path.exists(ref):
469 f = open(ref, "r")
487 f = open(ref, "r")
470 refout = splitnewlines(f.read())
488 refout = splitnewlines(f.read())
471 f.close()
489 f.close()
472 else:
490 else:
473 refout = []
491 refout = []
474 if skipped:
492 if skipped:
475 mark = 's'
493 mark = 's'
476 missing, failed = parsehghaveoutput(out)
494 missing, failed = parsehghaveoutput(out)
477 if not missing:
495 if not missing:
478 missing = ['irrelevant']
496 missing = ['irrelevant']
479 if failed:
497 if failed:
480 fail("hghave failed checking for %s" % failed[-1])
498 fail("hghave failed checking for %s" % failed[-1])
481 skipped = False
499 skipped = False
482 else:
500 else:
483 skip(missing[-1])
501 skip(missing[-1])
484 elif out != refout:
502 elif out != refout:
485 mark = '!'
503 mark = '!'
486 if ret:
504 if ret:
487 fail("output changed and returned error code %d" % ret)
505 fail("output changed and returned error code %d" % ret)
488 else:
506 else:
489 fail("output changed")
507 fail("output changed")
490 if not options.nodiff:
508 if not options.nodiff:
491 showdiff(refout, out)
509 showdiff(refout, out)
492 ret = 1
510 ret = 1
493 elif ret:
511 elif ret:
494 mark = '!'
512 mark = '!'
495 fail("returned error code %d" % ret)
513 fail("returned error code %d" % ret)
496
514
497 if not options.verbose:
515 if not options.verbose:
498 sys.stdout.write(mark)
516 sys.stdout.write(mark)
499 sys.stdout.flush()
517 sys.stdout.flush()
500
518
501 if ret != 0 and not skipped:
519 if ret != 0 and not skipped:
502 # Save errors to a file for diagnosis
520 # Save errors to a file for diagnosis
503 f = open(err, "wb")
521 f = open(err, "wb")
504 for line in out:
522 for line in out:
505 f.write(line)
523 f.write(line)
506 f.close()
524 f.close()
507
525
508 # Kill off any leftover daemon processes
526 # Kill off any leftover daemon processes
509 try:
527 try:
510 fp = file(DAEMON_PIDS)
528 fp = file(DAEMON_PIDS)
511 for line in fp:
529 for line in fp:
512 try:
530 try:
513 pid = int(line)
531 pid = int(line)
514 except ValueError:
532 except ValueError:
515 continue
533 continue
516 try:
534 try:
517 os.kill(pid, 0)
535 os.kill(pid, 0)
518 vlog('# Killing daemon process %d' % pid)
536 vlog('# Killing daemon process %d' % pid)
519 os.kill(pid, signal.SIGTERM)
537 os.kill(pid, signal.SIGTERM)
520 time.sleep(0.25)
538 time.sleep(0.25)
521 os.kill(pid, 0)
539 os.kill(pid, 0)
522 vlog('# Daemon process %d is stuck - really killing it' % pid)
540 vlog('# Daemon process %d is stuck - really killing it' % pid)
523 os.kill(pid, signal.SIGKILL)
541 os.kill(pid, signal.SIGKILL)
524 except OSError, err:
542 except OSError, err:
525 if err.errno != errno.ESRCH:
543 if err.errno != errno.ESRCH:
526 raise
544 raise
527 fp.close()
545 fp.close()
528 os.unlink(DAEMON_PIDS)
546 os.unlink(DAEMON_PIDS)
529 except IOError:
547 except IOError:
530 pass
548 pass
531
549
532 os.chdir(TESTDIR)
550 os.chdir(TESTDIR)
533 if not options.keep_tmpdir:
551 if not options.keep_tmpdir:
534 shutil.rmtree(tmpd, True)
552 shutil.rmtree(tmpd, True)
535 if skipped:
553 if skipped:
536 return None
554 return None
537 return ret == 0
555 return ret == 0
538
556
539 _hgpath = None
557 _hgpath = None
540
558
541 def _gethgpath():
559 def _gethgpath():
542 """Return the path to the mercurial package that is actually found by
560 """Return the path to the mercurial package that is actually found by
543 the current Python interpreter."""
561 the current Python interpreter."""
544 global _hgpath
562 global _hgpath
545 if _hgpath is not None:
563 if _hgpath is not None:
546 return _hgpath
564 return _hgpath
547
565
548 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
566 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
549 pipe = os.popen(cmd % PYTHON)
567 pipe = os.popen(cmd % PYTHON)
550 try:
568 try:
551 _hgpath = pipe.read().strip()
569 _hgpath = pipe.read().strip()
552 finally:
570 finally:
553 pipe.close()
571 pipe.close()
554 return _hgpath
572 return _hgpath
555
573
556 def _checkhglib(verb):
574 def _checkhglib(verb):
557 """Ensure that the 'mercurial' package imported by python is
575 """Ensure that the 'mercurial' package imported by python is
558 the one we expect it to be. If not, print a warning to stderr."""
576 the one we expect it to be. If not, print a warning to stderr."""
559 expecthg = os.path.join(PYTHONDIR, 'mercurial')
577 expecthg = os.path.join(PYTHONDIR, 'mercurial')
560 actualhg = _gethgpath()
578 actualhg = _gethgpath()
561 if actualhg != expecthg:
579 if actualhg != expecthg:
562 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
580 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
563 ' (expected %s)\n'
581 ' (expected %s)\n'
564 % (verb, actualhg, expecthg))
582 % (verb, actualhg, expecthg))
565
583
566 def runchildren(options, tests):
584 def runchildren(options, tests):
567 if INST:
585 if INST:
568 installhg(options)
586 installhg(options)
569 _checkhglib("Testing")
587 _checkhglib("Testing")
570
588
571 optcopy = dict(options.__dict__)
589 optcopy = dict(options.__dict__)
572 optcopy['jobs'] = 1
590 optcopy['jobs'] = 1
573 if optcopy['with_hg'] is None:
591 if optcopy['with_hg'] is None:
574 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
592 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
575 opts = []
593 opts = []
576 for opt, value in optcopy.iteritems():
594 for opt, value in optcopy.iteritems():
577 name = '--' + opt.replace('_', '-')
595 name = '--' + opt.replace('_', '-')
578 if value is True:
596 if value is True:
579 opts.append(name)
597 opts.append(name)
580 elif value is not None:
598 elif value is not None:
581 opts.append(name + '=' + str(value))
599 opts.append(name + '=' + str(value))
582
600
583 tests.reverse()
601 tests.reverse()
584 jobs = [[] for j in xrange(options.jobs)]
602 jobs = [[] for j in xrange(options.jobs)]
585 while tests:
603 while tests:
586 for job in jobs:
604 for job in jobs:
587 if not tests: break
605 if not tests: break
588 job.append(tests.pop())
606 job.append(tests.pop())
589 fps = {}
607 fps = {}
590 for j, job in enumerate(jobs):
608 for j, job in enumerate(jobs):
591 if not job:
609 if not job:
592 continue
610 continue
593 rfd, wfd = os.pipe()
611 rfd, wfd = os.pipe()
594 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
612 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
595 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
613 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
596 vlog(' '.join(cmdline))
614 vlog(' '.join(cmdline))
597 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
615 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
598 os.close(wfd)
616 os.close(wfd)
599 failures = 0
617 failures = 0
600 tested, skipped, failed = 0, 0, 0
618 tested, skipped, failed = 0, 0, 0
601 skips = []
619 skips = []
602 fails = []
620 fails = []
603 while fps:
621 while fps:
604 pid, status = os.wait()
622 pid, status = os.wait()
605 fp = fps.pop(pid)
623 fp = fps.pop(pid)
606 l = fp.read().splitlines()
624 l = fp.read().splitlines()
607 test, skip, fail = map(int, l[:3])
625 test, skip, fail = map(int, l[:3])
608 split = -fail or len(l)
626 split = -fail or len(l)
609 for s in l[3:split]:
627 for s in l[3:split]:
610 skips.append(s.split(" ", 1))
628 skips.append(s.split(" ", 1))
611 for s in l[split:]:
629 for s in l[split:]:
612 fails.append(s.split(" ", 1))
630 fails.append(s.split(" ", 1))
613 tested += test
631 tested += test
614 skipped += skip
632 skipped += skip
615 failed += fail
633 failed += fail
616 vlog('pid %d exited, status %d' % (pid, status))
634 vlog('pid %d exited, status %d' % (pid, status))
617 failures |= status
635 failures |= status
618 print
636 print
619 for s in skips:
637 for s in skips:
620 print "Skipped %s: %s" % (s[0], s[1])
638 print "Skipped %s: %s" % (s[0], s[1])
621 for s in fails:
639 for s in fails:
622 print "Failed %s: %s" % (s[0], s[1])
640 print "Failed %s: %s" % (s[0], s[1])
623
641
624 _checkhglib("Tested")
642 _checkhglib("Tested")
625 print "# Ran %d tests, %d skipped, %d failed." % (
643 print "# Ran %d tests, %d skipped, %d failed." % (
626 tested, skipped, failed)
644 tested, skipped, failed)
627 sys.exit(failures != 0)
645 sys.exit(failures != 0)
628
646
629 def runtests(options, tests):
647 def runtests(options, tests):
630 global DAEMON_PIDS, HGRCPATH
648 global DAEMON_PIDS, HGRCPATH
631 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
649 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
632 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
650 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
633
651
634 try:
652 try:
635 if INST:
653 if INST:
636 installhg(options)
654 installhg(options)
637 _checkhglib("Testing")
655 _checkhglib("Testing")
638
656
639 if options.timeout > 0:
657 if options.timeout > 0:
640 try:
658 try:
641 signal.signal(signal.SIGALRM, alarmed)
659 signal.signal(signal.SIGALRM, alarmed)
642 vlog('# Running each test with %d second timeout' %
660 vlog('# Running each test with %d second timeout' %
643 options.timeout)
661 options.timeout)
644 except AttributeError:
662 except AttributeError:
645 print 'WARNING: cannot run tests with timeouts'
663 print 'WARNING: cannot run tests with timeouts'
646 options.timeout = 0
664 options.timeout = 0
647
665
648 tested = 0
666 tested = 0
649 failed = 0
667 failed = 0
650 skipped = 0
668 skipped = 0
651
669
652 if options.restart:
670 if options.restart:
653 orig = list(tests)
671 orig = list(tests)
654 while tests:
672 while tests:
655 if os.path.exists(tests[0] + ".err"):
673 if os.path.exists(tests[0] + ".err"):
656 break
674 break
657 tests.pop(0)
675 tests.pop(0)
658 if not tests:
676 if not tests:
659 print "running all tests"
677 print "running all tests"
660 tests = orig
678 tests = orig
661
679
662 skips = []
680 skips = []
663 fails = []
681 fails = []
664 for test in tests:
682 for test in tests:
665 if options.retest and not os.path.exists(test + ".err"):
683 if options.retest and not os.path.exists(test + ".err"):
666 skipped += 1
684 skipped += 1
667 continue
685 continue
668 ret = runone(options, test, skips, fails)
686 ret = runone(options, test, skips, fails)
669 if ret is None:
687 if ret is None:
670 skipped += 1
688 skipped += 1
671 elif not ret:
689 elif not ret:
672 if options.interactive:
690 if options.interactive:
673 print "Accept this change? [n] ",
691 print "Accept this change? [n] ",
674 answer = sys.stdin.readline().strip()
692 answer = sys.stdin.readline().strip()
675 if answer.lower() in "y yes".split():
693 if answer.lower() in "y yes".split():
676 rename(test + ".err", test + ".out")
694 rename(test + ".err", test + ".out")
677 tested += 1
695 tested += 1
678 fails.pop()
696 fails.pop()
679 continue
697 continue
680 failed += 1
698 failed += 1
681 if options.first:
699 if options.first:
682 break
700 break
683 tested += 1
701 tested += 1
684
702
685 if options.child:
703 if options.child:
686 fp = os.fdopen(options.child, 'w')
704 fp = os.fdopen(options.child, 'w')
687 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
705 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
688 for s in skips:
706 for s in skips:
689 fp.write("%s %s\n" % s)
707 fp.write("%s %s\n" % s)
690 for s in fails:
708 for s in fails:
691 fp.write("%s %s\n" % s)
709 fp.write("%s %s\n" % s)
692 fp.close()
710 fp.close()
693 else:
711 else:
694 print
712 print
695 for s in skips:
713 for s in skips:
696 print "Skipped %s: %s" % s
714 print "Skipped %s: %s" % s
697 for s in fails:
715 for s in fails:
698 print "Failed %s: %s" % s
716 print "Failed %s: %s" % s
699 _checkhglib("Tested")
717 _checkhglib("Tested")
700 print "# Ran %d tests, %d skipped, %d failed." % (
718 print "# Ran %d tests, %d skipped, %d failed." % (
701 tested, skipped, failed)
719 tested, skipped, failed)
702
720
703 if options.anycoverage:
721 if options.anycoverage:
704 outputcoverage(options)
722 outputcoverage(options)
705 except KeyboardInterrupt:
723 except KeyboardInterrupt:
706 failed = True
724 failed = True
707 print "\ninterrupted!"
725 print "\ninterrupted!"
708
726
709 if failed:
727 if failed:
710 sys.exit(1)
728 sys.exit(1)
711
729
712 def main():
730 def main():
713 (options, args) = parseargs()
731 (options, args) = parseargs()
714 if not options.child:
732 if not options.child:
715 os.umask(022)
733 os.umask(022)
716
734
717 checktools()
735 checktools()
718
736
719 # Reset some environment variables to well-known values so that
737 # Reset some environment variables to well-known values so that
720 # the tests produce repeatable output.
738 # the tests produce repeatable output.
721 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
739 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
722 os.environ['TZ'] = 'GMT'
740 os.environ['TZ'] = 'GMT'
723 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
741 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
724 os.environ['CDPATH'] = ''
742 os.environ['CDPATH'] = ''
725
743
726 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
744 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
727 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
745 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
728 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
746 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
729 options.tmpdir))
747 options.tmpdir))
730 DAEMON_PIDS = None
748 DAEMON_PIDS = None
731 HGRCPATH = None
749 HGRCPATH = None
732
750
733 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
751 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
734 os.environ["HGMERGE"] = "internal:merge"
752 os.environ["HGMERGE"] = "internal:merge"
735 os.environ["HGUSER"] = "test"
753 os.environ["HGUSER"] = "test"
736 os.environ["HGENCODING"] = "ascii"
754 os.environ["HGENCODING"] = "ascii"
737 os.environ["HGENCODINGMODE"] = "strict"
755 os.environ["HGENCODINGMODE"] = "strict"
738 os.environ["HGPORT"] = str(options.port)
756 os.environ["HGPORT"] = str(options.port)
739 os.environ["HGPORT1"] = str(options.port + 1)
757 os.environ["HGPORT1"] = str(options.port + 1)
740 os.environ["HGPORT2"] = str(options.port + 2)
758 os.environ["HGPORT2"] = str(options.port + 2)
741
759
742 if options.with_hg:
760 if options.with_hg:
743 INST = None
761 INST = None
744 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
762 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
745
763
746 # This looks redundant with how Python initializes sys.path from
764 # This looks redundant with how Python initializes sys.path from
747 # the location of the script being executed. Needed because the
765 # the location of the script being executed. Needed because the
748 # "hg" specified by --with-hg is not the only Python script
766 # "hg" specified by --with-hg is not the only Python script
749 # executed in the test suite that needs to import 'mercurial'
767 # executed in the test suite that needs to import 'mercurial'
750 # ... which means it's not really redundant at all.
768 # ... which means it's not really redundant at all.
751 PYTHONDIR = BINDIR
769 PYTHONDIR = BINDIR
752 else:
770 else:
753 INST = os.path.join(HGTMP, "install")
771 INST = os.path.join(HGTMP, "install")
754 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
772 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
755 PYTHONDIR = os.path.join(INST, "lib", "python")
773 PYTHONDIR = os.path.join(INST, "lib", "python")
756
774
757 os.environ["BINDIR"] = BINDIR
775 os.environ["BINDIR"] = BINDIR
758 os.environ["PYTHON"] = PYTHON
776 os.environ["PYTHON"] = PYTHON
759
777
760 if not options.child:
778 if not options.child:
761 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
779 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
762 os.environ["PATH"] = os.pathsep.join(path)
780 os.environ["PATH"] = os.pathsep.join(path)
763
781
764 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
782 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
765 # can run .../tests/run-tests.py test-foo where test-foo
783 # can run .../tests/run-tests.py test-foo where test-foo
766 # adds an extension to HGRC
784 # adds an extension to HGRC
767 pypath = [PYTHONDIR, TESTDIR]
785 pypath = [PYTHONDIR, TESTDIR]
768 # We have to augment PYTHONPATH, rather than simply replacing
786 # We have to augment PYTHONPATH, rather than simply replacing
769 # it, in case external libraries are only available via current
787 # it, in case external libraries are only available via current
770 # PYTHONPATH. (In particular, the Subversion bindings on OS X
788 # PYTHONPATH. (In particular, the Subversion bindings on OS X
771 # are in /opt/subversion.)
789 # are in /opt/subversion.)
772 oldpypath = os.environ.get('PYTHONPATH')
790 oldpypath = os.environ.get('PYTHONPATH')
773 if oldpypath:
791 if oldpypath:
774 pypath.append(oldpypath)
792 pypath.append(oldpypath)
775 os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
793 os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
776
794
777 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
795 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
778
796
779 if len(args) == 0:
797 if len(args) == 0:
780 args = os.listdir(".")
798 args = os.listdir(".")
781 args.sort()
799 args.sort()
782
800
783 tests = []
801 tests = []
784 for test in args:
802 for test in args:
785 if (test.startswith("test-") and '~' not in test and
803 if (test.startswith("test-") and '~' not in test and
786 ('.' not in test or test.endswith('.py') or
804 ('.' not in test or test.endswith('.py') or
787 test.endswith('.bat'))):
805 test.endswith('.bat'))):
788 tests.append(test)
806 tests.append(test)
789 if not tests:
807 if not tests:
790 print "# Ran 0 tests, 0 skipped, 0 failed."
808 print "# Ran 0 tests, 0 skipped, 0 failed."
791 return
809 return
792
810
793 vlog("# Using TESTDIR", TESTDIR)
811 vlog("# Using TESTDIR", TESTDIR)
794 vlog("# Using HGTMP", HGTMP)
812 vlog("# Using HGTMP", HGTMP)
795 vlog("# Using PATH", os.environ["PATH"])
813 vlog("# Using PATH", os.environ["PATH"])
796 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
814 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
797
815
798 try:
816 try:
799 if len(tests) > 1 and options.jobs > 1:
817 if len(tests) > 1 and options.jobs > 1:
800 runchildren(options, tests)
818 runchildren(options, tests)
801 else:
819 else:
802 runtests(options, tests)
820 runtests(options, tests)
803 finally:
821 finally:
804 cleanup(options)
822 cleanup(options)
805
823
806 main()
824 main()
General Comments 0
You need to be logged in to leave comments. Login now