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