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