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