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