##// END OF EJS Templates
run-tests.py: add a default --date "0 0" argument to commit et al...
Alexis S. L. Carvalho -
r5524:453acf64 default
parent child Browse files
Show More
@@ -1,576 +1,581 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
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
8 # of the GNU General Public License, incorporated herein by reference.
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import optparse
12 import optparse
13 import os
13 import os
14 import popen2
14 import popen2
15 import re
15 import re
16 import shutil
16 import shutil
17 import signal
17 import signal
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 import time
20 import time
21
21
22 # hghave reserved exit code to skip test
22 # hghave reserved exit code to skip test
23 SKIPPED_STATUS = 80
23 SKIPPED_STATUS = 80
24
24
25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
26
26
27 parser = optparse.OptionParser("%prog [options] [tests]")
27 parser = optparse.OptionParser("%prog [options] [tests]")
28 parser.add_option("-C", "--annotate", action="store_true",
28 parser.add_option("-C", "--annotate", action="store_true",
29 help="output files annotated with coverage")
29 help="output files annotated with coverage")
30 parser.add_option("--child", type="int",
30 parser.add_option("--child", type="int",
31 help="run as child process, summary to given fd")
31 help="run as child process, summary to given fd")
32 parser.add_option("-c", "--cover", action="store_true",
32 parser.add_option("-c", "--cover", action="store_true",
33 help="print a test coverage report")
33 help="print a test coverage report")
34 parser.add_option("-f", "--first", action="store_true",
34 parser.add_option("-f", "--first", action="store_true",
35 help="exit on the first test failure")
35 help="exit on the first test failure")
36 parser.add_option("-i", "--interactive", action="store_true",
36 parser.add_option("-i", "--interactive", action="store_true",
37 help="prompt to accept changed output")
37 help="prompt to accept changed output")
38 parser.add_option("-j", "--jobs", type="int",
38 parser.add_option("-j", "--jobs", type="int",
39 help="number of jobs to run in parallel")
39 help="number of jobs to run in parallel")
40 parser.add_option("-R", "--restart", action="store_true",
40 parser.add_option("-R", "--restart", action="store_true",
41 help="restart at last error")
41 help="restart at last error")
42 parser.add_option("-p", "--port", type="int",
42 parser.add_option("-p", "--port", type="int",
43 help="port on which servers should listen")
43 help="port on which servers should listen")
44 parser.add_option("-r", "--retest", action="store_true",
44 parser.add_option("-r", "--retest", action="store_true",
45 help="retest failed tests")
45 help="retest failed tests")
46 parser.add_option("-s", "--cover_stdlib", action="store_true",
46 parser.add_option("-s", "--cover_stdlib", action="store_true",
47 help="print a test coverage report inc. standard libraries")
47 help="print a test coverage report inc. standard libraries")
48 parser.add_option("-t", "--timeout", type="int",
48 parser.add_option("-t", "--timeout", type="int",
49 help="kill errant tests after TIMEOUT seconds")
49 help="kill errant tests after TIMEOUT seconds")
50 parser.add_option("--tmpdir", type="string",
50 parser.add_option("--tmpdir", type="string",
51 help="run tests in the given temporary directory")
51 help="run tests in the given temporary directory")
52 parser.add_option("-v", "--verbose", action="store_true",
52 parser.add_option("-v", "--verbose", action="store_true",
53 help="output verbose messages")
53 help="output verbose messages")
54 parser.add_option("--with-hg", type="string",
54 parser.add_option("--with-hg", type="string",
55 help="test existing install at given location")
55 help="test existing install at given location")
56
56
57 parser.set_defaults(jobs=1, port=20059, timeout=180)
57 parser.set_defaults(jobs=1, port=20059, timeout=180)
58 (options, args) = parser.parse_args()
58 (options, args) = parser.parse_args()
59 verbose = options.verbose
59 verbose = options.verbose
60 coverage = options.cover or options.cover_stdlib or options.annotate
60 coverage = options.cover or options.cover_stdlib or options.annotate
61 python = sys.executable
61 python = sys.executable
62
62
63 if options.jobs < 1:
63 if options.jobs < 1:
64 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
64 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
65 sys.exit(1)
65 sys.exit(1)
66 if options.interactive and options.jobs > 1:
66 if options.interactive and options.jobs > 1:
67 print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1'
67 print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1'
68 sys.exit(1)
68 sys.exit(1)
69
69
70 def vlog(*msg):
70 def vlog(*msg):
71 if verbose:
71 if verbose:
72 for m in msg:
72 for m in msg:
73 print m,
73 print m,
74 print
74 print
75
75
76 def splitnewlines(text):
76 def splitnewlines(text):
77 '''like str.splitlines, but only split on newlines.
77 '''like str.splitlines, but only split on newlines.
78 keep line endings.'''
78 keep line endings.'''
79 i = 0
79 i = 0
80 lines = []
80 lines = []
81 while True:
81 while True:
82 n = text.find('\n', i)
82 n = text.find('\n', i)
83 if n == -1:
83 if n == -1:
84 last = text[i:]
84 last = text[i:]
85 if last:
85 if last:
86 lines.append(last)
86 lines.append(last)
87 return lines
87 return lines
88 lines.append(text[i:n+1])
88 lines.append(text[i:n+1])
89 i = n + 1
89 i = n + 1
90
90
91 def extract_missing_features(lines):
91 def extract_missing_features(lines):
92 '''Extract missing/unknown features log lines as a list'''
92 '''Extract missing/unknown features log lines as a list'''
93 missing = []
93 missing = []
94 for line in lines:
94 for line in lines:
95 if not line.startswith('hghave: '):
95 if not line.startswith('hghave: '):
96 continue
96 continue
97 line = line.splitlines()[0]
97 line = line.splitlines()[0]
98 missing.append(line[8:])
98 missing.append(line[8:])
99
99
100 return missing
100 return missing
101
101
102 def show_diff(expected, output):
102 def show_diff(expected, output):
103 for line in difflib.unified_diff(expected, output,
103 for line in difflib.unified_diff(expected, output,
104 "Expected output", "Test output"):
104 "Expected output", "Test output"):
105 sys.stdout.write(line)
105 sys.stdout.write(line)
106
106
107 def find_program(program):
107 def find_program(program):
108 """Search PATH for a executable program"""
108 """Search PATH for a executable program"""
109 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
109 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
110 name = os.path.join(p, program)
110 name = os.path.join(p, program)
111 if os.access(name, os.X_OK):
111 if os.access(name, os.X_OK):
112 return name
112 return name
113 return None
113 return None
114
114
115 def check_required_tools():
115 def check_required_tools():
116 # Before we go any further, check for pre-requisite tools
116 # Before we go any further, check for pre-requisite tools
117 # stuff from coreutils (cat, rm, etc) are not tested
117 # stuff from coreutils (cat, rm, etc) are not tested
118 for p in required_tools:
118 for p in required_tools:
119 if os.name == 'nt':
119 if os.name == 'nt':
120 p += '.exe'
120 p += '.exe'
121 found = find_program(p)
121 found = find_program(p)
122 if found:
122 if found:
123 vlog("# Found prerequisite", p, "at", found)
123 vlog("# Found prerequisite", p, "at", found)
124 else:
124 else:
125 print "WARNING: Did not find prerequisite tool: "+p
125 print "WARNING: Did not find prerequisite tool: "+p
126
126
127 def cleanup_exit():
127 def cleanup_exit():
128 if verbose:
128 if verbose:
129 print "# Cleaning up HGTMP", HGTMP
129 print "# Cleaning up HGTMP", HGTMP
130 shutil.rmtree(HGTMP, True)
130 shutil.rmtree(HGTMP, True)
131
131
132 def use_correct_python():
132 def use_correct_python():
133 # some tests run python interpreter. they must use same
133 # some tests run python interpreter. they must use same
134 # interpreter we use or bad things will happen.
134 # interpreter we use or bad things will happen.
135 exedir, exename = os.path.split(sys.executable)
135 exedir, exename = os.path.split(sys.executable)
136 if exename == 'python':
136 if exename == 'python':
137 path = find_program('python')
137 path = find_program('python')
138 if os.path.dirname(path) == exedir:
138 if os.path.dirname(path) == exedir:
139 return
139 return
140 vlog('# Making python executable in test path use correct Python')
140 vlog('# Making python executable in test path use correct Python')
141 my_python = os.path.join(BINDIR, 'python')
141 my_python = os.path.join(BINDIR, 'python')
142 try:
142 try:
143 os.symlink(sys.executable, my_python)
143 os.symlink(sys.executable, my_python)
144 except AttributeError:
144 except AttributeError:
145 # windows fallback
145 # windows fallback
146 shutil.copyfile(sys.executable, my_python)
146 shutil.copyfile(sys.executable, my_python)
147 shutil.copymode(sys.executable, my_python)
147 shutil.copymode(sys.executable, my_python)
148
148
149 def install_hg():
149 def install_hg():
150 global python
150 global python
151 vlog("# Performing temporary installation of HG")
151 vlog("# Performing temporary installation of HG")
152 installerrs = os.path.join("tests", "install.err")
152 installerrs = os.path.join("tests", "install.err")
153
153
154 # Run installer in hg root
154 # Run installer in hg root
155 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
155 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
156 cmd = ('%s setup.py clean --all'
156 cmd = ('%s setup.py clean --all'
157 ' install --force --home="%s" --install-lib="%s"'
157 ' install --force --home="%s" --install-lib="%s"'
158 ' --install-scripts="%s" >%s 2>&1'
158 ' --install-scripts="%s" >%s 2>&1'
159 % (sys.executable, INST, PYTHONDIR, BINDIR, installerrs))
159 % (sys.executable, INST, PYTHONDIR, BINDIR, installerrs))
160 vlog("# Running", cmd)
160 vlog("# Running", cmd)
161 if os.system(cmd) == 0:
161 if os.system(cmd) == 0:
162 if not verbose:
162 if not verbose:
163 os.remove(installerrs)
163 os.remove(installerrs)
164 else:
164 else:
165 f = open(installerrs)
165 f = open(installerrs)
166 for line in f:
166 for line in f:
167 print line,
167 print line,
168 f.close()
168 f.close()
169 sys.exit(1)
169 sys.exit(1)
170 os.chdir(TESTDIR)
170 os.chdir(TESTDIR)
171
171
172 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
172 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
173
173
174 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
174 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
175 pythonpath = os.environ.get("PYTHONPATH")
175 pythonpath = os.environ.get("PYTHONPATH")
176 if pythonpath:
176 if pythonpath:
177 pythonpath = pydir + os.pathsep + pythonpath
177 pythonpath = pydir + os.pathsep + pythonpath
178 else:
178 else:
179 pythonpath = pydir
179 pythonpath = pydir
180 os.environ["PYTHONPATH"] = pythonpath
180 os.environ["PYTHONPATH"] = pythonpath
181
181
182 use_correct_python()
182 use_correct_python()
183
183
184 if coverage:
184 if coverage:
185 vlog("# Installing coverage wrapper")
185 vlog("# Installing coverage wrapper")
186 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
186 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
187 if os.path.exists(COVERAGE_FILE):
187 if os.path.exists(COVERAGE_FILE):
188 os.unlink(COVERAGE_FILE)
188 os.unlink(COVERAGE_FILE)
189 # Create a wrapper script to invoke hg via coverage.py
189 # Create a wrapper script to invoke hg via coverage.py
190 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
190 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
191 f = open(os.path.join(BINDIR, 'hg'), 'w')
191 f = open(os.path.join(BINDIR, 'hg'), 'w')
192 f.write('#!' + sys.executable + '\n')
192 f.write('#!' + sys.executable + '\n')
193 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
193 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
194 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
194 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
195 (os.path.join(TESTDIR, 'coverage.py'),
195 (os.path.join(TESTDIR, 'coverage.py'),
196 os.path.join(BINDIR, '_hg.py')))
196 os.path.join(BINDIR, '_hg.py')))
197 f.close()
197 f.close()
198 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
198 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
199 python = '"%s" "%s" -x' % (sys.executable,
199 python = '"%s" "%s" -x' % (sys.executable,
200 os.path.join(TESTDIR,'coverage.py'))
200 os.path.join(TESTDIR,'coverage.py'))
201
201
202 def output_coverage():
202 def output_coverage():
203 vlog("# Producing coverage report")
203 vlog("# Producing coverage report")
204 omit = [BINDIR, TESTDIR, PYTHONDIR]
204 omit = [BINDIR, TESTDIR, PYTHONDIR]
205 if not options.cover_stdlib:
205 if not options.cover_stdlib:
206 # Exclude as system paths (ignoring empty strings seen on win)
206 # Exclude as system paths (ignoring empty strings seen on win)
207 omit += [x for x in sys.path if x != '']
207 omit += [x for x in sys.path if x != '']
208 omit = ','.join(omit)
208 omit = ','.join(omit)
209 os.chdir(PYTHONDIR)
209 os.chdir(PYTHONDIR)
210 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
210 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
211 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
211 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
212 vlog("# Running: "+cmd)
212 vlog("# Running: "+cmd)
213 os.system(cmd)
213 os.system(cmd)
214 if options.annotate:
214 if options.annotate:
215 adir = os.path.join(TESTDIR, 'annotated')
215 adir = os.path.join(TESTDIR, 'annotated')
216 if not os.path.isdir(adir):
216 if not os.path.isdir(adir):
217 os.mkdir(adir)
217 os.mkdir(adir)
218 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
218 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
219 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
219 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
220 adir, omit)
220 adir, omit)
221 vlog("# Running: "+cmd)
221 vlog("# Running: "+cmd)
222 os.system(cmd)
222 os.system(cmd)
223
223
224 class Timeout(Exception):
224 class Timeout(Exception):
225 pass
225 pass
226
226
227 def alarmed(signum, frame):
227 def alarmed(signum, frame):
228 raise Timeout
228 raise Timeout
229
229
230 def run(cmd):
230 def run(cmd):
231 """Run command in a sub-process, capturing the output (stdout and stderr).
231 """Run command in a sub-process, capturing the output (stdout and stderr).
232 Return the exist code, and output."""
232 Return the exist code, and output."""
233 # TODO: Use subprocess.Popen if we're running on Python 2.4
233 # TODO: Use subprocess.Popen if we're running on Python 2.4
234 if os.name == 'nt':
234 if os.name == 'nt':
235 tochild, fromchild = os.popen4(cmd)
235 tochild, fromchild = os.popen4(cmd)
236 tochild.close()
236 tochild.close()
237 output = fromchild.read()
237 output = fromchild.read()
238 ret = fromchild.close()
238 ret = fromchild.close()
239 if ret == None:
239 if ret == None:
240 ret = 0
240 ret = 0
241 else:
241 else:
242 proc = popen2.Popen4(cmd)
242 proc = popen2.Popen4(cmd)
243 try:
243 try:
244 output = ''
244 output = ''
245 proc.tochild.close()
245 proc.tochild.close()
246 output = proc.fromchild.read()
246 output = proc.fromchild.read()
247 ret = proc.wait()
247 ret = proc.wait()
248 if os.WIFEXITED(ret):
248 if os.WIFEXITED(ret):
249 ret = os.WEXITSTATUS(ret)
249 ret = os.WEXITSTATUS(ret)
250 except Timeout:
250 except Timeout:
251 vlog('# Process %d timed out - killing it' % proc.pid)
251 vlog('# Process %d timed out - killing it' % proc.pid)
252 os.kill(proc.pid, signal.SIGTERM)
252 os.kill(proc.pid, signal.SIGTERM)
253 ret = proc.wait()
253 ret = proc.wait()
254 if ret == 0:
254 if ret == 0:
255 ret = signal.SIGTERM << 8
255 ret = signal.SIGTERM << 8
256 output += ("\n### Abort: timeout after %d seconds.\n"
256 output += ("\n### Abort: timeout after %d seconds.\n"
257 % options.timeout)
257 % options.timeout)
258 return ret, splitnewlines(output)
258 return ret, splitnewlines(output)
259
259
260 def run_one(test, skips):
260 def run_one(test, skips):
261 '''tristate output:
261 '''tristate output:
262 None -> skipped
262 None -> skipped
263 True -> passed
263 True -> passed
264 False -> failed'''
264 False -> failed'''
265
265
266 def skip(msg):
266 def skip(msg):
267 if not verbose:
267 if not verbose:
268 skips.append((test, msg))
268 skips.append((test, msg))
269 else:
269 else:
270 print "\nSkipping %s: %s" % (test, msg)
270 print "\nSkipping %s: %s" % (test, msg)
271 return None
271 return None
272
272
273 vlog("# Test", test)
273 vlog("# Test", test)
274
274
275 # create a fresh hgrc
275 # create a fresh hgrc
276 hgrc = file(HGRCPATH, 'w+')
276 hgrc = file(HGRCPATH, 'w+')
277 hgrc.write('[ui]\n')
277 hgrc.write('[ui]\n')
278 hgrc.write('slash = True\n')
278 hgrc.write('slash = True\n')
279 hgrc.write('[defaults]\n')
280 hgrc.write('backout = -d "0 0"\n')
281 hgrc.write('commit = -d "0 0"\n')
282 hgrc.write('debugrawcommit = -d "0 0"\n')
283 hgrc.write('tag = -d "0 0"\n')
279 hgrc.close()
284 hgrc.close()
280
285
281 err = os.path.join(TESTDIR, test+".err")
286 err = os.path.join(TESTDIR, test+".err")
282 ref = os.path.join(TESTDIR, test+".out")
287 ref = os.path.join(TESTDIR, test+".out")
283 testpath = os.path.join(TESTDIR, test)
288 testpath = os.path.join(TESTDIR, test)
284
289
285 if os.path.exists(err):
290 if os.path.exists(err):
286 os.remove(err) # Remove any previous output files
291 os.remove(err) # Remove any previous output files
287
292
288 # Make a tmp subdirectory to work in
293 # Make a tmp subdirectory to work in
289 tmpd = os.path.join(HGTMP, test)
294 tmpd = os.path.join(HGTMP, test)
290 os.mkdir(tmpd)
295 os.mkdir(tmpd)
291 os.chdir(tmpd)
296 os.chdir(tmpd)
292
297
293 try:
298 try:
294 tf = open(testpath)
299 tf = open(testpath)
295 firstline = tf.readline().rstrip()
300 firstline = tf.readline().rstrip()
296 tf.close()
301 tf.close()
297 except:
302 except:
298 firstline = ''
303 firstline = ''
299 lctest = test.lower()
304 lctest = test.lower()
300
305
301 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
306 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
302 cmd = '%s "%s"' % (python, testpath)
307 cmd = '%s "%s"' % (python, testpath)
303 elif lctest.endswith('.bat'):
308 elif lctest.endswith('.bat'):
304 # do not run batch scripts on non-windows
309 # do not run batch scripts on non-windows
305 if os.name != 'nt':
310 if os.name != 'nt':
306 return skip("batch script")
311 return skip("batch script")
307 # To reliably get the error code from batch files on WinXP,
312 # To reliably get the error code from batch files on WinXP,
308 # the "cmd /c call" prefix is needed. Grrr
313 # the "cmd /c call" prefix is needed. Grrr
309 cmd = 'cmd /c call "%s"' % testpath
314 cmd = 'cmd /c call "%s"' % testpath
310 else:
315 else:
311 # do not run shell scripts on windows
316 # do not run shell scripts on windows
312 if os.name == 'nt':
317 if os.name == 'nt':
313 return skip("shell script")
318 return skip("shell script")
314 # do not try to run non-executable programs
319 # do not try to run non-executable programs
315 if not os.access(testpath, os.X_OK):
320 if not os.access(testpath, os.X_OK):
316 return skip("not executable")
321 return skip("not executable")
317 cmd = '"%s"' % testpath
322 cmd = '"%s"' % testpath
318
323
319 if options.timeout > 0:
324 if options.timeout > 0:
320 signal.alarm(options.timeout)
325 signal.alarm(options.timeout)
321
326
322 vlog("# Running", cmd)
327 vlog("# Running", cmd)
323 ret, out = run(cmd)
328 ret, out = run(cmd)
324 vlog("# Ret was:", ret)
329 vlog("# Ret was:", ret)
325
330
326 if options.timeout > 0:
331 if options.timeout > 0:
327 signal.alarm(0)
332 signal.alarm(0)
328
333
329 skipped = (ret == SKIPPED_STATUS)
334 skipped = (ret == SKIPPED_STATUS)
330 diffret = 0
335 diffret = 0
331 # If reference output file exists, check test output against it
336 # If reference output file exists, check test output against it
332 if os.path.exists(ref):
337 if os.path.exists(ref):
333 f = open(ref, "r")
338 f = open(ref, "r")
334 ref_out = splitnewlines(f.read())
339 ref_out = splitnewlines(f.read())
335 f.close()
340 f.close()
336 else:
341 else:
337 ref_out = []
342 ref_out = []
338 if not skipped and out != ref_out:
343 if not skipped and out != ref_out:
339 diffret = 1
344 diffret = 1
340 print "\nERROR: %s output changed" % (test)
345 print "\nERROR: %s output changed" % (test)
341 show_diff(ref_out, out)
346 show_diff(ref_out, out)
342 if skipped:
347 if skipped:
343 missing = extract_missing_features(out)
348 missing = extract_missing_features(out)
344 if not missing:
349 if not missing:
345 missing = ['irrelevant']
350 missing = ['irrelevant']
346 skip(missing[-1])
351 skip(missing[-1])
347 elif ret:
352 elif ret:
348 print "\nERROR: %s failed with error code %d" % (test, ret)
353 print "\nERROR: %s failed with error code %d" % (test, ret)
349 elif diffret:
354 elif diffret:
350 ret = diffret
355 ret = diffret
351
356
352 if not verbose:
357 if not verbose:
353 sys.stdout.write(skipped and 's' or '.')
358 sys.stdout.write(skipped and 's' or '.')
354 sys.stdout.flush()
359 sys.stdout.flush()
355
360
356 if ret != 0 and not skipped:
361 if ret != 0 and not skipped:
357 # Save errors to a file for diagnosis
362 # Save errors to a file for diagnosis
358 f = open(err, "wb")
363 f = open(err, "wb")
359 for line in out:
364 for line in out:
360 f.write(line)
365 f.write(line)
361 f.close()
366 f.close()
362
367
363 # Kill off any leftover daemon processes
368 # Kill off any leftover daemon processes
364 try:
369 try:
365 fp = file(DAEMON_PIDS)
370 fp = file(DAEMON_PIDS)
366 for line in fp:
371 for line in fp:
367 try:
372 try:
368 pid = int(line)
373 pid = int(line)
369 except ValueError:
374 except ValueError:
370 continue
375 continue
371 try:
376 try:
372 os.kill(pid, 0)
377 os.kill(pid, 0)
373 vlog('# Killing daemon process %d' % pid)
378 vlog('# Killing daemon process %d' % pid)
374 os.kill(pid, signal.SIGTERM)
379 os.kill(pid, signal.SIGTERM)
375 time.sleep(0.25)
380 time.sleep(0.25)
376 os.kill(pid, 0)
381 os.kill(pid, 0)
377 vlog('# Daemon process %d is stuck - really killing it' % pid)
382 vlog('# Daemon process %d is stuck - really killing it' % pid)
378 os.kill(pid, signal.SIGKILL)
383 os.kill(pid, signal.SIGKILL)
379 except OSError, err:
384 except OSError, err:
380 if err.errno != errno.ESRCH:
385 if err.errno != errno.ESRCH:
381 raise
386 raise
382 fp.close()
387 fp.close()
383 os.unlink(DAEMON_PIDS)
388 os.unlink(DAEMON_PIDS)
384 except IOError:
389 except IOError:
385 pass
390 pass
386
391
387 os.chdir(TESTDIR)
392 os.chdir(TESTDIR)
388 shutil.rmtree(tmpd, True)
393 shutil.rmtree(tmpd, True)
389 if skipped:
394 if skipped:
390 return None
395 return None
391 return ret == 0
396 return ret == 0
392
397
393 if not options.child:
398 if not options.child:
394 os.umask(022)
399 os.umask(022)
395
400
396 check_required_tools()
401 check_required_tools()
397
402
398 # Reset some environment variables to well-known values so that
403 # Reset some environment variables to well-known values so that
399 # the tests produce repeatable output.
404 # the tests produce repeatable output.
400 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
405 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
401 os.environ['TZ'] = 'GMT'
406 os.environ['TZ'] = 'GMT'
402
407
403 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
408 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
404 HGTMP = os.environ['HGTMP'] = tempfile.mkdtemp('', 'hgtests.', options.tmpdir)
409 HGTMP = os.environ['HGTMP'] = tempfile.mkdtemp('', 'hgtests.', options.tmpdir)
405 DAEMON_PIDS = None
410 DAEMON_PIDS = None
406 HGRCPATH = None
411 HGRCPATH = None
407
412
408 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
413 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
409 os.environ["HGMERGE"] = ('python "%s" -L my -L other'
414 os.environ["HGMERGE"] = ('python "%s" -L my -L other'
410 % os.path.join(TESTDIR, os.path.pardir,
415 % os.path.join(TESTDIR, os.path.pardir,
411 'contrib', 'simplemerge'))
416 'contrib', 'simplemerge'))
412 os.environ["HGUSER"] = "test"
417 os.environ["HGUSER"] = "test"
413 os.environ["HGENCODING"] = "ascii"
418 os.environ["HGENCODING"] = "ascii"
414 os.environ["HGENCODINGMODE"] = "strict"
419 os.environ["HGENCODINGMODE"] = "strict"
415 os.environ["HGPORT"] = str(options.port)
420 os.environ["HGPORT"] = str(options.port)
416 os.environ["HGPORT1"] = str(options.port + 1)
421 os.environ["HGPORT1"] = str(options.port + 1)
417 os.environ["HGPORT2"] = str(options.port + 2)
422 os.environ["HGPORT2"] = str(options.port + 2)
418
423
419 if options.with_hg:
424 if options.with_hg:
420 INST = options.with_hg
425 INST = options.with_hg
421 else:
426 else:
422 INST = os.path.join(HGTMP, "install")
427 INST = os.path.join(HGTMP, "install")
423 BINDIR = os.path.join(INST, "bin")
428 BINDIR = os.path.join(INST, "bin")
424 PYTHONDIR = os.path.join(INST, "lib", "python")
429 PYTHONDIR = os.path.join(INST, "lib", "python")
425 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
430 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
426
431
427 def run_children(tests):
432 def run_children(tests):
428 if not options.with_hg:
433 if not options.with_hg:
429 install_hg()
434 install_hg()
430
435
431 optcopy = dict(options.__dict__)
436 optcopy = dict(options.__dict__)
432 optcopy['jobs'] = 1
437 optcopy['jobs'] = 1
433 optcopy['with_hg'] = INST
438 optcopy['with_hg'] = INST
434 opts = []
439 opts = []
435 for opt, value in optcopy.iteritems():
440 for opt, value in optcopy.iteritems():
436 name = '--' + opt.replace('_', '-')
441 name = '--' + opt.replace('_', '-')
437 if value is True:
442 if value is True:
438 opts.append(name)
443 opts.append(name)
439 elif value is not None:
444 elif value is not None:
440 opts.append(name + '=' + str(value))
445 opts.append(name + '=' + str(value))
441
446
442 tests.reverse()
447 tests.reverse()
443 jobs = [[] for j in xrange(options.jobs)]
448 jobs = [[] for j in xrange(options.jobs)]
444 while tests:
449 while tests:
445 for j in xrange(options.jobs):
450 for j in xrange(options.jobs):
446 if not tests: break
451 if not tests: break
447 jobs[j].append(tests.pop())
452 jobs[j].append(tests.pop())
448 fps = {}
453 fps = {}
449 for j in xrange(len(jobs)):
454 for j in xrange(len(jobs)):
450 job = jobs[j]
455 job = jobs[j]
451 if not job:
456 if not job:
452 continue
457 continue
453 rfd, wfd = os.pipe()
458 rfd, wfd = os.pipe()
454 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
459 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
455 cmdline = [python, sys.argv[0]] + opts + childopts + job
460 cmdline = [python, sys.argv[0]] + opts + childopts + job
456 vlog(' '.join(cmdline))
461 vlog(' '.join(cmdline))
457 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
462 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
458 os.close(wfd)
463 os.close(wfd)
459 failures = 0
464 failures = 0
460 tested, skipped, failed = 0, 0, 0
465 tested, skipped, failed = 0, 0, 0
461 skips = []
466 skips = []
462 while fps:
467 while fps:
463 pid, status = os.wait()
468 pid, status = os.wait()
464 fp = fps.pop(pid)
469 fp = fps.pop(pid)
465 l = fp.read().splitlines()
470 l = fp.read().splitlines()
466 test, skip, fail = map(int, l[:3])
471 test, skip, fail = map(int, l[:3])
467 for s in l[3:]:
472 for s in l[3:]:
468 skips.append(s.split(" ", 1))
473 skips.append(s.split(" ", 1))
469 tested += test
474 tested += test
470 skipped += skip
475 skipped += skip
471 failed += fail
476 failed += fail
472 vlog('pid %d exited, status %d' % (pid, status))
477 vlog('pid %d exited, status %d' % (pid, status))
473 failures |= status
478 failures |= status
474 print
479 print
475 for s in skips:
480 for s in skips:
476 print "Skipped %s: %s" % (s[0], s[1])
481 print "Skipped %s: %s" % (s[0], s[1])
477 print "# Ran %d tests, %d skipped, %d failed." % (
482 print "# Ran %d tests, %d skipped, %d failed." % (
478 tested, skipped, failed)
483 tested, skipped, failed)
479 sys.exit(failures != 0)
484 sys.exit(failures != 0)
480
485
481 def run_tests(tests):
486 def run_tests(tests):
482 global DAEMON_PIDS, HGRCPATH
487 global DAEMON_PIDS, HGRCPATH
483 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
488 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
484 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
489 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
485
490
486 try:
491 try:
487 if not options.with_hg:
492 if not options.with_hg:
488 install_hg()
493 install_hg()
489
494
490 if options.timeout > 0:
495 if options.timeout > 0:
491 try:
496 try:
492 signal.signal(signal.SIGALRM, alarmed)
497 signal.signal(signal.SIGALRM, alarmed)
493 vlog('# Running tests with %d-second timeout' %
498 vlog('# Running tests with %d-second timeout' %
494 options.timeout)
499 options.timeout)
495 except AttributeError:
500 except AttributeError:
496 print 'WARNING: cannot run tests with timeouts'
501 print 'WARNING: cannot run tests with timeouts'
497 options.timeout = 0
502 options.timeout = 0
498
503
499 tested = 0
504 tested = 0
500 failed = 0
505 failed = 0
501 skipped = 0
506 skipped = 0
502
507
503 if options.restart:
508 if options.restart:
504 orig = list(tests)
509 orig = list(tests)
505 while tests:
510 while tests:
506 if os.path.exists(tests[0] + ".err"):
511 if os.path.exists(tests[0] + ".err"):
507 break
512 break
508 tests.pop(0)
513 tests.pop(0)
509 if not tests:
514 if not tests:
510 print "running all tests"
515 print "running all tests"
511 tests = orig
516 tests = orig
512
517
513 skips = []
518 skips = []
514 for test in tests:
519 for test in tests:
515 if options.retest and not os.path.exists(test + ".err"):
520 if options.retest and not os.path.exists(test + ".err"):
516 skipped += 1
521 skipped += 1
517 continue
522 continue
518 ret = run_one(test, skips)
523 ret = run_one(test, skips)
519 if ret is None:
524 if ret is None:
520 skipped += 1
525 skipped += 1
521 elif not ret:
526 elif not ret:
522 if options.interactive:
527 if options.interactive:
523 print "Accept this change? [n] ",
528 print "Accept this change? [n] ",
524 answer = sys.stdin.readline().strip()
529 answer = sys.stdin.readline().strip()
525 if answer.lower() in "y yes".split():
530 if answer.lower() in "y yes".split():
526 os.rename(test + ".err", test + ".out")
531 os.rename(test + ".err", test + ".out")
527 tested += 1
532 tested += 1
528 continue
533 continue
529 failed += 1
534 failed += 1
530 if options.first:
535 if options.first:
531 break
536 break
532 tested += 1
537 tested += 1
533
538
534 if options.child:
539 if options.child:
535 fp = os.fdopen(options.child, 'w')
540 fp = os.fdopen(options.child, 'w')
536 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
541 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
537 for s in skips:
542 for s in skips:
538 fp.write("%s %s\n" % s)
543 fp.write("%s %s\n" % s)
539 fp.close()
544 fp.close()
540 else:
545 else:
541 print
546 print
542 for s in skips:
547 for s in skips:
543 print "Skipped %s: %s" % s
548 print "Skipped %s: %s" % s
544 print "# Ran %d tests, %d skipped, %d failed." % (
549 print "# Ran %d tests, %d skipped, %d failed." % (
545 tested, skipped, failed)
550 tested, skipped, failed)
546
551
547 if coverage:
552 if coverage:
548 output_coverage()
553 output_coverage()
549 except KeyboardInterrupt:
554 except KeyboardInterrupt:
550 failed = True
555 failed = True
551 print "\ninterrupted!"
556 print "\ninterrupted!"
552
557
553 if failed:
558 if failed:
554 sys.exit(1)
559 sys.exit(1)
555
560
556 if len(args) == 0:
561 if len(args) == 0:
557 args = os.listdir(".")
562 args = os.listdir(".")
558 args.sort()
563 args.sort()
559
564
560 tests = []
565 tests = []
561 for test in args:
566 for test in args:
562 if (test.startswith("test-") and '~' not in test and
567 if (test.startswith("test-") and '~' not in test and
563 ('.' not in test or test.endswith('.py') or
568 ('.' not in test or test.endswith('.py') or
564 test.endswith('.bat'))):
569 test.endswith('.bat'))):
565 tests.append(test)
570 tests.append(test)
566
571
567 vlog("# Using TESTDIR", TESTDIR)
572 vlog("# Using TESTDIR", TESTDIR)
568 vlog("# Using HGTMP", HGTMP)
573 vlog("# Using HGTMP", HGTMP)
569
574
570 try:
575 try:
571 if len(tests) > 1 and options.jobs > 1:
576 if len(tests) > 1 and options.jobs > 1:
572 run_children(tests)
577 run_children(tests)
573 else:
578 else:
574 run_tests(tests)
579 run_tests(tests)
575 finally:
580 finally:
576 cleanup_exit()
581 cleanup_exit()
@@ -1,128 +1,128 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 mkdir t
3 mkdir t
4 cd t
4 cd t
5 hg init
5 hg init
6 hg id
6 hg id
7 echo a > a
7 echo a > a
8 hg add a
8 hg add a
9 hg commit -m "test" -d "1000000 0"
9 hg commit -m "test" -d "1000000 0"
10 hg co
10 hg co
11 hg identify
11 hg identify
12 T=`hg tip --debug | head -n 1 | cut -d : -f 3`
12 T=`hg tip --debug | head -n 1 | cut -d : -f 3`
13 hg tag -l "This is a local tag with a really long name!"
13 hg tag -l "This is a local tag with a really long name!"
14 hg tags
14 hg tags
15 rm .hg/localtags
15 rm .hg/localtags
16 echo "$T first" > .hgtags
16 echo "$T first" > .hgtags
17 cat .hgtags
17 cat .hgtags
18 hg add .hgtags
18 hg add .hgtags
19 hg commit -m "add tags" -d "1000000 0"
19 hg commit -m "add tags" -d "1000000 0"
20 hg tags
20 hg tags
21 hg identify
21 hg identify
22 echo bb > a
22 echo bb > a
23 hg status
23 hg status
24 hg identify
24 hg identify
25 hg co first
25 hg co first
26 hg id
26 hg id
27 hg -v id
27 hg -v id
28 hg status
28 hg status
29 echo 1 > b
29 echo 1 > b
30 hg add b
30 hg add b
31 hg commit -m "branch" -d "1000000 0"
31 hg commit -m "branch" -d "1000000 0"
32 hg id
32 hg id
33 hg merge 1
33 hg merge 1
34 hg id
34 hg id
35 hg status
35 hg status
36
36
37 hg commit -m "merge" -d "1000000 0"
37 hg commit -m "merge" -d "1000000 0"
38
38
39 # create fake head, make sure tag not visible afterwards
39 # create fake head, make sure tag not visible afterwards
40 cp .hgtags tags
40 cp .hgtags tags
41 hg tag -d "1000000 0" last
41 hg tag -d "1000000 0" last
42 hg rm .hgtags
42 hg rm .hgtags
43 hg commit -m "remove" -d "1000000 0"
43 hg commit -m "remove" -d "1000000 0"
44
44
45 mv tags .hgtags
45 mv tags .hgtags
46 hg add .hgtags
46 hg add .hgtags
47 hg commit -m "readd" -d "1000000 0"
47 hg commit -m "readd" -d "1000000 0"
48
48
49 hg tags
49 hg tags
50
50
51 # invalid tags
51 # invalid tags
52 echo "spam" >> .hgtags
52 echo "spam" >> .hgtags
53 echo >> .hgtags
53 echo >> .hgtags
54 echo "foo bar" >> .hgtags
54 echo "foo bar" >> .hgtags
55 echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags
55 echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags
56 hg commit -m "tags" -d "1000000 0"
56 hg commit -m "tags" -d "1000000 0"
57
57
58 # report tag parse error on other head
58 # report tag parse error on other head
59 hg up 3
59 hg up 3
60 echo 'x y' >> .hgtags
60 echo 'x y' >> .hgtags
61 hg commit -m "head" -d "1000000 0"
61 hg commit -m "head" -d "1000000 0"
62
62
63 hg tags
63 hg tags
64 hg tip
64 hg tip
65
65
66 # test tag precedence rules
66 # test tag precedence rules
67 cd ..
67 cd ..
68 hg init t2
68 hg init t2
69 cd t2
69 cd t2
70 echo foo > foo
70 echo foo > foo
71 hg add foo
71 hg add foo
72 hg ci -m 'add foo' -d '1000000 0' # rev 0
72 hg ci -m 'add foo' -d '1000000 0' # rev 0
73 hg tag -d '1000000 0' bar # rev 1
73 hg tag -d '1000000 0' bar # rev 1
74 echo >> foo
74 echo >> foo
75 hg ci -m 'change foo 1' -d '1000000 0' # rev 2
75 hg ci -m 'change foo 1' -d '1000000 0' # rev 2
76 hg up -C 1
76 hg up -C 1
77 hg tag -r 1 -d '1000000 0' -f bar # rev 3
77 hg tag -r 1 -d '1000000 0' -f bar # rev 3
78 hg up -C 1
78 hg up -C 1
79 echo >> foo
79 echo >> foo
80 hg ci -m 'change foo 2' -d '1000000 0' # rev 4
80 hg ci -m 'change foo 2' -d '1000000 0' # rev 4
81 hg tags
81 hg tags
82
82
83 # test tag removal
83 # test tag removal
84 hg tag --remove -d '1000000 0' bar
84 hg tag --remove -d '1000000 0' bar
85 hg tip
85 hg tip
86 hg tags
86 hg tags
87
87
88 echo '% remove nonexistent tag'
88 echo '% remove nonexistent tag'
89 hg tag --remove -d '1000000 0' foobar
89 hg tag --remove -d '1000000 0' foobar
90 hg tip
90 hg tip
91
91
92 # test tag rank
92 # test tag rank
93 cd ..
93 cd ..
94 hg init t3
94 hg init t3
95 cd t3
95 cd t3
96 echo foo > foo
96 echo foo > foo
97 hg add foo
97 hg add foo
98 hg ci -m 'add foo' -d '1000000 0' # rev 0
98 hg ci -m 'add foo' -d '1000000 0' # rev 0
99 hg tag -d '1000000 0' -f bar # rev 1 bar -> 0
99 hg tag -d '1000000 0' -f bar # rev 1 bar -> 0
100 hg tag -d '1000000 0' -f bar # rev 2 bar -> 1
100 hg tag -d '1000000 0' -f bar # rev 2 bar -> 1
101 hg tag -d '1000000 0' -fr 0 bar # rev 3 bar -> 0
101 hg tag -d '1000000 0' -fr 0 bar # rev 3 bar -> 0
102 hg tag -d '1000000 0' -fr 1 bar # rev 3 bar -> 1
102 hg tag -d '1000000 0' -fr 1 bar # rev 3 bar -> 1
103 hg tag -d '1000000 0' -fr 0 bar # rev 4 bar -> 0
103 hg tag -d '1000000 0' -fr 0 bar # rev 4 bar -> 0
104 hg tags
104 hg tags
105 hg co 3
105 hg co 3
106 echo barbar > foo
106 echo barbar > foo
107 hg ci -m 'change foo' -d '1000000 0' # rev 0
107 hg ci -m 'change foo' -d '1000000 0' # rev 0
108 hg tags
108 hg tags
109
109
110 hg tag -d '1000000 0' -r 3 bar # should complain
110 hg tag -d '1000000 0' -r 3 bar # should complain
111 hg tags
111 hg tags
112
112
113 # test tag rank with 3 heads
113 # test tag rank with 3 heads
114 cd ..
114 cd ..
115 hg init t4
115 hg init t4
116 cd t4
116 cd t4
117 echo foo > foo
117 echo foo > foo
118 hg add
118 hg add
119 hg ci -m 'add foo' -d '0 0' # rev 0
119 hg ci -m 'add foo' # rev 0
120 hg tag -d '0 0' bar # rev 1 bar -> 0
120 hg tag bar # rev 1 bar -> 0
121 hg tag -d '0 0' -f bar # rev 2 bar -> 1
121 hg tag -f bar # rev 2 bar -> 1
122 hg up -qC 0
122 hg up -qC 0
123 hg tag -d '0 0' -fr 2 bar # rev 3 bar -> 2
123 hg tag -fr 2 bar # rev 3 bar -> 2
124 hg tags
124 hg tags
125 hg up -qC 0
125 hg up -qC 0
126 hg tag -d '0 0' -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
126 hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
127 echo % bar should still point to rev 2
127 echo % bar should still point to rev 2
128 hg tags
128 hg tags
General Comments 0
You need to be logged in to leave comments. Login now