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