##// END OF EJS Templates
Massive amount of work to improve the test suite, restores doctests....
Fernando Perez -
Show More
@@ -0,0 +1,185 b''
1 """Tests for code execution (%run and related), which is particularly tricky.
2
3 Because of how %run manages namespaces, and the fact that we are trying here to
4 verify subtle object deletion and reference counting issues, the %run tests
5 will be kept in this separate file. This makes it easier to aggregate in one
6 place the tricks needed to handle it; most other magics are much easier to test
7 and we do so in a common test_magic file.
8 """
9 from __future__ import absolute_import
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 # stdlib
16 import os
17 import sys
18 import tempfile
19
20 # third-party
21 import nose.tools as nt
22
23 # our own
24 from IPython.utils.platutils import find_cmd
25 from IPython.utils import genutils
26 from IPython.testing import decorators as dec
27 from IPython.testing import tools as tt
28
29 #-----------------------------------------------------------------------------
30 # Test functions begin
31 #-----------------------------------------------------------------------------
32
33 def doctest_refbug():
34 """Very nasty problem with references held by multiple runs of a script.
35 See: https://bugs.launchpad.net/ipython/+bug/269966
36
37 In [1]: _ip.clear_main_mod_cache()
38 # random
39
40 In [2]: %run refbug
41
42 In [3]: call_f()
43 lowercased: hello
44
45 In [4]: %run refbug
46
47 In [5]: call_f()
48 lowercased: hello
49 lowercased: hello
50 """
51
52
53 def doctest_run_builtins():
54 r"""Check that %run doesn't damage __builtins__.
55
56 In [1]: import tempfile
57
58 In [2]: bid1 = id(__builtins__)
59
60 In [3]: fname = tempfile.mkstemp('.py')[1]
61
62 In [3]: f = open(fname,'w')
63
64 In [4]: f.write('pass\n')
65
66 In [5]: f.flush()
67
68 In [6]: t1 = type(__builtins__)
69
70 In [7]: %run "$fname"
71
72 In [7]: f.close()
73
74 In [8]: bid2 = id(__builtins__)
75
76 In [9]: t2 = type(__builtins__)
77
78 In [10]: t1 == t2
79 Out[10]: True
80
81 In [10]: bid1 == bid2
82 Out[10]: True
83
84 In [12]: try:
85 ....: os.unlink(fname)
86 ....: except:
87 ....: pass
88 ....:
89 """
90
91 # For some tests, it will be handy to organize them in a class with a common
92 # setup that makes a temp file
93
94 class TempFileMixin(object):
95 def mktmp(self, src, ext='.py'):
96 """Make a valid python temp file."""
97 fname, f = tt.temp_pyfile(src, ext)
98 self.tmpfile = f
99 self.fname = fname
100
101 def teardown(self):
102 self.tmpfile.close()
103 try:
104 os.unlink(self.fname)
105 except:
106 # On Windows, even though we close the file, we still can't delete
107 # it. I have no clue why
108 pass
109
110
111 class TestMagicRunPass(TempFileMixin):
112
113 def setup(self):
114 """Make a valid python temp file."""
115 self.mktmp('pass\n')
116
117 def run_tmpfile(self):
118 _ip = get_ipython()
119 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
120 # See below and ticket https://bugs.launchpad.net/bugs/366353
121 _ip.magic('run "%s"' % self.fname)
122
123 def test_builtins_id(self):
124 """Check that %run doesn't damage __builtins__ """
125 _ip = get_ipython()
126 # Test that the id of __builtins__ is not modified by %run
127 bid1 = id(_ip.user_ns['__builtins__'])
128 self.run_tmpfile()
129 bid2 = id(_ip.user_ns['__builtins__'])
130 tt.assert_equals(bid1, bid2)
131
132 def test_builtins_type(self):
133 """Check that the type of __builtins__ doesn't change with %run.
134
135 However, the above could pass if __builtins__ was already modified to
136 be a dict (it should be a module) by a previous use of %run. So we
137 also check explicitly that it really is a module:
138 """
139 _ip = get_ipython()
140 self.run_tmpfile()
141 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
142
143 def test_prompts(self):
144 """Test that prompts correctly generate after %run"""
145 self.run_tmpfile()
146 _ip = get_ipython()
147 p2 = str(_ip.outputcache.prompt2).strip()
148 nt.assert_equals(p2[:3], '...')
149
150
151 class TestMagicRunSimple(TempFileMixin):
152
153 def test_simpledef(self):
154 """Test that simple class definitions work."""
155 src = ("class foo: pass\n"
156 "def f(): return foo()")
157 self.mktmp(src)
158 _ip.magic('run "%s"' % self.fname)
159 _ip.runlines('t = isinstance(f(), foo)')
160 nt.assert_true(_ip.user_ns['t'])
161
162 def test_obj_del(self):
163 """Test that object's __del__ methods are called on exit."""
164
165 # This test is known to fail on win32.
166 # See ticket https://bugs.launchpad.net/bugs/366334
167 src = ("class A(object):\n"
168 " def __del__(self):\n"
169 " print 'object A deleted'\n"
170 "a = A()\n")
171 self.mktmp(src)
172 tt.ipexec_validate(self.fname, 'object A deleted')
173
174 def test_tclass(self):
175 mydir = os.path.dirname(__file__)
176 tc = os.path.join(mydir, 'tclass')
177 src = ("%%run '%s' C-first\n"
178 "%%run '%s' C-second\n") % (tc, tc)
179 self.mktmp(src, '.ipy')
180 out = """\
181 ARGV 1-: ['C-first']
182 ARGV 1-: ['C-second']
183 tclass.py: deleting object: C-first
184 """
185 tt.ipexec_validate(self.fname, out)
@@ -3506,6 +3506,7 b' Defaulting color scheme to \'NoColor\'"""'
3506 """Reload an IPython extension by its module name."""
3506 """Reload an IPython extension by its module name."""
3507 self.reload_extension(module_str)
3507 self.reload_extension(module_str)
3508
3508
3509 @testdec.skip_doctest
3509 def magic_install_profiles(self, s):
3510 def magic_install_profiles(self, s):
3510 """Install the default IPython profiles into the .ipython dir.
3511 """Install the default IPython profiles into the .ipython dir.
3511
3512
@@ -416,7 +416,7 b' class PrefilterManager(Component):'
416 # print "prefiltered line: %r" % prefiltered
416 # print "prefiltered line: %r" % prefiltered
417 return prefiltered
417 return prefiltered
418
418
419 def prefilter_lines(self, lines, continue_prompt):
419 def prefilter_lines(self, lines, continue_prompt=False):
420 """Prefilter multiple input lines of text.
420 """Prefilter multiple input lines of text.
421
421
422 This is the main entry point for prefiltering multiple lines of
422 This is the main entry point for prefiltering multiple lines of
@@ -1,16 +1,12 b''
1 """Simple script to instantiate a class for testing %run"""
1 """Simple script to be run *twice*, to check reference counting bugs.
2
2
3 import sys
3 See test_run for details."""
4
5 # An external test will check that calls to f() work after %run
6 class foo: pass
7
4
8 def f():
5 import sys
9 return foo()
10
6
11 # We also want to ensure that while objects remain available for immediate
7 # We want to ensure that while objects remain available for immediate access,
12 # access, objects from *previous* runs of the same script get collected, to
8 # objects from *previous* runs of the same script get collected, to avoid
13 # avoid accumulating massive amounts of old references.
9 # accumulating massive amounts of old references.
14 class C(object):
10 class C(object):
15 def __init__(self,name):
11 def __init__(self,name):
16 self.name = name
12 self.name = name
@@ -18,6 +14,7 b' class C(object):'
18 def __del__(self):
14 def __del__(self):
19 print 'tclass.py: deleting object:',self.name
15 print 'tclass.py: deleting object:',self.name
20
16
17
21 try:
18 try:
22 name = sys.argv[1]
19 name = sys.argv[1]
23 except IndexError:
20 except IndexError:
@@ -25,3 +22,8 b' except IndexError:'
25 else:
22 else:
26 if name.startswith('C'):
23 if name.startswith('C'):
27 c = C(name)
24 c = C(name)
25
26 #print >> sys.stderr, "ARGV:", sys.argv # dbg
27 # This print statement is NOT debugging, we're making the check on a completely
28 # separate process so we verify by capturing stdout.
29 print 'ARGV 1-:', sys.argv[1:]
@@ -2,21 +2,31 b''
2
2
3 Needs to be run by nose (to make ipython session available).
3 Needs to be run by nose (to make ipython session available).
4 """
4 """
5 from __future__ import absolute_import
5
6
7 #-----------------------------------------------------------------------------
8 # Imports
9 #-----------------------------------------------------------------------------
10
11 # stdlib
6 import os
12 import os
7 import sys
13 import sys
8 import tempfile
14 import tempfile
9 import types
15 import types
10 from cStringIO import StringIO
16 from cStringIO import StringIO
11
17
18 # third-party
12 import nose.tools as nt
19 import nose.tools as nt
13
20
21 # our own
22 from IPython.utils import genutils
14 from IPython.utils.platutils import find_cmd, get_long_path_name
23 from IPython.utils.platutils import find_cmd, get_long_path_name
15 from IPython.testing import decorators as dec
24 from IPython.testing import decorators as dec
16 from IPython.testing import tools as tt
25 from IPython.testing import tools as tt
17
26
18 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
19 # Test functions begin
28 # Test functions begin
29 #-----------------------------------------------------------------------------
20
30
21 def test_rehashx():
31 def test_rehashx():
22 # clear up everything
32 # clear up everything
@@ -63,17 +73,6 b' def doctest_hist_r():'
63 hist -n -r 2 # random
73 hist -n -r 2 # random
64 """
74 """
65
75
66 # This test is known to fail on win32.
67 # See ticket https://bugs.launchpad.net/bugs/366334
68 def test_obj_del():
69 _ip = get_ipython()
70 """Test that object's __del__ methods are called on exit."""
71 test_dir = os.path.dirname(__file__)
72 del_file = os.path.join(test_dir,'obj_del.py')
73 ipython_cmd = find_cmd('ipython')
74 out = _ip.getoutput('%s %s' % (ipython_cmd, del_file))
75 nt.assert_equals(out,'obj_del.py: object A deleted')
76
77
76
78 def test_shist():
77 def test_shist():
79 # Simple tests of ShadowHist class - test generator.
78 # Simple tests of ShadowHist class - test generator.
@@ -113,161 +112,6 b' def test_numpy_clear_array_undec():'
113 yield (nt.assert_false, 'a' in _ip.user_ns)
112 yield (nt.assert_false, 'a' in _ip.user_ns)
114
113
115
114
116 @dec.skip()
117 def test_fail_dec(*a,**k):
118 yield nt.assert_true, False
119
120 @dec.skip('This one shouldn not run')
121 def test_fail_dec2(*a,**k):
122 yield nt.assert_true, False
123
124 @dec.skipknownfailure
125 def test_fail_dec3(*a,**k):
126 yield nt.assert_true, False
127
128
129 def doctest_refbug():
130 """Very nasty problem with references held by multiple runs of a script.
131 See: https://bugs.launchpad.net/ipython/+bug/269966
132
133 In [1]: _ip.clear_main_mod_cache()
134
135 In [2]: run refbug
136
137 In [3]: call_f()
138 lowercased: hello
139
140 In [4]: run refbug
141
142 In [5]: call_f()
143 lowercased: hello
144 lowercased: hello
145 """
146
147 #-----------------------------------------------------------------------------
148 # Tests for %run
149 #-----------------------------------------------------------------------------
150
151 # %run is critical enough that it's a good idea to have a solid collection of
152 # tests for it, some as doctests and some as normal tests.
153
154 def doctest_run_ns():
155 """Classes declared %run scripts must be instantiable afterwards.
156
157 In [11]: run tclass foo
158
159 In [12]: isinstance(f(),foo)
160 Out[12]: True
161 """
162
163
164 def doctest_run_ns2():
165 """Classes declared %run scripts must be instantiable afterwards.
166
167 In [4]: run tclass C-first_pass
168
169 In [5]: run tclass C-second_pass
170 tclass.py: deleting object: C-first_pass
171 """
172
173 def doctest_run_builtins():
174 """Check that %run doesn't damage __builtins__ via a doctest.
175
176 This is similar to the test_run_builtins, but I want *both* forms of the
177 test to catch any possible glitches in our testing machinery, since that
178 modifies %run somewhat. So for this, we have both a normal test (below)
179 and a doctest (this one).
180
181 In [1]: import tempfile
182
183 In [2]: bid1 = id(__builtins__)
184
185 In [3]: fname = tempfile.mkstemp()[1]
186
187 In [3]: f = open(fname,'w')
188
189 In [4]: f.write('pass\\n')
190
191 In [5]: f.flush()
192
193 In [6]: print type(__builtins__)
194 <type 'module'>
195
196 In [7]: %run "$fname"
197
198 In [7]: f.close()
199
200 In [8]: bid2 = id(__builtins__)
201
202 In [9]: print type(__builtins__)
203 <type 'module'>
204
205 In [10]: bid1 == bid2
206 Out[10]: True
207
208 In [12]: try:
209 ....: os.unlink(fname)
210 ....: except:
211 ....: pass
212 ....:
213 """
214
215 # For some tests, it will be handy to organize them in a class with a common
216 # setup that makes a temp file
217
218 class TestMagicRun(object):
219
220 def setup(self):
221 """Make a valid python temp file."""
222 fname = tempfile.mkstemp('.py')[1]
223 f = open(fname,'w')
224 f.write('pass\n')
225 f.flush()
226 self.tmpfile = f
227 self.fname = fname
228
229 def run_tmpfile(self):
230 _ip = get_ipython()
231 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
232 # See below and ticket https://bugs.launchpad.net/bugs/366353
233 _ip.magic('run "%s"' % self.fname)
234
235 def test_builtins_id(self):
236 """Check that %run doesn't damage __builtins__ """
237 _ip = get_ipython()
238 # Test that the id of __builtins__ is not modified by %run
239 bid1 = id(_ip.user_ns['__builtins__'])
240 self.run_tmpfile()
241 bid2 = id(_ip.user_ns['__builtins__'])
242 tt.assert_equals(bid1, bid2)
243
244 def test_builtins_type(self):
245 """Check that the type of __builtins__ doesn't change with %run.
246
247 However, the above could pass if __builtins__ was already modified to
248 be a dict (it should be a module) by a previous use of %run. So we
249 also check explicitly that it really is a module:
250 """
251 _ip = get_ipython()
252 self.run_tmpfile()
253 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
254
255 def test_prompts(self):
256 """Test that prompts correctly generate after %run"""
257 self.run_tmpfile()
258 _ip = get_ipython()
259 p2 = str(_ip.outputcache.prompt2).strip()
260 nt.assert_equals(p2[:3], '...')
261
262 def teardown(self):
263 self.tmpfile.close()
264 try:
265 os.unlink(self.fname)
266 except:
267 # On Windows, even though we close the file, we still can't delete
268 # it. I have no clue why
269 pass
270
271 # Multiple tests for clipboard pasting
115 # Multiple tests for clipboard pasting
272 @dec.parametric
116 @dec.parametric
273 def test_paste():
117 def test_paste():
@@ -64,6 +64,9 b" if sys.version[0]=='2':"
64 else:
64 else:
65 from _paramtestpy3 import parametric
65 from _paramtestpy3 import parametric
66
66
67 # Expose the unittest-driven decorators
68 from ipunittest import ipdoctest, ipdocstring
69
67 # Grab the numpy-specific decorators which we keep in a file that we
70 # Grab the numpy-specific decorators which we keep in a file that we
68 # occasionally update from upstream: decorators.py is a copy of
71 # occasionally update from upstream: decorators.py is a copy of
69 # numpy.testing.decorators, we expose all of it here.
72 # numpy.testing.decorators, we expose all of it here.
@@ -16,6 +16,8 b' For now, this script requires that both nose and twisted are installed. This'
16 will change in the future.
16 will change in the future.
17 """
17 """
18
18
19 from __future__ import absolute_import
20
19 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
20 # Module imports
22 # Module imports
21 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
@@ -34,6 +36,8 b' from nose.core import TestProgram'
34
36
35 from IPython.utils import genutils
37 from IPython.utils import genutils
36 from IPython.utils.platutils import find_cmd, FindCmdError
38 from IPython.utils.platutils import find_cmd, FindCmdError
39 from . import globalipapp
40 from .plugin.ipdoctest import IPythonDoctest
37
41
38 pjoin = path.join
42 pjoin = path.join
39
43
@@ -76,7 +80,11 b' def make_exclude():'
76 # cause testing problems. We should strive to minimize the number of
80 # cause testing problems. We should strive to minimize the number of
77 # skipped modules, since this means untested code. As the testing
81 # skipped modules, since this means untested code. As the testing
78 # machinery solidifies, this list should eventually become empty.
82 # machinery solidifies, this list should eventually become empty.
79 EXCLUDE = [pjoin('IPython', 'external'),
83
84 # Note that these exclusions only mean that the docstrings are not analyzed
85 # for examples to be run as tests, if there are other test functions in
86 # those modules, they do get run.
87 exclusions = [pjoin('IPython', 'external'),
80 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
88 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
81 pjoin('IPython_doctest_plugin'),
89 pjoin('IPython_doctest_plugin'),
82 pjoin('IPython', 'quarantine'),
90 pjoin('IPython', 'quarantine'),
@@ -88,58 +96,58 b' def make_exclude():'
88 ]
96 ]
89
97
90 if not have_wx:
98 if not have_wx:
91 EXCLUDE.append(pjoin('IPython', 'gui'))
99 exclusions.append(pjoin('IPython', 'gui'))
92 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
100 exclusions.append(pjoin('IPython', 'frontend', 'wx'))
93 EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookwx'))
101 exclusions.append(pjoin('IPython', 'lib', 'inputhookwx'))
94
102
95 if not have_gtk or not have_gobject:
103 if not have_gtk or not have_gobject:
96 EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookgtk'))
104 exclusions.append(pjoin('IPython', 'lib', 'inputhookgtk'))
97
105
98 if not have_wx_aui:
106 if not have_wx_aui:
99 EXCLUDE.append(pjoin('IPython', 'gui', 'wx', 'wxIPython'))
107 exclusions.append(pjoin('IPython', 'gui', 'wx', 'wxIPython'))
100
108
101 if not have_objc:
109 if not have_objc:
102 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
110 exclusions.append(pjoin('IPython', 'frontend', 'cocoa'))
103
111
104 if not sys.platform == 'win32':
112 if not sys.platform == 'win32':
105 EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_win32'))
113 exclusions.append(pjoin('IPython', 'utils', 'platutils_win32'))
106
114
107 # These have to be skipped on win32 because the use echo, rm, cd, etc.
115 # These have to be skipped on win32 because the use echo, rm, cd, etc.
108 # See ticket https://bugs.launchpad.net/bugs/366982
116 # See ticket https://bugs.launchpad.net/bugs/366982
109 if sys.platform == 'win32':
117 if sys.platform == 'win32':
110 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
118 exclusions.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
111 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
119 exclusions.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
112
120
113 if not os.name == 'posix':
121 if not os.name == 'posix':
114 EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_posix'))
122 exclusions.append(pjoin('IPython', 'utils', 'platutils_posix'))
115
123
116 if not have_pexpect:
124 if not have_pexpect:
117 EXCLUDE.append(pjoin('IPython', 'scripts', 'irunner'))
125 exclusions.append(pjoin('IPython', 'scripts', 'irunner'))
118
126
119 # This is scary. We still have things in frontend and testing that
127 # This is scary. We still have things in frontend and testing that
120 # are being tested by nose that use twisted. We need to rethink
128 # are being tested by nose that use twisted. We need to rethink
121 # how we are isolating dependencies in testing.
129 # how we are isolating dependencies in testing.
122 if not (have_twisted and have_zi and have_foolscap):
130 if not (have_twisted and have_zi and have_foolscap):
123 EXCLUDE.append(pjoin('IPython', 'frontend', 'asyncfrontendbase'))
131 exclusions.append(pjoin('IPython', 'frontend', 'asyncfrontendbase'))
124 EXCLUDE.append(pjoin('IPython', 'frontend', 'prefilterfrontend'))
132 exclusions.append(pjoin('IPython', 'frontend', 'prefilterfrontend'))
125 EXCLUDE.append(pjoin('IPython', 'frontend', 'frontendbase'))
133 exclusions.append(pjoin('IPython', 'frontend', 'frontendbase'))
126 EXCLUDE.append(pjoin('IPython', 'frontend', 'linefrontendbase'))
134 exclusions.append(pjoin('IPython', 'frontend', 'linefrontendbase'))
127 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
135 exclusions.append(pjoin('IPython', 'frontend', 'tests',
128 'test_linefrontend'))
136 'test_linefrontend'))
129 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
137 exclusions.append(pjoin('IPython', 'frontend', 'tests',
130 'test_frontendbase'))
138 'test_frontendbase'))
131 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
139 exclusions.append(pjoin('IPython', 'frontend', 'tests',
132 'test_prefilterfrontend'))
140 'test_prefilterfrontend'))
133 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
141 exclusions.append(pjoin('IPython', 'frontend', 'tests',
134 'test_asyncfrontendbase')),
142 'test_asyncfrontendbase')),
135 EXCLUDE.append(pjoin('IPython', 'testing', 'parametric'))
143 exclusions.append(pjoin('IPython', 'testing', 'parametric'))
136 EXCLUDE.append(pjoin('IPython', 'testing', 'util'))
144 exclusions.append(pjoin('IPython', 'testing', 'util'))
137
145
138 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
146 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
139 if sys.platform == 'win32':
147 if sys.platform == 'win32':
140 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
148 exclusions = [s.replace('\\','\\\\') for s in exclusions]
141
149
142 return EXCLUDE
150 return exclusions
143
151
144
152
145 #-----------------------------------------------------------------------------
153 #-----------------------------------------------------------------------------
@@ -163,16 +171,16 b' class IPTester(object):'
163 if runner == 'iptest':
171 if runner == 'iptest':
164 # Find our own 'iptest' script OS-level entry point
172 # Find our own 'iptest' script OS-level entry point
165 try:
173 try:
166 iptest_path = find_cmd('iptest')
174 iptest_path = os.path.abspath(find_cmd('iptest'))
167 except FindCmdError:
175 except FindCmdError:
168 # Script not installed (may be the case for testing situations
176 # Script not installed (may be the case for testing situations
169 # that are running from a source tree only), pull from internal
177 # that are running from a source tree only), pull from internal
170 # path:
178 # path:
171 iptest_path = pjoin(genutils.get_ipython_package_dir(),
179 iptest_path = pjoin(genutils.get_ipython_package_dir(),
172 'scripts','iptest')
180 'scripts','iptest')
173 self.runner = [iptest_path,'-v']
181 self.runner = ['python', iptest_path, '-v']
174 else:
182 else:
175 self.runner = [find_cmd('trial')]
183 self.runner = ['python', os.path.abspath(find_cmd('trial'))]
176 if params is None:
184 if params is None:
177 params = []
185 params = []
178 if isinstance(params,str):
186 if isinstance(params,str):
@@ -238,11 +246,13 b' def make_runners():'
238 nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib',
246 nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib',
239 'scripts', 'testing', 'utils']
247 'scripts', 'testing', 'utils']
240 trial_packages = ['kernel']
248 trial_packages = ['kernel']
241 #trial_packages = [] # dbg
242
249
243 if have_wx:
250 if have_wx:
244 nose_packages.append('gui')
251 nose_packages.append('gui')
245
252
253 #nose_packages = ['core'] # dbg
254 #trial_packages = [] # dbg
255
246 nose_packages = ['IPython.%s' % m for m in nose_packages ]
256 nose_packages = ['IPython.%s' % m for m in nose_packages ]
247 trial_packages = ['IPython.%s' % m for m in trial_packages ]
257 trial_packages = ['IPython.%s' % m for m in trial_packages ]
248
258
@@ -268,15 +278,14 b' def run_iptest():'
268 warnings.filterwarnings('ignore',
278 warnings.filterwarnings('ignore',
269 'This will be removed soon. Use IPython.testing.util instead')
279 'This will be removed soon. Use IPython.testing.util instead')
270
280
271 argv = sys.argv + [
281 argv = sys.argv + [ '--detailed-errors',
272 # Loading ipdoctest causes problems with Twisted.
282 # Loading ipdoctest causes problems with Twisted, but
273 # I am removing this as a temporary fix to get the
283 # our test suite runner now separates things and runs
274 # test suite back into working shape. Our nose
284 # all Twisted tests with trial.
275 # plugin needs to be gone through with a fine
285 '--with-ipdoctest',
276 # toothed comb to find what is causing the problem.
286 '--ipdoctest-tests','--ipdoctest-extension=txt',
277 # '--with-ipdoctest',
287
278 # '--ipdoctest-tests','--ipdoctest-extension=txt',
288 #'-x','-s', # dbg
279 # '--detailed-errors',
280
289
281 # We add --exe because of setuptools' imbecility (it
290 # We add --exe because of setuptools' imbecility (it
282 # blindly does chmod +x on ALL files). Nose does the
291 # blindly does chmod +x on ALL files). Nose does the
@@ -300,17 +309,18 b' def run_iptest():'
300 if not has_tests:
309 if not has_tests:
301 argv.append('IPython')
310 argv.append('IPython')
302
311
303 # Construct list of plugins, omitting the existing doctest plugin, which
312 ## # Construct list of plugins, omitting the existing doctest plugin, which
304 # ours replaces (and extends).
313 ## # ours replaces (and extends).
305 EXCLUDE = make_exclude()
314 plugins = [IPythonDoctest(make_exclude())]
306 plugins = []
307 # plugins = [IPythonDoctest(EXCLUDE)]
308 for p in nose.plugins.builtin.plugins:
315 for p in nose.plugins.builtin.plugins:
309 plug = p()
316 plug = p()
310 if plug.name == 'doctest':
317 if plug.name == 'doctest':
311 continue
318 continue
312 plugins.append(plug)
319 plugins.append(plug)
313
320
321 # We need a global ipython running in this process
322 globalipapp.start_ipython()
323 # Now nose can run
314 TestProgram(argv=argv,plugins=plugins)
324 TestProgram(argv=argv,plugins=plugins)
315
325
316
326
@@ -22,6 +22,8 b' Authors'
22 - Fernando Perez <Fernando.Perez@berkeley.edu>
22 - Fernando Perez <Fernando.Perez@berkeley.edu>
23 """
23 """
24
24
25 from __future__ import absolute_import
26
25 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
26 # Copyright (C) 2009 The IPython Development Team
28 # Copyright (C) 2009 The IPython Development Team
27 #
29 #
@@ -40,14 +42,16 b' import sys'
40 import unittest
42 import unittest
41 from doctest import DocTestFinder, DocTestRunner, TestResults
43 from doctest import DocTestFinder, DocTestRunner, TestResults
42
44
43 # Our own
45 # Our own, a nose monkeypatch
44 import nosepatch
46 from . import nosepatch
45
47
46 # We already have python3-compliant code for parametric tests
48 # We already have python3-compliant code for parametric tests
47 if sys.version[0]=='2':
49 if sys.version[0]=='2':
48 from _paramtestpy2 import ParametricTestCase
50 from ._paramtestpy2 import ParametricTestCase
49 else:
51 else:
50 from _paramtestpy3 import ParametricTestCase
52 from ._paramtestpy3 import ParametricTestCase
53
54 from . import globalipapp
51
55
52 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
53 # Classes and functions
57 # Classes and functions
@@ -68,9 +72,13 b' class IPython2PythonConverter(object):'
68 implementation, but for now it only does prompt convertion."""
72 implementation, but for now it only does prompt convertion."""
69
73
70 def __init__(self):
74 def __init__(self):
71 self.ps1 = re.compile(r'In\ \[\d+\]: ')
75 self.rps1 = re.compile(r'In\ \[\d+\]: ')
72 self.ps2 = re.compile(r'\ \ \ \.\.\.+: ')
76 self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
73 self.out = re.compile(r'Out\[\d+\]: \s*?\n?')
77 self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
78 self.pyps1 = '>>> '
79 self.pyps2 = '... '
80 self.rpyps1 = re.compile ('(\s*%s)(.*)$' % self.pyps1)
81 self.rpyps2 = re.compile ('(\s*%s)(.*)$' % self.pyps2)
74
82
75 def __call__(self, ds):
83 def __call__(self, ds):
76 """Convert IPython prompts to python ones in a string."""
84 """Convert IPython prompts to python ones in a string."""
@@ -79,10 +87,34 b' class IPython2PythonConverter(object):'
79 pyout = ''
87 pyout = ''
80
88
81 dnew = ds
89 dnew = ds
82 dnew = self.ps1.sub(pyps1, dnew)
90 dnew = self.rps1.sub(pyps1, dnew)
83 dnew = self.ps2.sub(pyps2, dnew)
91 dnew = self.rps2.sub(pyps2, dnew)
84 dnew = self.out.sub(pyout, dnew)
92 dnew = self.rout.sub(pyout, dnew)
85 return dnew
93 ip = globalipapp.get_ipython()
94
95 # Convert input IPython source into valid Python.
96 out = []
97 newline = out.append
98 for line in dnew.splitlines():
99
100 mps1 = self.rpyps1.match(line)
101 if mps1 is not None:
102 prompt, text = mps1.groups()
103 newline(prompt+ip.prefilter(text, False))
104 continue
105
106 mps2 = self.rpyps2.match(line)
107 if mps2 is not None:
108 prompt, text = mps2.groups()
109 newline(prompt+ip.prefilter(text, True))
110 continue
111
112 newline(line)
113 newline('') # ensure a closing newline, needed by doctest
114 #print "PYSRC:", '\n'.join(out) # dbg
115 return '\n'.join(out)
116
117 #return dnew
86
118
87
119
88 class Doc2UnitTester(object):
120 class Doc2UnitTester(object):
@@ -49,183 +49,14 b' from nose.util import anyp, getpackage, test_address, resolve_name, tolist'
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Module globals and other constants
51 # Module globals and other constants
52 #-----------------------------------------------------------------------------
52
53
53 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
54
55
55 ###########################################################################
56 # *** HACK ***
57 # We must start our own ipython object and heavily muck with it so that all the
58 # modifications IPython makes to system behavior don't send the doctest
59 # machinery into a fit. This code should be considered a gross hack, but it
60 # gets the job done.
61
62 def default_argv():
63 """Return a valid default argv for creating testing instances of ipython"""
64
65 # Get the install directory for the user configuration and tell ipython to
66 # use the default profile from there.
67 from IPython.config import default
68 ipcdir = os.path.dirname(default.__file__)
69 ipconf = os.path.join(ipcdir,'ipython_config.py')
70 #print 'conf:',ipconf # dbg
71 return ['--colors=NoColor', '--no-term-title','--no-banner',
72 '--config-file=%s' % ipconf]
73
74
75 # Hack to modify the %run command so we can sync the user's namespace with the
76 # test globals. Once we move over to a clean magic system, this will be done
77 # with much less ugliness.
78
79 class py_file_finder(object):
80 def __init__(self,test_filename):
81 self.test_filename = test_filename
82
83 def __call__(self,name):
84 from IPython.utils.genutils import get_py_filename
85 try:
86 return get_py_filename(name)
87 except IOError:
88 test_dir = os.path.dirname(self.test_filename)
89 new_path = os.path.join(test_dir,name)
90 return get_py_filename(new_path)
91
92
93 def _run_ns_sync(self,arg_s,runner=None):
94 """Modified version of %run that syncs testing namespaces.
95
96 This is strictly needed for running doctests that call %run.
97 """
98
99 # When tests call %run directly (not via doctest) these function attributes
100 # are not set
101 try:
102 fname = _run_ns_sync.test_filename
103 except AttributeError:
104 fname = arg_s
105
106 finder = py_file_finder(fname)
107 out = _ip.magic_run_ori(arg_s,runner,finder)
108
109 # Simliarly, there is no test_globs when a test is NOT a doctest
110 if hasattr(_run_ns_sync,'test_globs'):
111 _run_ns_sync.test_globs.update(_ip.user_ns)
112 return out
113
114
115 class ipnsdict(dict):
116 """A special subclass of dict for use as an IPython namespace in doctests.
117
118 This subclass adds a simple checkpointing capability so that when testing
119 machinery clears it (we use it as the test execution context), it doesn't
120 get completely destroyed.
121 """
122
123 def __init__(self,*a):
124 dict.__init__(self,*a)
125 self._savedict = {}
126
127 def clear(self):
128 dict.clear(self)
129 self.update(self._savedict)
130
131 def _checkpoint(self):
132 self._savedict.clear()
133 self._savedict.update(self)
134
135 def update(self,other):
136 self._checkpoint()
137 dict.update(self,other)
138
139 # If '_' is in the namespace, python won't set it when executing code,
140 # and we have examples that test it. So we ensure that the namespace
141 # is always 'clean' of it before it's used for test code execution.
142 self.pop('_',None)
143
144 # The builtins namespace must *always* be the real __builtin__ module,
145 # else weird stuff happens. The main ipython code does have provisions
146 # to ensure this after %run, but since in this class we do some
147 # aggressive low-level cleaning of the execution namespace, we need to
148 # correct for that ourselves, to ensure consitency with the 'real'
149 # ipython.
150 self['__builtins__'] = __builtin__
151
152
153 def start_ipython():
154 """Start a global IPython shell, which we need for IPython-specific syntax.
155 """
156
157 # This function should only ever run once!
158 if hasattr(start_ipython,'already_called'):
159 return
160 start_ipython.already_called = True
161
162 # Ok, first time we're called, go ahead
163 import new
164
165 import IPython
166 from IPython.core import ipapp, iplib
167
168 def xsys(cmd):
169 """Execute a command and print its output.
170
171 This is just a convenience function to replace the IPython system call
172 with one that is more doctest-friendly.
173 """
174 cmd = _ip.var_expand(cmd,depth=1)
175 sys.stdout.write(commands.getoutput(cmd))
176 sys.stdout.flush()
177
178 # Store certain global objects that IPython modifies
179 _displayhook = sys.displayhook
180 _excepthook = sys.excepthook
181 _main = sys.modules.get('__main__')
182
183 argv = default_argv()
184
185 # Start IPython instance. We customize it to start with minimal frills.
186 user_ns,global_ns = iplib.make_user_namespaces(ipnsdict(),{})
187 ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns)
188 ip.initialize()
189 ip.shell.builtin_trap.set()
190
191 # Deactivate the various python system hooks added by ipython for
192 # interactive convenience so we don't confuse the doctest system
193 sys.modules['__main__'] = _main
194 sys.displayhook = _displayhook
195 sys.excepthook = _excepthook
196
197 # So that ipython magics and aliases can be doctested (they work by making
198 # a call into a global _ip object)
199 __builtin__._ip = ip.shell
200
201 # Modify the IPython system call with one that uses getoutput, so that we
202 # can capture subcommands and print them to Python's stdout, otherwise the
203 # doctest machinery would miss them.
204 ip.shell.system = xsys
205
206 # Also patch our %run function in.
207 im = new.instancemethod(_run_ns_sync,_ip, _ip.__class__)
208 ip.shell.magic_run_ori = _ip.magic_run
209 ip.shell.magic_run = im
210
211 # XXX - For some very bizarre reason, the loading of %history by default is
212 # failing. This needs to be fixed later, but for now at least this ensures
213 # that tests that use %hist run to completion.
214 from IPython.core import history
215 history.init_ipython(ip.shell)
216 if not hasattr(ip.shell,'magic_history'):
217 raise RuntimeError("Can't load magics, aborting")
218
219
220 # The start call MUST be made here. I'm not sure yet why it doesn't work if
221 # it is made later, at plugin initialization time, but in all my tests, that's
222 # the case.
223 start_ipython()
224
225 # *** END HACK ***
226 ###########################################################################
227
56
57 #-----------------------------------------------------------------------------
228 # Classes and functions
58 # Classes and functions
59 #-----------------------------------------------------------------------------
229
60
230 def is_extension_module(filename):
61 def is_extension_module(filename):
231 """Return whether the given filename is an extension module.
62 """Return whether the given filename is an extension module.
@@ -288,7 +119,7 b' class DocTestFinder(doctest.DocTestFinder):'
288 Find tests for the given object and any contained objects, and
119 Find tests for the given object and any contained objects, and
289 add them to `tests`.
120 add them to `tests`.
290 """
121 """
291
122 #print '_find for:', obj, name, module # dbg
292 if hasattr(obj,"skip_doctest"):
123 if hasattr(obj,"skip_doctest"):
293 #print 'SKIPPING DOCTEST FOR:',obj # dbg
124 #print 'SKIPPING DOCTEST FOR:',obj # dbg
294 obj = DocTestSkip(obj)
125 obj = DocTestSkip(obj)
@@ -396,8 +227,9 b' class DocTestCase(doctests.DocTestCase):'
396 self._dt_runner = runner
227 self._dt_runner = runner
397
228
398
229
399 # Each doctest should remember what directory it was loaded from...
230 # Each doctest should remember the directory it was loaded from, so
400 self._ori_dir = os.getcwd()
231 # things like %run work without too many contortions
232 self._ori_dir = os.path.dirname(test.filename)
401
233
402 # Modified runTest from the default stdlib
234 # Modified runTest from the default stdlib
403 def runTest(self):
235 def runTest(self):
@@ -418,6 +250,7 b' class DocTestCase(doctests.DocTestCase):'
418 # test was originally created, in case another doctest did a
250 # test was originally created, in case another doctest did a
419 # directory change. We'll restore this in the finally clause.
251 # directory change. We'll restore this in the finally clause.
420 curdir = os.getcwd()
252 curdir = os.getcwd()
253 #print 'runTest in dir:', self._ori_dir # dbg
421 os.chdir(self._ori_dir)
254 os.chdir(self._ori_dir)
422
255
423 runner.DIVIDER = "-"*70
256 runner.DIVIDER = "-"*70
@@ -432,7 +265,7 b' class DocTestCase(doctests.DocTestCase):'
432
265
433 def setUp(self):
266 def setUp(self):
434 """Modified test setup that syncs with ipython namespace"""
267 """Modified test setup that syncs with ipython namespace"""
435
268 #print "setUp test", self._dt_test.examples # dbg
436 if isinstance(self._dt_test.examples[0],IPExample):
269 if isinstance(self._dt_test.examples[0],IPExample):
437 # for IPython examples *only*, we swap the globals with the ipython
270 # for IPython examples *only*, we swap the globals with the ipython
438 # namespace, after updating it with the globals (which doctest
271 # namespace, after updating it with the globals (which doctest
@@ -731,8 +564,10 b' class IPDocTestRunner(doctest.DocTestRunner,object):'
731 # attribute. Our new %run will then only make the namespace update
564 # attribute. Our new %run will then only make the namespace update
732 # when called (rather than unconconditionally updating test.globs here
565 # when called (rather than unconconditionally updating test.globs here
733 # for all examples, most of which won't be calling %run anyway).
566 # for all examples, most of which won't be calling %run anyway).
734 _run_ns_sync.test_globs = test.globs
567 #_ip._ipdoctest_test_globs = test.globs
735 _run_ns_sync.test_filename = test.filename
568 #_ip._ipdoctest_test_filename = test.filename
569
570 test.globs.update(_ip.user_ns)
736
571
737 return super(IPDocTestRunner,self).run(test,
572 return super(IPDocTestRunner,self).run(test,
738 compileflags,out,clear_globs)
573 compileflags,out,clear_globs)
@@ -846,6 +681,7 b' class ExtensionDoctest(doctests.Doctest):'
846
681
847
682
848 def loadTestsFromFile(self, filename):
683 def loadTestsFromFile(self, filename):
684 #print "ipdoctest - from file", filename # dbg
849 if is_extension_module(filename):
685 if is_extension_module(filename):
850 for t in self.loadTestsFromExtensionModule(filename):
686 for t in self.loadTestsFromExtensionModule(filename):
851 yield t
687 yield t
@@ -895,6 +731,7 b' class IPythonDoctest(ExtensionDoctest):'
895 """Look for doctests in the given object, which will be a
731 """Look for doctests in the given object, which will be a
896 function, method or class.
732 function, method or class.
897 """
733 """
734 #print 'Plugin analyzing:', obj, parent # dbg
898 # always use whitespace and ellipsis options
735 # always use whitespace and ellipsis options
899 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
736 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
900
737
@@ -909,6 +746,7 b' class IPythonDoctest(ExtensionDoctest):'
909 checker=self.checker)
746 checker=self.checker)
910
747
911 def options(self, parser, env=os.environ):
748 def options(self, parser, env=os.environ):
749 #print "Options for nose plugin:", self.name # dbg
912 Plugin.options(self, parser, env)
750 Plugin.options(self, parser, env)
913 parser.add_option('--ipdoctest-tests', action='store_true',
751 parser.add_option('--ipdoctest-tests', action='store_true',
914 dest='ipdoctest_tests',
752 dest='ipdoctest_tests',
@@ -929,6 +767,7 b' class IPythonDoctest(ExtensionDoctest):'
929 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
767 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
930
768
931 def configure(self, options, config):
769 def configure(self, options, config):
770 #print "Configuring nose plugin:", self.name # dbg
932 Plugin.configure(self, options, config)
771 Plugin.configure(self, options, config)
933 self.doctest_tests = options.ipdoctest_tests
772 self.doctest_tests = options.ipdoctest_tests
934 self.extension = tolist(options.ipdoctest_extension)
773 self.extension = tolist(options.ipdoctest_extension)
@@ -29,10 +29,11 b' Authors'
29 import os
29 import os
30 import re
30 import re
31 import sys
31 import sys
32 import tempfile
32
33
33 import nose.tools as nt
34 import nose.tools as nt
34
35
35 from IPython.utils import genutils
36 from IPython.utils import genutils, platutils
36
37
37 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
38 # Globals
39 # Globals
@@ -128,5 +129,93 b' def parse_test_output(txt):'
128 # If the input didn't match any of these forms, assume no error/failures
129 # If the input didn't match any of these forms, assume no error/failures
129 return 0, 0
130 return 0, 0
130
131
132
131 # So nose doesn't think this is a test
133 # So nose doesn't think this is a test
132 parse_test_output.__test__ = False
134 parse_test_output.__test__ = False
135
136
137 def temp_pyfile(src, ext='.py'):
138 """Make a temporary python file, return filename and filehandle.
139
140 Parameters
141 ----------
142 src : string or list of strings (no need for ending newlines if list)
143 Source code to be written to the file.
144
145 ext : optional, string
146 Extension for the generated file.
147
148 Returns
149 -------
150 (filename, open filehandle)
151 It is the caller's responsibility to close the open file and unlink it.
152 """
153 fname = tempfile.mkstemp(ext)[1]
154 f = open(fname,'w')
155 f.write(src)
156 f.flush()
157 return fname, f
158
159
160 def default_argv():
161 """Return a valid default argv for creating testing instances of ipython"""
162
163 # Get the install directory for the user configuration and tell ipython to
164 # use the default profile from there.
165 from IPython.config import default
166 ipcdir = os.path.dirname(default.__file__)
167 ipconf = os.path.join(ipcdir,'ipython_config.py')
168 #print 'conf:',ipconf # dbg
169 return ['--colors=NoColor', '--no-term-title','--no-banner',
170 '--config-file=%s' % ipconf, '--autocall=0', '--quick']
171
172
173 def ipexec(fname):
174 """Utility to call 'ipython filename'.
175
176 Starts IPython witha minimal and safe configuration to make startup as fast
177 as possible.
178
179 Note that this starts IPython in a subprocess!
180
181 Parameters
182 ----------
183 fname : str
184 Name of file to be executed (should have .py or .ipy extension).
185
186 Returns
187 -------
188 (stdout, stderr) of ipython subprocess.
189 """
190 _ip = get_ipython()
191 test_dir = os.path.dirname(__file__)
192 full_fname = os.path.join(test_dir, fname)
193 ipython_cmd = platutils.find_cmd('ipython')
194 cmdargs = ' '.join(default_argv())
195 return genutils.getoutputerror('%s %s' % (ipython_cmd, full_fname))
196
197
198 def ipexec_validate(fname, expected_out, expected_err=None):
199 """Utility to call 'ipython filename' and validate output/error.
200
201 This function raises an AssertionError if the validation fails.
202
203 Note that this starts IPython in a subprocess!
204
205 Parameters
206 ----------
207 fname : str
208 Name of the file to be executed (should have .py or .ipy extension).
209
210 expected_out : str
211 Expected stdout of the process.
212
213 Returns
214 -------
215 None
216 """
217
218 out, err = ipexec(fname)
219 nt.assert_equals(out.strip(), expected_out.strip())
220 if expected_err:
221 nt.assert_equals(err.strip(), expected_err.strip())
General Comments 0
You need to be logged in to leave comments. Login now