##// END OF EJS Templates
run-tests: use os.path.realpath() to find hg's setup.py....
Greg Ward -
r8943:09ff905c default
parent child Browse files
Show More
@@ -1,804 +1,806 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 script = os.path.realpath(sys.argv[0])
270 hgroot = os.path.dirname(os.path.dirname(script))
271 os.chdir(hgroot)
270 cmd = ('%s setup.py %s clean --all'
272 cmd = ('%s setup.py %s clean --all'
271 ' install --force --prefix="%s" --install-lib="%s"'
273 ' install --force --prefix="%s" --install-lib="%s"'
272 ' --install-scripts="%s" >%s 2>&1'
274 ' --install-scripts="%s" >%s 2>&1'
273 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
275 % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
274 vlog("# Running", cmd)
276 vlog("# Running", cmd)
275 if os.system(cmd) == 0:
277 if os.system(cmd) == 0:
276 if not options.verbose:
278 if not options.verbose:
277 os.remove(installerrs)
279 os.remove(installerrs)
278 else:
280 else:
279 f = open(installerrs)
281 f = open(installerrs)
280 for line in f:
282 for line in f:
281 print line,
283 print line,
282 f.close()
284 f.close()
283 sys.exit(1)
285 sys.exit(1)
284 os.chdir(TESTDIR)
286 os.chdir(TESTDIR)
285
287
286 usecorrectpython()
288 usecorrectpython()
287
289
288 vlog("# Installing dummy diffstat")
290 vlog("# Installing dummy diffstat")
289 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
291 f = open(os.path.join(BINDIR, 'diffstat'), 'w')
290 f.write('#!' + sys.executable + '\n'
292 f.write('#!' + sys.executable + '\n'
291 'import sys\n'
293 'import sys\n'
292 'files = 0\n'
294 'files = 0\n'
293 'for line in sys.stdin:\n'
295 'for line in sys.stdin:\n'
294 ' if line.startswith("diff "):\n'
296 ' if line.startswith("diff "):\n'
295 ' files += 1\n'
297 ' files += 1\n'
296 'sys.stdout.write("files patched: %d\\n" % files)\n')
298 'sys.stdout.write("files patched: %d\\n" % files)\n')
297 f.close()
299 f.close()
298 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
300 os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
299
301
300 if options.anycoverage:
302 if options.anycoverage:
301 vlog("# Installing coverage wrapper")
303 vlog("# Installing coverage wrapper")
302 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
304 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
303 if os.path.exists(COVERAGE_FILE):
305 if os.path.exists(COVERAGE_FILE):
304 os.unlink(COVERAGE_FILE)
306 os.unlink(COVERAGE_FILE)
305 # Create a wrapper script to invoke hg via coverage.py
307 # Create a wrapper script to invoke hg via coverage.py
306 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
308 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
307 f = open(os.path.join(BINDIR, 'hg'), 'w')
309 f = open(os.path.join(BINDIR, 'hg'), 'w')
308 f.write('#!' + sys.executable + '\n')
310 f.write('#!' + sys.executable + '\n')
309 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
311 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
310 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
312 '"%s", "-x", "-p", "%s"] + sys.argv[1:])\n' %
311 (os.path.join(TESTDIR, 'coverage.py'),
313 (os.path.join(TESTDIR, 'coverage.py'),
312 os.path.join(BINDIR, '_hg.py')))
314 os.path.join(BINDIR, '_hg.py')))
313 f.close()
315 f.close()
314 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
316 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
315
317
316 def outputcoverage(options):
318 def outputcoverage(options):
317
319
318 vlog('# Producing coverage report')
320 vlog('# Producing coverage report')
319 os.chdir(PYTHONDIR)
321 os.chdir(PYTHONDIR)
320
322
321 def covrun(*args):
323 def covrun(*args):
322 start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
324 start = sys.executable, os.path.join(TESTDIR, 'coverage.py')
323 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
325 cmd = '"%s" "%s" %s' % (start[0], start[1], ' '.join(args))
324 vlog('# Running: %s' % cmd)
326 vlog('# Running: %s' % cmd)
325 os.system(cmd)
327 os.system(cmd)
326
328
327 omit = [BINDIR, TESTDIR, PYTHONDIR]
329 omit = [BINDIR, TESTDIR, PYTHONDIR]
328 if not options.cover_stdlib:
330 if not options.cover_stdlib:
329 # Exclude as system paths (ignoring empty strings seen on win)
331 # Exclude as system paths (ignoring empty strings seen on win)
330 omit += [x for x in sys.path if x != '']
332 omit += [x for x in sys.path if x != '']
331 omit = ','.join(omit)
333 omit = ','.join(omit)
332
334
333 covrun('-c') # combine from parallel processes
335 covrun('-c') # combine from parallel processes
334 for fn in os.listdir(TESTDIR):
336 for fn in os.listdir(TESTDIR):
335 if fn.startswith('.coverage.'):
337 if fn.startswith('.coverage.'):
336 os.unlink(os.path.join(TESTDIR, fn))
338 os.unlink(os.path.join(TESTDIR, fn))
337
339
338 covrun('-i', '-r', '"--omit=%s"' % omit) # report
340 covrun('-i', '-r', '"--omit=%s"' % omit) # report
339 if options.annotate:
341 if options.annotate:
340 adir = os.path.join(TESTDIR, 'annotated')
342 adir = os.path.join(TESTDIR, 'annotated')
341 if not os.path.isdir(adir):
343 if not os.path.isdir(adir):
342 os.mkdir(adir)
344 os.mkdir(adir)
343 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
345 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
344
346
345 class Timeout(Exception):
347 class Timeout(Exception):
346 pass
348 pass
347
349
348 def alarmed(signum, frame):
350 def alarmed(signum, frame):
349 raise Timeout
351 raise Timeout
350
352
351 def run(cmd, options):
353 def run(cmd, options):
352 """Run command in a sub-process, capturing the output (stdout and stderr).
354 """Run command in a sub-process, capturing the output (stdout and stderr).
353 Return the exist code, and output."""
355 Return the exist code, and output."""
354 # TODO: Use subprocess.Popen if we're running on Python 2.4
356 # TODO: Use subprocess.Popen if we're running on Python 2.4
355 if os.name == 'nt' or sys.platform.startswith('java'):
357 if os.name == 'nt' or sys.platform.startswith('java'):
356 tochild, fromchild = os.popen4(cmd)
358 tochild, fromchild = os.popen4(cmd)
357 tochild.close()
359 tochild.close()
358 output = fromchild.read()
360 output = fromchild.read()
359 ret = fromchild.close()
361 ret = fromchild.close()
360 if ret == None:
362 if ret == None:
361 ret = 0
363 ret = 0
362 else:
364 else:
363 proc = Popen4(cmd)
365 proc = Popen4(cmd)
364 try:
366 try:
365 output = ''
367 output = ''
366 proc.tochild.close()
368 proc.tochild.close()
367 output = proc.fromchild.read()
369 output = proc.fromchild.read()
368 ret = proc.wait()
370 ret = proc.wait()
369 if os.WIFEXITED(ret):
371 if os.WIFEXITED(ret):
370 ret = os.WEXITSTATUS(ret)
372 ret = os.WEXITSTATUS(ret)
371 except Timeout:
373 except Timeout:
372 vlog('# Process %d timed out - killing it' % proc.pid)
374 vlog('# Process %d timed out - killing it' % proc.pid)
373 os.kill(proc.pid, signal.SIGTERM)
375 os.kill(proc.pid, signal.SIGTERM)
374 ret = proc.wait()
376 ret = proc.wait()
375 if ret == 0:
377 if ret == 0:
376 ret = signal.SIGTERM << 8
378 ret = signal.SIGTERM << 8
377 output += ("\n### Abort: timeout after %d seconds.\n"
379 output += ("\n### Abort: timeout after %d seconds.\n"
378 % options.timeout)
380 % options.timeout)
379 return ret, splitnewlines(output)
381 return ret, splitnewlines(output)
380
382
381 def runone(options, test, skips, fails):
383 def runone(options, test, skips, fails):
382 '''tristate output:
384 '''tristate output:
383 None -> skipped
385 None -> skipped
384 True -> passed
386 True -> passed
385 False -> failed'''
387 False -> failed'''
386
388
387 def skip(msg):
389 def skip(msg):
388 if not options.verbose:
390 if not options.verbose:
389 skips.append((test, msg))
391 skips.append((test, msg))
390 else:
392 else:
391 print "\nSkipping %s: %s" % (test, msg)
393 print "\nSkipping %s: %s" % (test, msg)
392 return None
394 return None
393
395
394 def fail(msg):
396 def fail(msg):
395 fails.append((test, msg))
397 fails.append((test, msg))
396 if not options.nodiff:
398 if not options.nodiff:
397 print "\nERROR: %s %s" % (test, msg)
399 print "\nERROR: %s %s" % (test, msg)
398 return None
400 return None
399
401
400 vlog("# Test", test)
402 vlog("# Test", test)
401
403
402 # create a fresh hgrc
404 # create a fresh hgrc
403 hgrc = file(HGRCPATH, 'w+')
405 hgrc = file(HGRCPATH, 'w+')
404 hgrc.write('[ui]\n')
406 hgrc.write('[ui]\n')
405 hgrc.write('slash = True\n')
407 hgrc.write('slash = True\n')
406 hgrc.write('[defaults]\n')
408 hgrc.write('[defaults]\n')
407 hgrc.write('backout = -d "0 0"\n')
409 hgrc.write('backout = -d "0 0"\n')
408 hgrc.write('commit = -d "0 0"\n')
410 hgrc.write('commit = -d "0 0"\n')
409 hgrc.write('tag = -d "0 0"\n')
411 hgrc.write('tag = -d "0 0"\n')
410 hgrc.close()
412 hgrc.close()
411
413
412 err = os.path.join(TESTDIR, test+".err")
414 err = os.path.join(TESTDIR, test+".err")
413 ref = os.path.join(TESTDIR, test+".out")
415 ref = os.path.join(TESTDIR, test+".out")
414 testpath = os.path.join(TESTDIR, test)
416 testpath = os.path.join(TESTDIR, test)
415
417
416 if os.path.exists(err):
418 if os.path.exists(err):
417 os.remove(err) # Remove any previous output files
419 os.remove(err) # Remove any previous output files
418
420
419 # Make a tmp subdirectory to work in
421 # Make a tmp subdirectory to work in
420 tmpd = os.path.join(HGTMP, test)
422 tmpd = os.path.join(HGTMP, test)
421 os.mkdir(tmpd)
423 os.mkdir(tmpd)
422 os.chdir(tmpd)
424 os.chdir(tmpd)
423
425
424 try:
426 try:
425 tf = open(testpath)
427 tf = open(testpath)
426 firstline = tf.readline().rstrip()
428 firstline = tf.readline().rstrip()
427 tf.close()
429 tf.close()
428 except:
430 except:
429 firstline = ''
431 firstline = ''
430 lctest = test.lower()
432 lctest = test.lower()
431
433
432 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
434 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
433 cmd = '%s "%s"' % (PYTHON, testpath)
435 cmd = '%s "%s"' % (PYTHON, testpath)
434 elif lctest.endswith('.bat'):
436 elif lctest.endswith('.bat'):
435 # do not run batch scripts on non-windows
437 # do not run batch scripts on non-windows
436 if os.name != 'nt':
438 if os.name != 'nt':
437 return skip("batch script")
439 return skip("batch script")
438 # To reliably get the error code from batch files on WinXP,
440 # To reliably get the error code from batch files on WinXP,
439 # the "cmd /c call" prefix is needed. Grrr
441 # the "cmd /c call" prefix is needed. Grrr
440 cmd = 'cmd /c call "%s"' % testpath
442 cmd = 'cmd /c call "%s"' % testpath
441 else:
443 else:
442 # do not run shell scripts on windows
444 # do not run shell scripts on windows
443 if os.name == 'nt':
445 if os.name == 'nt':
444 return skip("shell script")
446 return skip("shell script")
445 # do not try to run non-executable programs
447 # do not try to run non-executable programs
446 if not os.path.exists(testpath):
448 if not os.path.exists(testpath):
447 return fail("does not exist")
449 return fail("does not exist")
448 elif not os.access(testpath, os.X_OK):
450 elif not os.access(testpath, os.X_OK):
449 return skip("not executable")
451 return skip("not executable")
450 cmd = '"%s"' % testpath
452 cmd = '"%s"' % testpath
451
453
452 if options.timeout > 0:
454 if options.timeout > 0:
453 signal.alarm(options.timeout)
455 signal.alarm(options.timeout)
454
456
455 vlog("# Running", cmd)
457 vlog("# Running", cmd)
456 ret, out = run(cmd, options)
458 ret, out = run(cmd, options)
457 vlog("# Ret was:", ret)
459 vlog("# Ret was:", ret)
458
460
459 if options.timeout > 0:
461 if options.timeout > 0:
460 signal.alarm(0)
462 signal.alarm(0)
461
463
462 mark = '.'
464 mark = '.'
463
465
464 skipped = (ret == SKIPPED_STATUS)
466 skipped = (ret == SKIPPED_STATUS)
465 # If reference output file exists, check test output against it
467 # If reference output file exists, check test output against it
466 if os.path.exists(ref):
468 if os.path.exists(ref):
467 f = open(ref, "r")
469 f = open(ref, "r")
468 refout = splitnewlines(f.read())
470 refout = splitnewlines(f.read())
469 f.close()
471 f.close()
470 else:
472 else:
471 refout = []
473 refout = []
472 if skipped:
474 if skipped:
473 mark = 's'
475 mark = 's'
474 missing, failed = parsehghaveoutput(out)
476 missing, failed = parsehghaveoutput(out)
475 if not missing:
477 if not missing:
476 missing = ['irrelevant']
478 missing = ['irrelevant']
477 if failed:
479 if failed:
478 fail("hghave failed checking for %s" % failed[-1])
480 fail("hghave failed checking for %s" % failed[-1])
479 skipped = False
481 skipped = False
480 else:
482 else:
481 skip(missing[-1])
483 skip(missing[-1])
482 elif out != refout:
484 elif out != refout:
483 mark = '!'
485 mark = '!'
484 if ret:
486 if ret:
485 fail("output changed and returned error code %d" % ret)
487 fail("output changed and returned error code %d" % ret)
486 else:
488 else:
487 fail("output changed")
489 fail("output changed")
488 if not options.nodiff:
490 if not options.nodiff:
489 showdiff(refout, out)
491 showdiff(refout, out)
490 ret = 1
492 ret = 1
491 elif ret:
493 elif ret:
492 mark = '!'
494 mark = '!'
493 fail("returned error code %d" % ret)
495 fail("returned error code %d" % ret)
494
496
495 if not options.verbose:
497 if not options.verbose:
496 sys.stdout.write(mark)
498 sys.stdout.write(mark)
497 sys.stdout.flush()
499 sys.stdout.flush()
498
500
499 if ret != 0 and not skipped:
501 if ret != 0 and not skipped:
500 # Save errors to a file for diagnosis
502 # Save errors to a file for diagnosis
501 f = open(err, "wb")
503 f = open(err, "wb")
502 for line in out:
504 for line in out:
503 f.write(line)
505 f.write(line)
504 f.close()
506 f.close()
505
507
506 # Kill off any leftover daemon processes
508 # Kill off any leftover daemon processes
507 try:
509 try:
508 fp = file(DAEMON_PIDS)
510 fp = file(DAEMON_PIDS)
509 for line in fp:
511 for line in fp:
510 try:
512 try:
511 pid = int(line)
513 pid = int(line)
512 except ValueError:
514 except ValueError:
513 continue
515 continue
514 try:
516 try:
515 os.kill(pid, 0)
517 os.kill(pid, 0)
516 vlog('# Killing daemon process %d' % pid)
518 vlog('# Killing daemon process %d' % pid)
517 os.kill(pid, signal.SIGTERM)
519 os.kill(pid, signal.SIGTERM)
518 time.sleep(0.25)
520 time.sleep(0.25)
519 os.kill(pid, 0)
521 os.kill(pid, 0)
520 vlog('# Daemon process %d is stuck - really killing it' % pid)
522 vlog('# Daemon process %d is stuck - really killing it' % pid)
521 os.kill(pid, signal.SIGKILL)
523 os.kill(pid, signal.SIGKILL)
522 except OSError, err:
524 except OSError, err:
523 if err.errno != errno.ESRCH:
525 if err.errno != errno.ESRCH:
524 raise
526 raise
525 fp.close()
527 fp.close()
526 os.unlink(DAEMON_PIDS)
528 os.unlink(DAEMON_PIDS)
527 except IOError:
529 except IOError:
528 pass
530 pass
529
531
530 os.chdir(TESTDIR)
532 os.chdir(TESTDIR)
531 if not options.keep_tmpdir:
533 if not options.keep_tmpdir:
532 shutil.rmtree(tmpd, True)
534 shutil.rmtree(tmpd, True)
533 if skipped:
535 if skipped:
534 return None
536 return None
535 return ret == 0
537 return ret == 0
536
538
537 _hgpath = None
539 _hgpath = None
538
540
539 def _gethgpath():
541 def _gethgpath():
540 """Return the path to the mercurial package that is actually found by
542 """Return the path to the mercurial package that is actually found by
541 the current Python interpreter."""
543 the current Python interpreter."""
542 global _hgpath
544 global _hgpath
543 if _hgpath is not None:
545 if _hgpath is not None:
544 return _hgpath
546 return _hgpath
545
547
546 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
548 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
547 pipe = os.popen(cmd % PYTHON)
549 pipe = os.popen(cmd % PYTHON)
548 try:
550 try:
549 _hgpath = pipe.read().strip()
551 _hgpath = pipe.read().strip()
550 finally:
552 finally:
551 pipe.close()
553 pipe.close()
552 return _hgpath
554 return _hgpath
553
555
554 def _checkhglib(verb):
556 def _checkhglib(verb):
555 """Ensure that the 'mercurial' package imported by python is
557 """Ensure that the 'mercurial' package imported by python is
556 the one we expect it to be. If not, print a warning to stderr."""
558 the one we expect it to be. If not, print a warning to stderr."""
557 expecthg = os.path.join(PYTHONDIR, 'mercurial')
559 expecthg = os.path.join(PYTHONDIR, 'mercurial')
558 actualhg = _gethgpath()
560 actualhg = _gethgpath()
559 if actualhg != expecthg:
561 if actualhg != expecthg:
560 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
562 sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
561 ' (expected %s)\n'
563 ' (expected %s)\n'
562 % (verb, actualhg, expecthg))
564 % (verb, actualhg, expecthg))
563
565
564 def runchildren(options, tests):
566 def runchildren(options, tests):
565 if INST:
567 if INST:
566 installhg(options)
568 installhg(options)
567 _checkhglib("Testing")
569 _checkhglib("Testing")
568
570
569 optcopy = dict(options.__dict__)
571 optcopy = dict(options.__dict__)
570 optcopy['jobs'] = 1
572 optcopy['jobs'] = 1
571 if optcopy['with_hg'] is None:
573 if optcopy['with_hg'] is None:
572 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
574 optcopy['with_hg'] = os.path.join(BINDIR, "hg")
573 opts = []
575 opts = []
574 for opt, value in optcopy.iteritems():
576 for opt, value in optcopy.iteritems():
575 name = '--' + opt.replace('_', '-')
577 name = '--' + opt.replace('_', '-')
576 if value is True:
578 if value is True:
577 opts.append(name)
579 opts.append(name)
578 elif value is not None:
580 elif value is not None:
579 opts.append(name + '=' + str(value))
581 opts.append(name + '=' + str(value))
580
582
581 tests.reverse()
583 tests.reverse()
582 jobs = [[] for j in xrange(options.jobs)]
584 jobs = [[] for j in xrange(options.jobs)]
583 while tests:
585 while tests:
584 for job in jobs:
586 for job in jobs:
585 if not tests: break
587 if not tests: break
586 job.append(tests.pop())
588 job.append(tests.pop())
587 fps = {}
589 fps = {}
588 for j, job in enumerate(jobs):
590 for j, job in enumerate(jobs):
589 if not job:
591 if not job:
590 continue
592 continue
591 rfd, wfd = os.pipe()
593 rfd, wfd = os.pipe()
592 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
594 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
593 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
595 cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
594 vlog(' '.join(cmdline))
596 vlog(' '.join(cmdline))
595 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
597 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
596 os.close(wfd)
598 os.close(wfd)
597 failures = 0
599 failures = 0
598 tested, skipped, failed = 0, 0, 0
600 tested, skipped, failed = 0, 0, 0
599 skips = []
601 skips = []
600 fails = []
602 fails = []
601 while fps:
603 while fps:
602 pid, status = os.wait()
604 pid, status = os.wait()
603 fp = fps.pop(pid)
605 fp = fps.pop(pid)
604 l = fp.read().splitlines()
606 l = fp.read().splitlines()
605 test, skip, fail = map(int, l[:3])
607 test, skip, fail = map(int, l[:3])
606 split = -fail or len(l)
608 split = -fail or len(l)
607 for s in l[3:split]:
609 for s in l[3:split]:
608 skips.append(s.split(" ", 1))
610 skips.append(s.split(" ", 1))
609 for s in l[split:]:
611 for s in l[split:]:
610 fails.append(s.split(" ", 1))
612 fails.append(s.split(" ", 1))
611 tested += test
613 tested += test
612 skipped += skip
614 skipped += skip
613 failed += fail
615 failed += fail
614 vlog('pid %d exited, status %d' % (pid, status))
616 vlog('pid %d exited, status %d' % (pid, status))
615 failures |= status
617 failures |= status
616 print
618 print
617 for s in skips:
619 for s in skips:
618 print "Skipped %s: %s" % (s[0], s[1])
620 print "Skipped %s: %s" % (s[0], s[1])
619 for s in fails:
621 for s in fails:
620 print "Failed %s: %s" % (s[0], s[1])
622 print "Failed %s: %s" % (s[0], s[1])
621
623
622 _checkhglib("Tested")
624 _checkhglib("Tested")
623 print "# Ran %d tests, %d skipped, %d failed." % (
625 print "# Ran %d tests, %d skipped, %d failed." % (
624 tested, skipped, failed)
626 tested, skipped, failed)
625 sys.exit(failures != 0)
627 sys.exit(failures != 0)
626
628
627 def runtests(options, tests):
629 def runtests(options, tests):
628 global DAEMON_PIDS, HGRCPATH
630 global DAEMON_PIDS, HGRCPATH
629 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
631 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
630 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
632 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
631
633
632 try:
634 try:
633 if INST:
635 if INST:
634 installhg(options)
636 installhg(options)
635 _checkhglib("Testing")
637 _checkhglib("Testing")
636
638
637 if options.timeout > 0:
639 if options.timeout > 0:
638 try:
640 try:
639 signal.signal(signal.SIGALRM, alarmed)
641 signal.signal(signal.SIGALRM, alarmed)
640 vlog('# Running each test with %d second timeout' %
642 vlog('# Running each test with %d second timeout' %
641 options.timeout)
643 options.timeout)
642 except AttributeError:
644 except AttributeError:
643 print 'WARNING: cannot run tests with timeouts'
645 print 'WARNING: cannot run tests with timeouts'
644 options.timeout = 0
646 options.timeout = 0
645
647
646 tested = 0
648 tested = 0
647 failed = 0
649 failed = 0
648 skipped = 0
650 skipped = 0
649
651
650 if options.restart:
652 if options.restart:
651 orig = list(tests)
653 orig = list(tests)
652 while tests:
654 while tests:
653 if os.path.exists(tests[0] + ".err"):
655 if os.path.exists(tests[0] + ".err"):
654 break
656 break
655 tests.pop(0)
657 tests.pop(0)
656 if not tests:
658 if not tests:
657 print "running all tests"
659 print "running all tests"
658 tests = orig
660 tests = orig
659
661
660 skips = []
662 skips = []
661 fails = []
663 fails = []
662 for test in tests:
664 for test in tests:
663 if options.retest and not os.path.exists(test + ".err"):
665 if options.retest and not os.path.exists(test + ".err"):
664 skipped += 1
666 skipped += 1
665 continue
667 continue
666 ret = runone(options, test, skips, fails)
668 ret = runone(options, test, skips, fails)
667 if ret is None:
669 if ret is None:
668 skipped += 1
670 skipped += 1
669 elif not ret:
671 elif not ret:
670 if options.interactive:
672 if options.interactive:
671 print "Accept this change? [n] ",
673 print "Accept this change? [n] ",
672 answer = sys.stdin.readline().strip()
674 answer = sys.stdin.readline().strip()
673 if answer.lower() in "y yes".split():
675 if answer.lower() in "y yes".split():
674 rename(test + ".err", test + ".out")
676 rename(test + ".err", test + ".out")
675 tested += 1
677 tested += 1
676 fails.pop()
678 fails.pop()
677 continue
679 continue
678 failed += 1
680 failed += 1
679 if options.first:
681 if options.first:
680 break
682 break
681 tested += 1
683 tested += 1
682
684
683 if options.child:
685 if options.child:
684 fp = os.fdopen(options.child, 'w')
686 fp = os.fdopen(options.child, 'w')
685 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
687 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
686 for s in skips:
688 for s in skips:
687 fp.write("%s %s\n" % s)
689 fp.write("%s %s\n" % s)
688 for s in fails:
690 for s in fails:
689 fp.write("%s %s\n" % s)
691 fp.write("%s %s\n" % s)
690 fp.close()
692 fp.close()
691 else:
693 else:
692 print
694 print
693 for s in skips:
695 for s in skips:
694 print "Skipped %s: %s" % s
696 print "Skipped %s: %s" % s
695 for s in fails:
697 for s in fails:
696 print "Failed %s: %s" % s
698 print "Failed %s: %s" % s
697 _checkhglib("Tested")
699 _checkhglib("Tested")
698 print "# Ran %d tests, %d skipped, %d failed." % (
700 print "# Ran %d tests, %d skipped, %d failed." % (
699 tested, skipped, failed)
701 tested, skipped, failed)
700
702
701 if options.anycoverage:
703 if options.anycoverage:
702 outputcoverage(options)
704 outputcoverage(options)
703 except KeyboardInterrupt:
705 except KeyboardInterrupt:
704 failed = True
706 failed = True
705 print "\ninterrupted!"
707 print "\ninterrupted!"
706
708
707 if failed:
709 if failed:
708 sys.exit(1)
710 sys.exit(1)
709
711
710 def main():
712 def main():
711 (options, args) = parseargs()
713 (options, args) = parseargs()
712 if not options.child:
714 if not options.child:
713 os.umask(022)
715 os.umask(022)
714
716
715 checktools()
717 checktools()
716
718
717 # Reset some environment variables to well-known values so that
719 # Reset some environment variables to well-known values so that
718 # the tests produce repeatable output.
720 # the tests produce repeatable output.
719 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
721 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
720 os.environ['TZ'] = 'GMT'
722 os.environ['TZ'] = 'GMT'
721 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
723 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
722 os.environ['CDPATH'] = ''
724 os.environ['CDPATH'] = ''
723
725
724 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
726 global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
725 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
727 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
726 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
728 HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
727 options.tmpdir))
729 options.tmpdir))
728 DAEMON_PIDS = None
730 DAEMON_PIDS = None
729 HGRCPATH = None
731 HGRCPATH = None
730
732
731 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
733 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
732 os.environ["HGMERGE"] = "internal:merge"
734 os.environ["HGMERGE"] = "internal:merge"
733 os.environ["HGUSER"] = "test"
735 os.environ["HGUSER"] = "test"
734 os.environ["HGENCODING"] = "ascii"
736 os.environ["HGENCODING"] = "ascii"
735 os.environ["HGENCODINGMODE"] = "strict"
737 os.environ["HGENCODINGMODE"] = "strict"
736 os.environ["HGPORT"] = str(options.port)
738 os.environ["HGPORT"] = str(options.port)
737 os.environ["HGPORT1"] = str(options.port + 1)
739 os.environ["HGPORT1"] = str(options.port + 1)
738 os.environ["HGPORT2"] = str(options.port + 2)
740 os.environ["HGPORT2"] = str(options.port + 2)
739
741
740 if options.with_hg:
742 if options.with_hg:
741 INST = None
743 INST = None
742 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
744 BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
743
745
744 # This looks redundant with how Python initializes sys.path from
746 # This looks redundant with how Python initializes sys.path from
745 # the location of the script being executed. Needed because the
747 # the location of the script being executed. Needed because the
746 # "hg" specified by --with-hg is not the only Python script
748 # "hg" specified by --with-hg is not the only Python script
747 # executed in the test suite that needs to import 'mercurial'
749 # executed in the test suite that needs to import 'mercurial'
748 # ... which means it's not really redundant at all.
750 # ... which means it's not really redundant at all.
749 PYTHONDIR = BINDIR
751 PYTHONDIR = BINDIR
750 else:
752 else:
751 INST = os.path.join(HGTMP, "install")
753 INST = os.path.join(HGTMP, "install")
752 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
754 BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
753 PYTHONDIR = os.path.join(INST, "lib", "python")
755 PYTHONDIR = os.path.join(INST, "lib", "python")
754
756
755 os.environ["BINDIR"] = BINDIR
757 os.environ["BINDIR"] = BINDIR
756 os.environ["PYTHON"] = PYTHON
758 os.environ["PYTHON"] = PYTHON
757
759
758 if not options.child:
760 if not options.child:
759 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
761 path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
760 os.environ["PATH"] = os.pathsep.join(path)
762 os.environ["PATH"] = os.pathsep.join(path)
761
763
762 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
764 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
763 # can run .../tests/run-tests.py test-foo where test-foo
765 # can run .../tests/run-tests.py test-foo where test-foo
764 # adds an extension to HGRC
766 # adds an extension to HGRC
765 pypath = [PYTHONDIR, TESTDIR]
767 pypath = [PYTHONDIR, TESTDIR]
766 # We have to augment PYTHONPATH, rather than simply replacing
768 # We have to augment PYTHONPATH, rather than simply replacing
767 # it, in case external libraries are only available via current
769 # it, in case external libraries are only available via current
768 # PYTHONPATH. (In particular, the Subversion bindings on OS X
770 # PYTHONPATH. (In particular, the Subversion bindings on OS X
769 # are in /opt/subversion.)
771 # are in /opt/subversion.)
770 oldpypath = os.environ.get('PYTHONPATH')
772 oldpypath = os.environ.get('PYTHONPATH')
771 if oldpypath:
773 if oldpypath:
772 pypath.append(oldpypath)
774 pypath.append(oldpypath)
773 os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
775 os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
774
776
775 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
777 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
776
778
777 if len(args) == 0:
779 if len(args) == 0:
778 args = os.listdir(".")
780 args = os.listdir(".")
779 args.sort()
781 args.sort()
780
782
781 tests = []
783 tests = []
782 for test in args:
784 for test in args:
783 if (test.startswith("test-") and '~' not in test and
785 if (test.startswith("test-") and '~' not in test and
784 ('.' not in test or test.endswith('.py') or
786 ('.' not in test or test.endswith('.py') or
785 test.endswith('.bat'))):
787 test.endswith('.bat'))):
786 tests.append(test)
788 tests.append(test)
787 if not tests:
789 if not tests:
788 print "# Ran 0 tests, 0 skipped, 0 failed."
790 print "# Ran 0 tests, 0 skipped, 0 failed."
789 return
791 return
790
792
791 vlog("# Using TESTDIR", TESTDIR)
793 vlog("# Using TESTDIR", TESTDIR)
792 vlog("# Using HGTMP", HGTMP)
794 vlog("# Using HGTMP", HGTMP)
793 vlog("# Using PATH", os.environ["PATH"])
795 vlog("# Using PATH", os.environ["PATH"])
794 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
796 vlog("# Using PYTHONPATH", os.environ["PYTHONPATH"])
795
797
796 try:
798 try:
797 if len(tests) > 1 and options.jobs > 1:
799 if len(tests) > 1 and options.jobs > 1:
798 runchildren(options, tests)
800 runchildren(options, tests)
799 else:
801 else:
800 runtests(options, tests)
802 runtests(options, tests)
801 finally:
803 finally:
802 cleanup(options)
804 cleanup(options)
803
805
804 main()
806 main()
General Comments 0
You need to be logged in to leave comments. Login now