##// END OF EJS Templates
cleanup unused bits
Paul Ivanov -
Show More
@@ -1,417 +1,414 b''
1 1 """Generic testing tools.
2 2
3 3 Authors
4 4 -------
5 5 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 6 """
7 7
8 8 from __future__ import absolute_import
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2009-2011 The IPython Development Team
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #-----------------------------------------------------------------------------
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20
21 21 import os
22 import pipes
23 22 import re
24 23 import sys
25 24 import tempfile
26 25
27 26 from contextlib import contextmanager
28 27 from io import StringIO
29 28 from subprocess import Popen, PIPE
30 29
31 30 try:
32 31 # These tools are used by parts of the runtime, so we make the nose
33 32 # dependency optional at this point. Nose is a hard dependency to run the
34 33 # test suite, but NOT to use ipython itself.
35 34 import nose.tools as nt
36 35 has_nose = True
37 36 except ImportError:
38 37 has_nose = False
39 38
40 39 from IPython.config.loader import Config
41 from IPython.utils.process import getoutputerror
42 40 from IPython.utils.text import list_strings
43 41 from IPython.utils.io import temp_pyfile, Tee
44 42 from IPython.utils import py3compat
45 43 from IPython.utils.encoding import DEFAULT_ENCODING
46 44
47 45 from . import decorators as dec
48 46 from . import skipdoctest
49 47
50 48 #-----------------------------------------------------------------------------
51 49 # Functions and classes
52 50 #-----------------------------------------------------------------------------
53 51
54 52 # The docstring for full_path doctests differently on win32 (different path
55 53 # separator) so just skip the doctest there. The example remains informative.
56 54 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
57 55
58 56 @doctest_deco
59 57 def full_path(startPath,files):
60 58 """Make full paths for all the listed files, based on startPath.
61 59
62 60 Only the base part of startPath is kept, since this routine is typically
63 61 used with a script's __file__ variable as startPath. The base of startPath
64 62 is then prepended to all the listed files, forming the output list.
65 63
66 64 Parameters
67 65 ----------
68 66 startPath : string
69 67 Initial path to use as the base for the results. This path is split
70 68 using os.path.split() and only its first component is kept.
71 69
72 70 files : string or list
73 71 One or more files.
74 72
75 73 Examples
76 74 --------
77 75
78 76 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
79 77 ['/foo/a.txt', '/foo/b.txt']
80 78
81 79 >>> full_path('/foo',['a.txt','b.txt'])
82 80 ['/a.txt', '/b.txt']
83 81
84 82 If a single file is given, the output is still a list:
85 83 >>> full_path('/foo','a.txt')
86 84 ['/a.txt']
87 85 """
88 86
89 87 files = list_strings(files)
90 88 base = os.path.split(startPath)[0]
91 89 return [ os.path.join(base,f) for f in files ]
92 90
93 91
94 92 def parse_test_output(txt):
95 93 """Parse the output of a test run and return errors, failures.
96 94
97 95 Parameters
98 96 ----------
99 97 txt : str
100 98 Text output of a test run, assumed to contain a line of one of the
101 99 following forms::
102 100
103 101 'FAILED (errors=1)'
104 102 'FAILED (failures=1)'
105 103 'FAILED (errors=1, failures=1)'
106 104
107 105 Returns
108 106 -------
109 107 nerr, nfail: number of errors and failures.
110 108 """
111 109
112 110 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
113 111 if err_m:
114 112 nerr = int(err_m.group(1))
115 113 nfail = 0
116 114 return nerr, nfail
117 115
118 116 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
119 117 if fail_m:
120 118 nerr = 0
121 119 nfail = int(fail_m.group(1))
122 120 return nerr, nfail
123 121
124 122 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
125 123 re.MULTILINE)
126 124 if both_m:
127 125 nerr = int(both_m.group(1))
128 126 nfail = int(both_m.group(2))
129 127 return nerr, nfail
130 128
131 129 # If the input didn't match any of these forms, assume no error/failures
132 130 return 0, 0
133 131
134 132
135 133 # So nose doesn't think this is a test
136 134 parse_test_output.__test__ = False
137 135
138 136
139 137 def default_argv():
140 138 """Return a valid default argv for creating testing instances of ipython"""
141 139
142 140 return ['--quick', # so no config file is loaded
143 141 # Other defaults to minimize side effects on stdout
144 142 '--colors=NoColor', '--no-term-title','--no-banner',
145 143 '--autocall=0']
146 144
147 145
148 146 def default_config():
149 147 """Return a config object with good defaults for testing."""
150 148 config = Config()
151 149 config.TerminalInteractiveShell.colors = 'NoColor'
152 150 config.TerminalTerminalInteractiveShell.term_title = False,
153 151 config.TerminalInteractiveShell.autocall = 0
154 152 config.HistoryManager.hist_file = tempfile.mktemp(u'test_hist.sqlite')
155 153 config.HistoryManager.db_cache_size = 10000
156 154 return config
157 155
158 156
159 157 def get_ipython_cmd(as_string=False):
160 158 """
161 159 Return appropriate IPython command line name. By default, this will return
162 160 a list that can be used with subprocess.Popen, for example, but passing
163 161 `as_string=True` allows for returning the IPython command as a string.
164 162
165 163 Parameters
166 164 ----------
167 165 as_string: bool
168 166 Flag to allow to return the command as a string.
169 167 """
170 168 # FIXME: remove workaround for 2.6 support
171 169 if sys.version_info[:2] > (2,6):
172 170 ipython_cmd = [sys.executable, "-m", "IPython"]
173 171 else:
174 172 ipython_cmd = ["ipython"]
175 173
176 174 if as_string:
177 175 ipython_cmd = " ".join(ipython_cmd)
178 176
179 177 return ipython_cmd
180 178
181 179 def ipexec(fname, options=None):
182 180 """Utility to call 'ipython filename'.
183 181
184 182 Starts IPython with a minimal and safe configuration to make startup as fast
185 183 as possible.
186 184
187 185 Note that this starts IPython in a subprocess!
188 186
189 187 Parameters
190 188 ----------
191 189 fname : str
192 190 Name of file to be executed (should have .py or .ipy extension).
193 191
194 192 options : optional, list
195 193 Extra command-line flags to be passed to IPython.
196 194
197 195 Returns
198 196 -------
199 197 (stdout, stderr) of ipython subprocess.
200 198 """
201 199 if options is None: options = []
202 200
203 201 # For these subprocess calls, eliminate all prompt printing so we only see
204 202 # output from script execution
205 203 prompt_opts = [ '--PromptManager.in_template=""',
206 204 '--PromptManager.in2_template=""',
207 205 '--PromptManager.out_template=""'
208 206 ]
209 207 cmdargs = default_argv() + prompt_opts + options
210 208
211 _ip = get_ipython()
212 209 test_dir = os.path.dirname(__file__)
213 210
214 211 ipython_cmd = get_ipython_cmd()
215 212 # Absolute path for filename
216 213 full_fname = os.path.join(test_dir, fname)
217 214 full_cmd = ipython_cmd + cmdargs + [full_fname]
218 215 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE)
219 216 out, err = p.communicate()
220 217 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
221 218 # `import readline` causes 'ESC[?1034h' to be output sometimes,
222 219 # so strip that out before doing comparisons
223 220 if out:
224 221 out = re.sub(r'\x1b\[[^h]+h', '', out)
225 222 return out, err
226 223
227 224
228 225 def ipexec_validate(fname, expected_out, expected_err='',
229 226 options=None):
230 227 """Utility to call 'ipython filename' and validate output/error.
231 228
232 229 This function raises an AssertionError if the validation fails.
233 230
234 231 Note that this starts IPython in a subprocess!
235 232
236 233 Parameters
237 234 ----------
238 235 fname : str
239 236 Name of the file to be executed (should have .py or .ipy extension).
240 237
241 238 expected_out : str
242 239 Expected stdout of the process.
243 240
244 241 expected_err : optional, str
245 242 Expected stderr of the process.
246 243
247 244 options : optional, list
248 245 Extra command-line flags to be passed to IPython.
249 246
250 247 Returns
251 248 -------
252 249 None
253 250 """
254 251
255 252 import nose.tools as nt
256 253
257 254 out, err = ipexec(fname, options)
258 255 #print 'OUT', out # dbg
259 256 #print 'ERR', err # dbg
260 257 # If there are any errors, we must check those befor stdout, as they may be
261 258 # more informative than simply having an empty stdout.
262 259 if err:
263 260 if expected_err:
264 261 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
265 262 else:
266 263 raise ValueError('Running file %r produced error: %r' %
267 264 (fname, err))
268 265 # If no errors or output on stderr was expected, match stdout
269 266 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
270 267
271 268
272 269 class TempFileMixin(object):
273 270 """Utility class to create temporary Python/IPython files.
274 271
275 272 Meant as a mixin class for test cases."""
276 273
277 274 def mktmp(self, src, ext='.py'):
278 275 """Make a valid python temp file."""
279 276 fname, f = temp_pyfile(src, ext)
280 277 self.tmpfile = f
281 278 self.fname = fname
282 279
283 280 def tearDown(self):
284 281 if hasattr(self, 'tmpfile'):
285 282 # If the tmpfile wasn't made because of skipped tests, like in
286 283 # win32, there's nothing to cleanup.
287 284 self.tmpfile.close()
288 285 try:
289 286 os.unlink(self.fname)
290 287 except:
291 288 # On Windows, even though we close the file, we still can't
292 289 # delete it. I have no clue why
293 290 pass
294 291
295 292 pair_fail_msg = ("Testing {0}\n\n"
296 293 "In:\n"
297 294 " {1!r}\n"
298 295 "Expected:\n"
299 296 " {2!r}\n"
300 297 "Got:\n"
301 298 " {3!r}\n")
302 299 def check_pairs(func, pairs):
303 300 """Utility function for the common case of checking a function with a
304 301 sequence of input/output pairs.
305 302
306 303 Parameters
307 304 ----------
308 305 func : callable
309 306 The function to be tested. Should accept a single argument.
310 307 pairs : iterable
311 308 A list of (input, expected_output) tuples.
312 309
313 310 Returns
314 311 -------
315 312 None. Raises an AssertionError if any output does not match the expected
316 313 value.
317 314 """
318 315 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
319 316 for inp, expected in pairs:
320 317 out = func(inp)
321 318 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
322 319
323 320
324 321 if py3compat.PY3:
325 322 MyStringIO = StringIO
326 323 else:
327 324 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
328 325 # so we need a class that can handle both.
329 326 class MyStringIO(StringIO):
330 327 def write(self, s):
331 328 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
332 329 super(MyStringIO, self).write(s)
333 330
334 331 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
335 332 -------
336 333 {2!s}
337 334 -------
338 335 """
339 336
340 337 class AssertPrints(object):
341 338 """Context manager for testing that code prints certain text.
342 339
343 340 Examples
344 341 --------
345 342 >>> with AssertPrints("abc", suppress=False):
346 343 ... print "abcd"
347 344 ... print "def"
348 345 ...
349 346 abcd
350 347 def
351 348 """
352 349 def __init__(self, s, channel='stdout', suppress=True):
353 350 self.s = s
354 351 self.channel = channel
355 352 self.suppress = suppress
356 353
357 354 def __enter__(self):
358 355 self.orig_stream = getattr(sys, self.channel)
359 356 self.buffer = MyStringIO()
360 357 self.tee = Tee(self.buffer, channel=self.channel)
361 358 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
362 359
363 360 def __exit__(self, etype, value, traceback):
364 361 self.tee.flush()
365 362 setattr(sys, self.channel, self.orig_stream)
366 363 printed = self.buffer.getvalue()
367 364 assert self.s in printed, notprinted_msg.format(self.s, self.channel, printed)
368 365 return False
369 366
370 367 printed_msg = """Found {0!r} in printed output (on {1}):
371 368 -------
372 369 {2!s}
373 370 -------
374 371 """
375 372
376 373 class AssertNotPrints(AssertPrints):
377 374 """Context manager for checking that certain output *isn't* produced.
378 375
379 376 Counterpart of AssertPrints"""
380 377 def __exit__(self, etype, value, traceback):
381 378 self.tee.flush()
382 379 setattr(sys, self.channel, self.orig_stream)
383 380 printed = self.buffer.getvalue()
384 381 assert self.s not in printed, printed_msg.format(self.s, self.channel, printed)
385 382 return False
386 383
387 384 @contextmanager
388 385 def mute_warn():
389 386 from IPython.utils import warn
390 387 save_warn = warn.warn
391 388 warn.warn = lambda *a, **kw: None
392 389 try:
393 390 yield
394 391 finally:
395 392 warn.warn = save_warn
396 393
397 394 @contextmanager
398 395 def make_tempfile(name):
399 396 """ Create an empty, named, temporary file for the duration of the context.
400 397 """
401 398 f = open(name, 'w')
402 399 f.close()
403 400 try:
404 401 yield
405 402 finally:
406 403 os.unlink(name)
407 404
408 405
409 406 @contextmanager
410 407 def monkeypatch(obj, name, attr):
411 408 """
412 409 Context manager to replace attribute named `name` in `obj` with `attr`.
413 410 """
414 411 orig = getattr(obj, name)
415 412 setattr(obj, name, attr)
416 413 yield
417 414 setattr(obj, name, orig)
General Comments 0
You need to be logged in to leave comments. Login now