##// END OF EJS Templates
Various fixes for IPython.core tests to pass under win32.
Fernando Perez -
Show More
@@ -1,168 +1,174 b''
1 1 """Tests for code execution (%run and related), which is particularly tricky.
2 2
3 3 Because of how %run manages namespaces, and the fact that we are trying here to
4 4 verify subtle object deletion and reference counting issues, the %run tests
5 5 will be kept in this separate file. This makes it easier to aggregate in one
6 6 place the tricks needed to handle it; most other magics are much easier to test
7 7 and we do so in a common test_magic file.
8 8 """
9 9 from __future__ import absolute_import
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # stdlib
16 16 import os
17 17 import sys
18 18 import tempfile
19 19
20 20 # third-party
21 21 import nose.tools as nt
22 22
23 23 # our own
24 24 from IPython.utils.platutils import find_cmd
25 25 from IPython.utils import genutils
26 26 from IPython.testing import decorators as dec
27 27 from IPython.testing import tools as tt
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Test functions begin
31 31 #-----------------------------------------------------------------------------
32 32
33 33 def doctest_refbug():
34 34 """Very nasty problem with references held by multiple runs of a script.
35 35 See: https://bugs.launchpad.net/ipython/+bug/269966
36 36
37 37 In [1]: _ip.clear_main_mod_cache()
38 38 # random
39 39
40 40 In [2]: %run refbug
41 41
42 42 In [3]: call_f()
43 43 lowercased: hello
44 44
45 45 In [4]: %run refbug
46 46
47 47 In [5]: call_f()
48 48 lowercased: hello
49 49 lowercased: hello
50 50 """
51 51
52 52
53 53 def doctest_run_builtins():
54 54 r"""Check that %run doesn't damage __builtins__.
55 55
56 56 In [1]: import tempfile
57 57
58 58 In [2]: bid1 = id(__builtins__)
59 59
60 60 In [3]: fname = tempfile.mkstemp('.py')[1]
61 61
62 62 In [3]: f = open(fname,'w')
63 63
64 64 In [4]: f.write('pass\n')
65 65
66 66 In [5]: f.flush()
67 67
68 68 In [6]: t1 = type(__builtins__)
69 69
70 70 In [7]: %run "$fname"
71 71
72 72 In [7]: f.close()
73 73
74 74 In [8]: bid2 = id(__builtins__)
75 75
76 76 In [9]: t2 = type(__builtins__)
77 77
78 78 In [10]: t1 == t2
79 79 Out[10]: True
80 80
81 81 In [10]: bid1 == bid2
82 82 Out[10]: True
83 83
84 84 In [12]: try:
85 85 ....: os.unlink(fname)
86 86 ....: except:
87 87 ....: pass
88 88 ....:
89 89 """
90 90
91 91 # For some tests, it will be handy to organize them in a class with a common
92 92 # setup that makes a temp file
93 93
94 94 class TestMagicRunPass(tt.TempFileMixin):
95 95
96 96 def setup(self):
97 97 """Make a valid python temp file."""
98 98 self.mktmp('pass\n')
99 99
100 100 def run_tmpfile(self):
101 101 _ip = get_ipython()
102 102 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
103 103 # See below and ticket https://bugs.launchpad.net/bugs/366353
104 104 _ip.magic('run "%s"' % self.fname)
105 105
106 106 def test_builtins_id(self):
107 107 """Check that %run doesn't damage __builtins__ """
108 108 _ip = get_ipython()
109 109 # Test that the id of __builtins__ is not modified by %run
110 110 bid1 = id(_ip.user_ns['__builtins__'])
111 111 self.run_tmpfile()
112 112 bid2 = id(_ip.user_ns['__builtins__'])
113 113 tt.assert_equals(bid1, bid2)
114 114
115 115 def test_builtins_type(self):
116 116 """Check that the type of __builtins__ doesn't change with %run.
117 117
118 118 However, the above could pass if __builtins__ was already modified to
119 119 be a dict (it should be a module) by a previous use of %run. So we
120 120 also check explicitly that it really is a module:
121 121 """
122 122 _ip = get_ipython()
123 123 self.run_tmpfile()
124 124 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
125 125
126 126 def test_prompts(self):
127 127 """Test that prompts correctly generate after %run"""
128 128 self.run_tmpfile()
129 129 _ip = get_ipython()
130 130 p2 = str(_ip.outputcache.prompt2).strip()
131 131 nt.assert_equals(p2[:3], '...')
132 132
133 133
134 134 class TestMagicRunSimple(tt.TempFileMixin):
135 135
136 136 def test_simpledef(self):
137 137 """Test that simple class definitions work."""
138 138 src = ("class foo: pass\n"
139 139 "def f(): return foo()")
140 140 self.mktmp(src)
141 141 _ip.magic('run "%s"' % self.fname)
142 142 _ip.runlines('t = isinstance(f(), foo)')
143 143 nt.assert_true(_ip.user_ns['t'])
144
144
145 # We have to skip these in win32 because genutils.getoutputerr() crashes,
146 # due to the fact that subprocess does not support close_fds when
147 # redirecting stdout/err. So unless someone who knows more tells us how to
148 # implement genutils.getoutputerr() in win32, we're stuck avoiding these.
149 @dec.skip_win32
145 150 def test_obj_del(self):
146 151 """Test that object's __del__ methods are called on exit."""
147 152
148 153 # This test is known to fail on win32.
149 154 # See ticket https://bugs.launchpad.net/bugs/366334
150 155 src = ("class A(object):\n"
151 156 " def __del__(self):\n"
152 157 " print 'object A deleted'\n"
153 158 "a = A()\n")
154 159 self.mktmp(src)
155 160 tt.ipexec_validate(self.fname, 'object A deleted')
156 161
162 @dec.skip_win32
157 163 def test_tclass(self):
158 164 mydir = os.path.dirname(__file__)
159 165 tc = os.path.join(mydir, 'tclass')
160 166 src = ("%%run '%s' C-first\n"
161 167 "%%run '%s' C-second\n") % (tc, tc)
162 168 self.mktmp(src, '.ipy')
163 169 out = """\
164 170 ARGV 1-: ['C-first']
165 171 ARGV 1-: ['C-second']
166 172 tclass.py: deleting object: C-first
167 173 """
168 174 tt.ipexec_validate(self.fname, out)
@@ -1,266 +1,274 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 #*****************************************************************************
19 19 # Copyright (C) 2009 The IPython Development Team
20 20 #
21 21 # Distributed under the terms of the BSD License. The full license is in
22 22 # the file COPYING, distributed as part of this software.
23 23 #*****************************************************************************
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Required modules and packages
27 27 #-----------------------------------------------------------------------------
28 28
29 29 import os
30 30 import re
31 31 import sys
32 32 import tempfile
33 33
34 34 try:
35 35 # These tools are used by parts of the runtime, so we make the nose
36 36 # dependency optional at this point. Nose is a hard dependency to run the
37 37 # test suite, but NOT to use ipython itself.
38 38 import nose.tools as nt
39 39 has_nose = True
40 40 except ImportError:
41 41 has_nose = False
42 42
43 43 from IPython.utils import genutils, platutils
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # Globals
47 47 #-----------------------------------------------------------------------------
48 48
49 49 # Make a bunch of nose.tools assert wrappers that can be used in test
50 50 # generators. This will expose an assert* function for each one in nose.tools.
51 51
52 52 _tpl = """
53 53 def %(name)s(*a,**kw):
54 54 return nt.%(name)s(*a,**kw)
55 55 """
56 56
57 57 if has_nose:
58 58 for _x in [a for a in dir(nt) if a.startswith('assert')]:
59 59 exec _tpl % dict(name=_x)
60 60
61 61 #-----------------------------------------------------------------------------
62 62 # Functions and classes
63 63 #-----------------------------------------------------------------------------
64 64
65 65
66 66 def full_path(startPath,files):
67 67 """Make full paths for all the listed files, based on startPath.
68 68
69 69 Only the base part of startPath is kept, since this routine is typically
70 70 used with a script's __file__ variable as startPath. The base of startPath
71 71 is then prepended to all the listed files, forming the output list.
72 72
73 73 Parameters
74 74 ----------
75 75 startPath : string
76 76 Initial path to use as the base for the results. This path is split
77 77 using os.path.split() and only its first component is kept.
78 78
79 79 files : string or list
80 80 One or more files.
81 81
82 82 Examples
83 83 --------
84 84
85 85 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
86 86 ['/foo/a.txt', '/foo/b.txt']
87 87
88 88 >>> full_path('/foo',['a.txt','b.txt'])
89 89 ['/a.txt', '/b.txt']
90 90
91 91 If a single file is given, the output is still a list:
92 92 >>> full_path('/foo','a.txt')
93 93 ['/a.txt']
94 94 """
95 95
96 96 files = genutils.list_strings(files)
97 97 base = os.path.split(startPath)[0]
98 98 return [ os.path.join(base,f) for f in files ]
99 99
100 100
101 101 def parse_test_output(txt):
102 102 """Parse the output of a test run and return errors, failures.
103 103
104 104 Parameters
105 105 ----------
106 106 txt : str
107 107 Text output of a test run, assumed to contain a line of one of the
108 108 following forms::
109 109 'FAILED (errors=1)'
110 110 'FAILED (failures=1)'
111 111 'FAILED (errors=1, failures=1)'
112 112
113 113 Returns
114 114 -------
115 115 nerr, nfail: number of errors and failures.
116 116 """
117 117
118 118 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
119 119 if err_m:
120 120 nerr = int(err_m.group(1))
121 121 nfail = 0
122 122 return nerr, nfail
123 123
124 124 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
125 125 if fail_m:
126 126 nerr = 0
127 127 nfail = int(fail_m.group(1))
128 128 return nerr, nfail
129 129
130 130 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
131 131 re.MULTILINE)
132 132 if both_m:
133 133 nerr = int(both_m.group(1))
134 134 nfail = int(both_m.group(2))
135 135 return nerr, nfail
136 136
137 137 # If the input didn't match any of these forms, assume no error/failures
138 138 return 0, 0
139 139
140 140
141 141 # So nose doesn't think this is a test
142 142 parse_test_output.__test__ = False
143 143
144 144
145 145 def temp_pyfile(src, ext='.py'):
146 146 """Make a temporary python file, return filename and filehandle.
147 147
148 148 Parameters
149 149 ----------
150 150 src : string or list of strings (no need for ending newlines if list)
151 151 Source code to be written to the file.
152 152
153 153 ext : optional, string
154 154 Extension for the generated file.
155 155
156 156 Returns
157 157 -------
158 158 (filename, open filehandle)
159 159 It is the caller's responsibility to close the open file and unlink it.
160 160 """
161 161 fname = tempfile.mkstemp(ext)[1]
162 162 f = open(fname,'w')
163 163 f.write(src)
164 164 f.flush()
165 165 return fname, f
166 166
167 167
168 168 def default_argv():
169 169 """Return a valid default argv for creating testing instances of ipython"""
170 170
171 171 # Get the install directory for the user configuration and tell ipython to
172 172 # use the default profile from there.
173 173 from IPython.config import default
174 174 ipcdir = os.path.dirname(default.__file__)
175 175 ipconf = os.path.join(ipcdir,'ipython_config.py')
176 176 return ['--colors=NoColor', '--no-term-title','--no-banner',
177 '--config-file=%s' % ipconf, '--autocall=0',
177 '--config-file="%s"' % ipconf, '--autocall=0',
178 178 '--prompt-out=""']
179 179
180 180
181 181 def ipexec(fname, options=None):
182 182 """Utility to call 'ipython filename'.
183 183
184 184 Starts IPython witha minimal and safe configuration to make startup as fast
185 185 as possible.
186 186
187 187 Note that this starts IPython in a subprocess!
188 188
189 189 Parameters
190 190 ----------
191 191 fname : str
192 192 Name of file to be executed (should have .py or .ipy extension).
193 193
194 194 options : optional, list
195 195 Extra command-line flags to be passed to IPython.
196 196
197 197 Returns
198 198 -------
199 199 (stdout, stderr) of ipython subprocess.
200 200 """
201 201 if options is None: options = []
202 202 cmdargs = ' '.join(default_argv() + options)
203 203
204 204 _ip = get_ipython()
205 205 test_dir = os.path.dirname(__file__)
206 # Find the ipython script from the package we're using, so that the test
207 # suite can be run from the source tree without an installed IPython
208 ipython_package_dir = genutils.get_ipython_package_dir()
209 ipython_script = os.path.join(ipython_package_dir,'scripts','ipython')
210 ipython_cmd = 'python "%s"' % ipython_script
211 # Absolute path for filename
206 212 full_fname = os.path.join(test_dir, fname)
207 ipython_cmd = platutils.find_cmd('ipython')
208 full_cmd = '%s %s %s' % (ipython_cmd, cmdargs, full_fname)
213 full_cmd = '%s %s "%s"' % (ipython_cmd, cmdargs, full_fname)
209 214 return genutils.getoutputerror(full_cmd)
210 215
211 216
212 217 def ipexec_validate(fname, expected_out, expected_err=None,
213 218 options=None):
214 219 """Utility to call 'ipython filename' and validate output/error.
215 220
216 221 This function raises an AssertionError if the validation fails.
217 222
218 223 Note that this starts IPython in a subprocess!
219 224
220 225 Parameters
221 226 ----------
222 227 fname : str
223 228 Name of the file to be executed (should have .py or .ipy extension).
224 229
225 230 expected_out : str
226 231 Expected stdout of the process.
227 232
228 233 expected_err : optional, str
229 234 Expected stderr of the process.
230 235
231 236 options : optional, list
232 237 Extra command-line flags to be passed to IPython.
233 238
234 239 Returns
235 240 -------
236 241 None
237 242 """
238 243
239 244 import nose.tools as nt
240 245
241 246 out, err = ipexec(fname)
242 247 nt.assert_equals(out.strip(), expected_out.strip())
243 248 if expected_err:
244 249 nt.assert_equals(err.strip(), expected_err.strip())
245 250
246 251
247 252 class TempFileMixin(object):
248 253 """Utility class to create temporary Python/IPython files.
249 254
250 255 Meant as a mixin class for test cases."""
251 256
252 257 def mktmp(self, src, ext='.py'):
253 258 """Make a valid python temp file."""
254 259 fname, f = temp_pyfile(src, ext)
255 260 self.tmpfile = f
256 261 self.fname = fname
257 262
258 263 def teardown(self):
259 self.tmpfile.close()
260 try:
261 os.unlink(self.fname)
262 except:
263 # On Windows, even though we close the file, we still can't delete
264 # it. I have no clue why
265 pass
264 if hasattr(self, 'tmpfile'):
265 # If the tmpfile wasn't made because of skipped tests, like in
266 # win32, there's nothing to cleanup.
267 self.tmpfile.close()
268 try:
269 os.unlink(self.fname)
270 except:
271 # On Windows, even though we close the file, we still can't
272 # delete it. I have no clue why
273 pass
266 274
General Comments 0
You need to be logged in to leave comments. Login now