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