##// END OF EJS Templates
strip leading 'ESC[?1034h' in tests caused by `import readline`...
MinRK -
Show More
@@ -1,321 +1,332 b''
1 1 """Generic testing tools that do NOT depend on Twisted.
2 2
3 3 In particular, this module exposes a set of top-level assert* functions that
4 4 can be used in place of nose.tools.assert* in method generators (the ones in
5 5 nose can not, at least as of nose 0.10.4).
6 6
7 7 Note: our testing package contains testing.util, which does depend on Twisted
8 8 and provides utilities for tests that manage Deferreds. All testing support
9 9 tools that only depend on nose, IPython or the standard library should go here
10 10 instead.
11 11
12 12
13 13 Authors
14 14 -------
15 15 - Fernando Perez <Fernando.Perez@berkeley.edu>
16 16 """
17 17
18 18 from __future__ import absolute_import
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Copyright (C) 2009 The IPython Development Team
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #-----------------------------------------------------------------------------
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Imports
29 29 #-----------------------------------------------------------------------------
30 30
31 31 import os
32 32 import re
33 33 import sys
34 34
35 35 from contextlib import contextmanager
36 36
37 37 try:
38 38 # These tools are used by parts of the runtime, so we make the nose
39 39 # dependency optional at this point. Nose is a hard dependency to run the
40 40 # test suite, but NOT to use ipython itself.
41 41 import nose.tools as nt
42 42 has_nose = True
43 43 except ImportError:
44 44 has_nose = False
45 45
46 46 from IPython.config.loader import Config
47 47 from IPython.utils.process import find_cmd, getoutputerror
48 48 from IPython.utils.text import list_strings
49 49 from IPython.utils.io import temp_pyfile
50 50
51 51 from . import decorators as dec
52 52 from . import skipdoctest
53 53
54 54 #-----------------------------------------------------------------------------
55 55 # Globals
56 56 #-----------------------------------------------------------------------------
57 57
58 58 # Make a bunch of nose.tools assert wrappers that can be used in test
59 59 # generators. This will expose an assert* function for each one in nose.tools.
60 60
61 61 _tpl = """
62 62 def %(name)s(*a,**kw):
63 63 return nt.%(name)s(*a,**kw)
64 64 """
65 65
66 66 if has_nose:
67 67 for _x in [a for a in dir(nt) if a.startswith('assert')]:
68 68 exec _tpl % dict(name=_x)
69 69
70 70 #-----------------------------------------------------------------------------
71 71 # Functions and classes
72 72 #-----------------------------------------------------------------------------
73 73
74 74 # The docstring for full_path doctests differently on win32 (different path
75 75 # separator) so just skip the doctest there. The example remains informative.
76 76 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
77 77
78 78 @doctest_deco
79 79 def full_path(startPath,files):
80 80 """Make full paths for all the listed files, based on startPath.
81 81
82 82 Only the base part of startPath is kept, since this routine is typically
83 83 used with a script's __file__ variable as startPath. The base of startPath
84 84 is then prepended to all the listed files, forming the output list.
85 85
86 86 Parameters
87 87 ----------
88 88 startPath : string
89 89 Initial path to use as the base for the results. This path is split
90 90 using os.path.split() and only its first component is kept.
91 91
92 92 files : string or list
93 93 One or more files.
94 94
95 95 Examples
96 96 --------
97 97
98 98 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
99 99 ['/foo/a.txt', '/foo/b.txt']
100 100
101 101 >>> full_path('/foo',['a.txt','b.txt'])
102 102 ['/a.txt', '/b.txt']
103 103
104 104 If a single file is given, the output is still a list:
105 105 >>> full_path('/foo','a.txt')
106 106 ['/a.txt']
107 107 """
108 108
109 109 files = list_strings(files)
110 110 base = os.path.split(startPath)[0]
111 111 return [ os.path.join(base,f) for f in files ]
112 112
113 113
114 114 def parse_test_output(txt):
115 115 """Parse the output of a test run and return errors, failures.
116 116
117 117 Parameters
118 118 ----------
119 119 txt : str
120 120 Text output of a test run, assumed to contain a line of one of the
121 121 following forms::
122 122 'FAILED (errors=1)'
123 123 'FAILED (failures=1)'
124 124 'FAILED (errors=1, failures=1)'
125 125
126 126 Returns
127 127 -------
128 128 nerr, nfail: number of errors and failures.
129 129 """
130 130
131 131 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
132 132 if err_m:
133 133 nerr = int(err_m.group(1))
134 134 nfail = 0
135 135 return nerr, nfail
136 136
137 137 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
138 138 if fail_m:
139 139 nerr = 0
140 140 nfail = int(fail_m.group(1))
141 141 return nerr, nfail
142 142
143 143 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
144 144 re.MULTILINE)
145 145 if both_m:
146 146 nerr = int(both_m.group(1))
147 147 nfail = int(both_m.group(2))
148 148 return nerr, nfail
149 149
150 150 # If the input didn't match any of these forms, assume no error/failures
151 151 return 0, 0
152 152
153 153
154 154 # So nose doesn't think this is a test
155 155 parse_test_output.__test__ = False
156 156
157 157
158 158 def default_argv():
159 159 """Return a valid default argv for creating testing instances of ipython"""
160 160
161 161 return ['--quick', # so no config file is loaded
162 162 # Other defaults to minimize side effects on stdout
163 163 '--colors=NoColor', '--no-term-title','--no-banner',
164 164 '--autocall=0']
165 165
166 166
167 167 def default_config():
168 168 """Return a config object with good defaults for testing."""
169 169 config = Config()
170 170 config.TerminalInteractiveShell.colors = 'NoColor'
171 171 config.TerminalTerminalInteractiveShell.term_title = False,
172 172 config.TerminalInteractiveShell.autocall = 0
173 173 config.HistoryManager.hist_file = u'test_hist.sqlite'
174 174 config.HistoryManager.db_cache_size = 10000
175 175 return config
176 176
177 177
178 178 def ipexec(fname, options=None):
179 179 """Utility to call 'ipython filename'.
180 180
181 181 Starts IPython witha minimal and safe configuration to make startup as fast
182 182 as possible.
183 183
184 184 Note that this starts IPython in a subprocess!
185 185
186 186 Parameters
187 187 ----------
188 188 fname : str
189 189 Name of file to be executed (should have .py or .ipy extension).
190 190
191 191 options : optional, list
192 192 Extra command-line flags to be passed to IPython.
193 193
194 194 Returns
195 195 -------
196 196 (stdout, stderr) of ipython subprocess.
197 197 """
198 198 if options is None: options = []
199 199
200 200 # For these subprocess calls, eliminate all prompt printing so we only see
201 201 # output from script execution
202 202 prompt_opts = [ '--InteractiveShell.prompt_in1=""',
203 203 '--InteractiveShell.prompt_in2=""',
204 204 '--InteractiveShell.prompt_out=""'
205 205 ]
206 206 cmdargs = ' '.join(default_argv() + prompt_opts + options)
207 207
208 208 _ip = get_ipython()
209 209 test_dir = os.path.dirname(__file__)
210 210
211 211 ipython_cmd = find_cmd('ipython')
212 212 # Absolute path for filename
213 213 full_fname = os.path.join(test_dir, fname)
214 214 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
215 215 #print >> sys.stderr, 'FULL CMD:', full_cmd # dbg
216 return getoutputerror(full_cmd)
216 out = getoutputerror(full_cmd)
217 # `import readline` causes 'ESC[?1034h' to be the first output sometimes,
218 # so strip that off the front of the first line if it is found
219 if out:
220 first = out[0]
221 m = re.match(r'\x1b\[[^h]+h', first)
222 if m:
223 # strip initial readline escape
224 out = list(out)
225 out[0] = first[len(m.group()):]
226 out = tuple(out)
227 return out
217 228
218 229
219 230 def ipexec_validate(fname, expected_out, expected_err='',
220 231 options=None):
221 232 """Utility to call 'ipython filename' and validate output/error.
222 233
223 234 This function raises an AssertionError if the validation fails.
224 235
225 236 Note that this starts IPython in a subprocess!
226 237
227 238 Parameters
228 239 ----------
229 240 fname : str
230 241 Name of the file to be executed (should have .py or .ipy extension).
231 242
232 243 expected_out : str
233 244 Expected stdout of the process.
234 245
235 246 expected_err : optional, str
236 247 Expected stderr of the process.
237 248
238 249 options : optional, list
239 250 Extra command-line flags to be passed to IPython.
240 251
241 252 Returns
242 253 -------
243 254 None
244 255 """
245 256
246 257 import nose.tools as nt
247 258
248 259 out, err = ipexec(fname)
249 260 #print 'OUT', out # dbg
250 261 #print 'ERR', err # dbg
251 262 # If there are any errors, we must check those befor stdout, as they may be
252 263 # more informative than simply having an empty stdout.
253 264 if err:
254 265 if expected_err:
255 266 nt.assert_equals(err.strip(), expected_err.strip())
256 267 else:
257 268 raise ValueError('Running file %r produced error: %r' %
258 269 (fname, err))
259 270 # If no errors or output on stderr was expected, match stdout
260 271 nt.assert_equals(out.strip(), expected_out.strip())
261 272
262 273
263 274 class TempFileMixin(object):
264 275 """Utility class to create temporary Python/IPython files.
265 276
266 277 Meant as a mixin class for test cases."""
267 278
268 279 def mktmp(self, src, ext='.py'):
269 280 """Make a valid python temp file."""
270 281 fname, f = temp_pyfile(src, ext)
271 282 self.tmpfile = f
272 283 self.fname = fname
273 284
274 285 def tearDown(self):
275 286 if hasattr(self, 'tmpfile'):
276 287 # If the tmpfile wasn't made because of skipped tests, like in
277 288 # win32, there's nothing to cleanup.
278 289 self.tmpfile.close()
279 290 try:
280 291 os.unlink(self.fname)
281 292 except:
282 293 # On Windows, even though we close the file, we still can't
283 294 # delete it. I have no clue why
284 295 pass
285 296
286 297 pair_fail_msg = ("Testing function {0}\n\n"
287 298 "In:\n"
288 299 " {1!r}\n"
289 300 "Expected:\n"
290 301 " {2!r}\n"
291 302 "Got:\n"
292 303 " {3!r}\n")
293 304 def check_pairs(func, pairs):
294 305 """Utility function for the common case of checking a function with a
295 306 sequence of input/output pairs.
296 307
297 308 Parameters
298 309 ----------
299 310 func : callable
300 311 The function to be tested. Should accept a single argument.
301 312 pairs : iterable
302 313 A list of (input, expected_output) tuples.
303 314
304 315 Returns
305 316 -------
306 317 None. Raises an AssertionError if any output does not match the expected
307 318 value.
308 319 """
309 320 for inp, expected in pairs:
310 321 out = func(inp)
311 322 assert out == expected, pair_fail_msg.format(func.func_name, inp, expected, out)
312 323
313 324 @contextmanager
314 325 def mute_warn():
315 326 from IPython.utils import warn
316 327 save_warn = warn.warn
317 328 warn.warn = lambda *a, **kw: None
318 329 try:
319 330 yield
320 331 finally:
321 332 warn.warn = save_warn No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now