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