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