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