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