##// END OF EJS Templates
run-tests.py: make tests use same python interpreter as test harness....
Vadim Gelfer -
r2570:2264b2b0 default
parent child Browse files
Show More
@@ -1,269 +1,288 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 os, sys, shutil, re
10 import os, sys, shutil, re
11 import tempfile
11 import tempfile
12 import difflib
12 import difflib
13 import popen2
13 import popen2
14 from optparse import OptionParser
14 from optparse import OptionParser
15
15
16 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
16 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
17
17
18 parser = OptionParser("%prog [options] [tests]")
18 parser = OptionParser("%prog [options] [tests]")
19 parser.add_option("-v", "--verbose", action="store_true",
19 parser.add_option("-v", "--verbose", action="store_true",
20 help="output verbose messages")
20 help="output verbose messages")
21 parser.add_option("-c", "--cover", action="store_true",
21 parser.add_option("-c", "--cover", action="store_true",
22 help="print a test coverage report")
22 help="print a test coverage report")
23 parser.add_option("-s", "--cover_stdlib", action="store_true",
23 parser.add_option("-s", "--cover_stdlib", action="store_true",
24 help="print a test coverage report inc. standard libraries")
24 help="print a test coverage report inc. standard libraries")
25 parser.add_option("-C", "--annotate", action="store_true",
25 parser.add_option("-C", "--annotate", action="store_true",
26 help="output files annotated with coverage")
26 help="output files annotated with coverage")
27 (options, args) = parser.parse_args()
27 (options, args) = parser.parse_args()
28 verbose = options.verbose
28 verbose = options.verbose
29 coverage = options.cover or options.cover_stdlib or options.annotate
29 coverage = options.cover or options.cover_stdlib or options.annotate
30
30
31 def vlog(*msg):
31 def vlog(*msg):
32 if verbose:
32 if verbose:
33 for m in msg:
33 for m in msg:
34 print m,
34 print m,
35 print
35 print
36
36
37 def splitnewlines(text):
37 def splitnewlines(text):
38 '''like str.splitlines, but only split on newlines.
38 '''like str.splitlines, but only split on newlines.
39 keep line endings.'''
39 keep line endings.'''
40 i = 0
40 i = 0
41 lines = []
41 lines = []
42 while True:
42 while True:
43 n = text.find('\n', i)
43 n = text.find('\n', i)
44 if n == -1:
44 if n == -1:
45 last = text[i:]
45 last = text[i:]
46 if last:
46 if last:
47 lines.append(last)
47 lines.append(last)
48 return lines
48 return lines
49 lines.append(text[i:n+1])
49 lines.append(text[i:n+1])
50 i = n + 1
50 i = n + 1
51
51
52 def show_diff(expected, output):
52 def show_diff(expected, output):
53 for line in difflib.unified_diff(expected, output,
53 for line in difflib.unified_diff(expected, output,
54 "Expected output", "Test output"):
54 "Expected output", "Test output"):
55 sys.stdout.write(line)
55 sys.stdout.write(line)
56
56
57 def find_program(program):
57 def find_program(program):
58 """Search PATH for a executable program"""
58 """Search PATH for a executable program"""
59 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
59 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
60 name = os.path.join(p, program)
60 name = os.path.join(p, program)
61 if os.access(name, os.X_OK):
61 if os.access(name, os.X_OK):
62 return name
62 return name
63 return None
63 return None
64
64
65 def check_required_tools():
65 def check_required_tools():
66 # Before we go any further, check for pre-requisite tools
66 # Before we go any further, check for pre-requisite tools
67 # stuff from coreutils (cat, rm, etc) are not tested
67 # stuff from coreutils (cat, rm, etc) are not tested
68 for p in required_tools:
68 for p in required_tools:
69 if os.name == 'nt':
69 if os.name == 'nt':
70 p += '.exe'
70 p += '.exe'
71 found = find_program(p)
71 found = find_program(p)
72 if found:
72 if found:
73 vlog("# Found prerequisite", p, "at", found)
73 vlog("# Found prerequisite", p, "at", found)
74 else:
74 else:
75 print "WARNING: Did not find prerequisite tool: "+p
75 print "WARNING: Did not find prerequisite tool: "+p
76
76
77 def cleanup_exit():
77 def cleanup_exit():
78 if verbose:
78 if verbose:
79 print "# Cleaning up HGTMP", HGTMP
79 print "# Cleaning up HGTMP", HGTMP
80 shutil.rmtree(HGTMP, True)
80 shutil.rmtree(HGTMP, True)
81
81
82 def use_correct_python():
83 # some tests run python interpreter. they must use same
84 # interpreter we use or bad things will happen.
85 exedir, exename = os.path.split(sys.executable)
86 if exename == 'python':
87 path = find_program('python')
88 if os.path.dirname(path) == exedir:
89 return
90 vlog('# Making python executable in test path use correct Python')
91 my_python = os.path.join(BINDIR, 'python')
92 try:
93 os.symlink(sys.executable, my_python)
94 except AttributeError:
95 # windows fallback
96 shutil.copyfile(sys.executable, my_python)
97 shutil.copymode(sys.executable, my_python)
98
82 def install_hg():
99 def install_hg():
83 vlog("# Performing temporary installation of HG")
100 vlog("# Performing temporary installation of HG")
84 installerrs = os.path.join("tests", "install.err")
101 installerrs = os.path.join("tests", "install.err")
85
102
86 os.chdir("..") # Get back to hg root
103 os.chdir("..") # Get back to hg root
87 cmd = ('%s setup.py clean --all'
104 cmd = ('%s setup.py clean --all'
88 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
105 ' install --force --home="%s" --install-lib="%s" >%s 2>&1'
89 % (sys.executable, INST, PYTHONDIR, installerrs))
106 % (sys.executable, INST, PYTHONDIR, installerrs))
90 vlog("# Running", cmd)
107 vlog("# Running", cmd)
91 if os.system(cmd) == 0:
108 if os.system(cmd) == 0:
92 if not verbose:
109 if not verbose:
93 os.remove(installerrs)
110 os.remove(installerrs)
94 else:
111 else:
95 f = open(installerrs)
112 f = open(installerrs)
96 for line in f:
113 for line in f:
97 print line,
114 print line,
98 f.close()
115 f.close()
99 sys.exit(1)
116 sys.exit(1)
100 os.chdir(TESTDIR)
117 os.chdir(TESTDIR)
101
118
102 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
119 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
103 os.environ["PYTHONPATH"] = PYTHONDIR
120 os.environ["PYTHONPATH"] = PYTHONDIR
104
121
122 use_correct_python()
123
105 if coverage:
124 if coverage:
106 vlog("# Installing coverage wrapper")
125 vlog("# Installing coverage wrapper")
107 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
126 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
108 if os.path.exists(COVERAGE_FILE):
127 if os.path.exists(COVERAGE_FILE):
109 os.unlink(COVERAGE_FILE)
128 os.unlink(COVERAGE_FILE)
110 # Create a wrapper script to invoke hg via coverage.py
129 # Create a wrapper script to invoke hg via coverage.py
111 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
130 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
112 f = open(os.path.join(BINDIR, 'hg'), 'w')
131 f = open(os.path.join(BINDIR, 'hg'), 'w')
113 f.write('#!' + sys.executable + '\n')
132 f.write('#!' + sys.executable + '\n')
114 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '+ \
133 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '+ \
115 '"%s", "-x", "%s"] + sys.argv[1:])\n' % (
134 '"%s", "-x", "%s"] + sys.argv[1:])\n' % (
116 os.path.join(TESTDIR, 'coverage.py'),
135 os.path.join(TESTDIR, 'coverage.py'),
117 os.path.join(BINDIR, '_hg.py')))
136 os.path.join(BINDIR, '_hg.py')))
118 f.close()
137 f.close()
119 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
138 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
120
139
121 def output_coverage():
140 def output_coverage():
122 vlog("# Producing coverage report")
141 vlog("# Producing coverage report")
123 omit = [BINDIR, TESTDIR, PYTHONDIR]
142 omit = [BINDIR, TESTDIR, PYTHONDIR]
124 if not options.cover_stdlib:
143 if not options.cover_stdlib:
125 # Exclude as system paths (ignoring empty strings seen on win)
144 # Exclude as system paths (ignoring empty strings seen on win)
126 omit += [x for x in sys.path if x != '']
145 omit += [x for x in sys.path if x != '']
127 omit = ','.join(omit)
146 omit = ','.join(omit)
128 os.chdir(PYTHONDIR)
147 os.chdir(PYTHONDIR)
129 cmd = '"%s" "%s" -r "--omit=%s"' % (
148 cmd = '"%s" "%s" -r "--omit=%s"' % (
130 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
149 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
131 vlog("# Running: "+cmd)
150 vlog("# Running: "+cmd)
132 os.system(cmd)
151 os.system(cmd)
133 if options.annotate:
152 if options.annotate:
134 adir = os.path.join(TESTDIR, 'annotated')
153 adir = os.path.join(TESTDIR, 'annotated')
135 if not os.path.isdir(adir):
154 if not os.path.isdir(adir):
136 os.mkdir(adir)
155 os.mkdir(adir)
137 cmd = '"%s" "%s" -a "--directory=%s" "--omit=%s"' % (
156 cmd = '"%s" "%s" -a "--directory=%s" "--omit=%s"' % (
138 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
157 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
139 adir, omit)
158 adir, omit)
140 vlog("# Running: "+cmd)
159 vlog("# Running: "+cmd)
141 os.system(cmd)
160 os.system(cmd)
142
161
143 def run(cmd):
162 def run(cmd):
144 """Run command in a sub-process, capturing the output (stdout and stderr).
163 """Run command in a sub-process, capturing the output (stdout and stderr).
145 Return the exist code, and output."""
164 Return the exist code, and output."""
146 # TODO: Use subprocess.Popen if we're running on Python 2.4
165 # TODO: Use subprocess.Popen if we're running on Python 2.4
147 if os.name == 'nt':
166 if os.name == 'nt':
148 tochild, fromchild = os.popen4(cmd)
167 tochild, fromchild = os.popen4(cmd)
149 tochild.close()
168 tochild.close()
150 output = fromchild.read()
169 output = fromchild.read()
151 ret = fromchild.close()
170 ret = fromchild.close()
152 if ret == None:
171 if ret == None:
153 ret = 0
172 ret = 0
154 else:
173 else:
155 proc = popen2.Popen4(cmd)
174 proc = popen2.Popen4(cmd)
156 proc.tochild.close()
175 proc.tochild.close()
157 output = proc.fromchild.read()
176 output = proc.fromchild.read()
158 ret = proc.wait()
177 ret = proc.wait()
159 return ret, splitnewlines(output)
178 return ret, splitnewlines(output)
160
179
161 def run_one(test):
180 def run_one(test):
162 vlog("# Test", test)
181 vlog("# Test", test)
163 if not verbose:
182 if not verbose:
164 sys.stdout.write('.')
183 sys.stdout.write('.')
165 sys.stdout.flush()
184 sys.stdout.flush()
166
185
167 err = os.path.join(TESTDIR, test+".err")
186 err = os.path.join(TESTDIR, test+".err")
168 ref = os.path.join(TESTDIR, test+".out")
187 ref = os.path.join(TESTDIR, test+".out")
169
188
170 if os.path.exists(err):
189 if os.path.exists(err):
171 os.remove(err) # Remove any previous output files
190 os.remove(err) # Remove any previous output files
172
191
173 # Make a tmp subdirectory to work in
192 # Make a tmp subdirectory to work in
174 tmpd = os.path.join(HGTMP, test)
193 tmpd = os.path.join(HGTMP, test)
175 os.mkdir(tmpd)
194 os.mkdir(tmpd)
176 os.chdir(tmpd)
195 os.chdir(tmpd)
177
196
178 if test.endswith(".py"):
197 if test.endswith(".py"):
179 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
198 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
180 else:
199 else:
181 cmd = '"%s"' % (os.path.join(TESTDIR, test))
200 cmd = '"%s"' % (os.path.join(TESTDIR, test))
182
201
183 # To reliably get the error code from batch files on WinXP,
202 # To reliably get the error code from batch files on WinXP,
184 # the "cmd /c call" prefix is needed. Grrr
203 # the "cmd /c call" prefix is needed. Grrr
185 if os.name == 'nt' and test.endswith(".bat"):
204 if os.name == 'nt' and test.endswith(".bat"):
186 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
205 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
187
206
188 vlog("# Running", cmd)
207 vlog("# Running", cmd)
189 ret, out = run(cmd)
208 ret, out = run(cmd)
190 vlog("# Ret was:", ret)
209 vlog("# Ret was:", ret)
191
210
192 diffret = 0
211 diffret = 0
193 # If reference output file exists, check test output against it
212 # If reference output file exists, check test output against it
194 if os.path.exists(ref):
213 if os.path.exists(ref):
195 f = open(ref, "r")
214 f = open(ref, "r")
196 ref_out = splitnewlines(f.read())
215 ref_out = splitnewlines(f.read())
197 f.close()
216 f.close()
198 else:
217 else:
199 ref_out = ['']
218 ref_out = ['']
200 if out != ref_out:
219 if out != ref_out:
201 diffret = 1
220 diffret = 1
202 print "\nERROR: %s output changed" % (test)
221 print "\nERROR: %s output changed" % (test)
203 show_diff(ref_out, out)
222 show_diff(ref_out, out)
204 if ret:
223 if ret:
205 print "\nERROR: %s failed with error code %d" % (test, ret)
224 print "\nERROR: %s failed with error code %d" % (test, ret)
206 elif diffret:
225 elif diffret:
207 ret = diffret
226 ret = diffret
208
227
209 if ret != 0: # Save errors to a file for diagnosis
228 if ret != 0: # Save errors to a file for diagnosis
210 f = open(err, "wb")
229 f = open(err, "wb")
211 for line in out:
230 for line in out:
212 f.write(line)
231 f.write(line)
213 f.close()
232 f.close()
214
233
215 os.chdir(TESTDIR)
234 os.chdir(TESTDIR)
216 shutil.rmtree(tmpd, True)
235 shutil.rmtree(tmpd, True)
217 return ret == 0
236 return ret == 0
218
237
219
238
220 os.umask(022)
239 os.umask(022)
221
240
222 check_required_tools()
241 check_required_tools()
223
242
224 # Reset some environment variables to well-known values so that
243 # Reset some environment variables to well-known values so that
225 # the tests produce repeatable output.
244 # the tests produce repeatable output.
226 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
245 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
227 os.environ['TZ'] = 'GMT'
246 os.environ['TZ'] = 'GMT'
228
247
229 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
248 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
230 os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"'
249 os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"'
231 os.environ["HGUSER"] = "test"
250 os.environ["HGUSER"] = "test"
232 os.environ["HGRCPATH"] = ""
251 os.environ["HGRCPATH"] = ""
233
252
234 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
253 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
235 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
254 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
236 vlog("# Using TESTDIR", TESTDIR)
255 vlog("# Using TESTDIR", TESTDIR)
237 vlog("# Using HGTMP", HGTMP)
256 vlog("# Using HGTMP", HGTMP)
238
257
239 INST = os.path.join(HGTMP, "install")
258 INST = os.path.join(HGTMP, "install")
240 BINDIR = os.path.join(INST, "bin")
259 BINDIR = os.path.join(INST, "bin")
241 PYTHONDIR = os.path.join(INST, "lib", "python")
260 PYTHONDIR = os.path.join(INST, "lib", "python")
242 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
261 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
243
262
244 try:
263 try:
245 try:
264 try:
246 install_hg()
265 install_hg()
247
266
248 tests = 0
267 tests = 0
249 failed = 0
268 failed = 0
250
269
251 if len(args) == 0:
270 if len(args) == 0:
252 args = os.listdir(".")
271 args = os.listdir(".")
253 for test in args:
272 for test in args:
254 if test.startswith("test-") and not '~' in test and not '.' in test:
273 if test.startswith("test-") and not '~' in test and not '.' in test:
255 if not run_one(test):
274 if not run_one(test):
256 failed += 1
275 failed += 1
257 tests += 1
276 tests += 1
258
277
259 print "\n# Ran %d tests, %d failed." % (tests, failed)
278 print "\n# Ran %d tests, %d failed." % (tests, failed)
260 if coverage:
279 if coverage:
261 output_coverage()
280 output_coverage()
262 except KeyboardInterrupt:
281 except KeyboardInterrupt:
263 failed = True
282 failed = True
264 print "\ninterrupted!"
283 print "\ninterrupted!"
265 finally:
284 finally:
266 cleanup_exit()
285 cleanup_exit()
267
286
268 if failed:
287 if failed:
269 sys.exit(1)
288 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now