##// END OF EJS Templates
Remove unused imports in IPython.testing
Thomas Kluyver -
Show More
@@ -1,360 +1,359 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Decorators for labeling test objects.
3 3
4 4 Decorators that merely return a modified version of the original function
5 5 object are straightforward. Decorators that return a new function object need
6 6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 7 decorator, in order to preserve metadata such as function name, setup and
8 8 teardown functions and so on - see nose.tools for more information.
9 9
10 10 This module provides a set of useful decorators meant to be ready to use in
11 11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 12 find yourself writing a new one that may be of generic use, add it here.
13 13
14 14 Included decorators:
15 15
16 16
17 17 Lightweight testing that remains unittest-compatible.
18 18
19 19 - @parametric, for parametric test support that is vastly easier to use than
20 20 nose's for debugging. With ours, if a test fails, the stack under inspection
21 21 is that of the test and not that of the test framework.
22 22
23 23 - An @as_unittest decorator can be used to tag any normal parameter-less
24 24 function as a unittest TestCase. Then, both nose and normal unittest will
25 25 recognize it as such. This will make it easier to migrate away from Nose if
26 26 we ever need/want to while maintaining very lightweight tests.
27 27
28 28 NOTE: This file contains IPython-specific decorators. Using the machinery in
29 29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
30 30 available, OR use equivalent code in IPython.external._decorators, which
31 31 we've copied verbatim from numpy.
32 32
33 33 Authors
34 34 -------
35 35
36 36 - Fernando Perez <Fernando.Perez@berkeley.edu>
37 37 """
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Copyright (C) 2009-2011 The IPython Development Team
41 41 #
42 42 # Distributed under the terms of the BSD License. The full license is in
43 43 # the file COPYING, distributed as part of this software.
44 44 #-----------------------------------------------------------------------------
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Imports
48 48 #-----------------------------------------------------------------------------
49 49
50 50 # Stdlib imports
51 import inspect
52 51 import sys
53 52 import tempfile
54 53 import unittest
55 54
56 55 # Third-party imports
57 56
58 57 # This is Michele Simionato's decorator module, kept verbatim.
59 58 from IPython.external.decorator import decorator
60 59
61 60 # We already have python3-compliant code for parametric tests
62 61 if sys.version[0]=='2':
63 from _paramtestpy2 import parametric, ParametricTestCase
62 from _paramtestpy2 import parametric
64 63 else:
65 from _paramtestpy3 import parametric, ParametricTestCase
64 from _paramtestpy3 import parametric
66 65
67 66 # Expose the unittest-driven decorators
68 67 from ipunittest import ipdoctest, ipdocstring
69 68
70 69 # Grab the numpy-specific decorators which we keep in a file that we
71 70 # occasionally update from upstream: decorators.py is a copy of
72 71 # numpy.testing.decorators, we expose all of it here.
73 72 from IPython.external.decorators import *
74 73
75 74 # For onlyif_cmd_exists decorator
76 75 from IPython.utils.process import is_cmd_found
77 76
78 77 #-----------------------------------------------------------------------------
79 78 # Classes and functions
80 79 #-----------------------------------------------------------------------------
81 80
82 81 # Simple example of the basic idea
83 82 def as_unittest(func):
84 83 """Decorator to make a simple function into a normal test via unittest."""
85 84 class Tester(unittest.TestCase):
86 85 def test(self):
87 86 func()
88 87
89 88 Tester.__name__ = func.__name__
90 89
91 90 return Tester
92 91
93 92 # Utility functions
94 93
95 94 def apply_wrapper(wrapper,func):
96 95 """Apply a wrapper to a function for decoration.
97 96
98 97 This mixes Michele Simionato's decorator tool with nose's make_decorator,
99 98 to apply a wrapper in a decorator so that all nose attributes, as well as
100 99 function signature and other properties, survive the decoration cleanly.
101 100 This will ensure that wrapped functions can still be well introspected via
102 101 IPython, for example.
103 102 """
104 103 import nose.tools
105 104
106 105 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
107 106
108 107
109 108 def make_label_dec(label,ds=None):
110 109 """Factory function to create a decorator that applies one or more labels.
111 110
112 111 Parameters
113 112 ----------
114 113 label : string or sequence
115 114 One or more labels that will be applied by the decorator to the functions
116 115 it decorates. Labels are attributes of the decorated function with their
117 116 value set to True.
118 117
119 118 ds : string
120 119 An optional docstring for the resulting decorator. If not given, a
121 120 default docstring is auto-generated.
122 121
123 122 Returns
124 123 -------
125 124 A decorator.
126 125
127 126 Examples
128 127 --------
129 128
130 129 A simple labeling decorator:
131 130
132 131 >>> slow = make_label_dec('slow')
133 132 >>> slow.__doc__
134 133 "Labels a test as 'slow'."
135 134
136 135 And one that uses multiple labels and a custom docstring:
137 136
138 137 >>> rare = make_label_dec(['slow','hard'],
139 138 ... "Mix labels 'slow' and 'hard' for rare tests.")
140 139 >>> rare.__doc__
141 140 "Mix labels 'slow' and 'hard' for rare tests."
142 141
143 142 Now, let's test using this one:
144 143 >>> @rare
145 144 ... def f(): pass
146 145 ...
147 146 >>>
148 147 >>> f.slow
149 148 True
150 149 >>> f.hard
151 150 True
152 151 """
153 152
154 153 if isinstance(label,basestring):
155 154 labels = [label]
156 155 else:
157 156 labels = label
158 157
159 158 # Validate that the given label(s) are OK for use in setattr() by doing a
160 159 # dry run on a dummy function.
161 160 tmp = lambda : None
162 161 for label in labels:
163 162 setattr(tmp,label,True)
164 163
165 164 # This is the actual decorator we'll return
166 165 def decor(f):
167 166 for label in labels:
168 167 setattr(f,label,True)
169 168 return f
170 169
171 170 # Apply the user's docstring, or autogenerate a basic one
172 171 if ds is None:
173 172 ds = "Labels a test as %r." % label
174 173 decor.__doc__ = ds
175 174
176 175 return decor
177 176
178 177
179 178 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
180 179 # preserve function metadata better and allows the skip condition to be a
181 180 # callable.
182 181 def skipif(skip_condition, msg=None):
183 182 ''' Make function raise SkipTest exception if skip_condition is true
184 183
185 184 Parameters
186 185 ----------
187 186 skip_condition : bool or callable.
188 187 Flag to determine whether to skip test. If the condition is a
189 188 callable, it is used at runtime to dynamically make the decision. This
190 189 is useful for tests that may require costly imports, to delay the cost
191 190 until the test suite is actually executed.
192 191 msg : string
193 192 Message to give on raising a SkipTest exception
194 193
195 194 Returns
196 195 -------
197 196 decorator : function
198 197 Decorator, which, when applied to a function, causes SkipTest
199 198 to be raised when the skip_condition was True, and the function
200 199 to be called normally otherwise.
201 200
202 201 Notes
203 202 -----
204 203 You will see from the code that we had to further decorate the
205 204 decorator with the nose.tools.make_decorator function in order to
206 205 transmit function name, and various other metadata.
207 206 '''
208 207
209 208 def skip_decorator(f):
210 209 # Local import to avoid a hard nose dependency and only incur the
211 210 # import time overhead at actual test-time.
212 211 import nose
213 212
214 213 # Allow for both boolean or callable skip conditions.
215 214 if callable(skip_condition):
216 215 skip_val = skip_condition
217 216 else:
218 217 skip_val = lambda : skip_condition
219 218
220 219 def get_msg(func,msg=None):
221 220 """Skip message with information about function being skipped."""
222 221 if msg is None: out = 'Test skipped due to test condition.'
223 222 else: out = msg
224 223 return "Skipping test: %s. %s" % (func.__name__,out)
225 224
226 225 # We need to define *two* skippers because Python doesn't allow both
227 226 # return with value and yield inside the same function.
228 227 def skipper_func(*args, **kwargs):
229 228 """Skipper for normal test functions."""
230 229 if skip_val():
231 230 raise nose.SkipTest(get_msg(f,msg))
232 231 else:
233 232 return f(*args, **kwargs)
234 233
235 234 def skipper_gen(*args, **kwargs):
236 235 """Skipper for test generators."""
237 236 if skip_val():
238 237 raise nose.SkipTest(get_msg(f,msg))
239 238 else:
240 239 for x in f(*args, **kwargs):
241 240 yield x
242 241
243 242 # Choose the right skipper to use when building the actual generator.
244 243 if nose.util.isgenerator(f):
245 244 skipper = skipper_gen
246 245 else:
247 246 skipper = skipper_func
248 247
249 248 return nose.tools.make_decorator(f)(skipper)
250 249
251 250 return skip_decorator
252 251
253 252 # A version with the condition set to true, common case just to attach a message
254 253 # to a skip decorator
255 254 def skip(msg=None):
256 255 """Decorator factory - mark a test function for skipping from test suite.
257 256
258 257 Parameters
259 258 ----------
260 259 msg : string
261 260 Optional message to be added.
262 261
263 262 Returns
264 263 -------
265 264 decorator : function
266 265 Decorator, which, when applied to a function, causes SkipTest
267 266 to be raised, with the optional message added.
268 267 """
269 268
270 269 return skipif(True,msg)
271 270
272 271
273 272 def onlyif(condition, msg):
274 273 """The reverse from skipif, see skipif for details."""
275 274
276 275 if callable(condition):
277 276 skip_condition = lambda : not condition()
278 277 else:
279 278 skip_condition = lambda : not condition
280 279
281 280 return skipif(skip_condition, msg)
282 281
283 282 #-----------------------------------------------------------------------------
284 283 # Utility functions for decorators
285 284 def module_not_available(module):
286 285 """Can module be imported? Returns true if module does NOT import.
287 286
288 287 This is used to make a decorator to skip tests that require module to be
289 288 available, but delay the 'import numpy' to test execution time.
290 289 """
291 290 try:
292 291 mod = __import__(module)
293 292 mod_not_avail = False
294 293 except ImportError:
295 294 mod_not_avail = True
296 295
297 296 return mod_not_avail
298 297
299 298 #-----------------------------------------------------------------------------
300 299 # Decorators for public use
301 300
302 301 # Decorators to skip certain tests on specific platforms.
303 302 skip_win32 = skipif(sys.platform == 'win32',
304 303 "This test does not run under Windows")
305 304 skip_linux = skipif(sys.platform.startswith('linux'),
306 305 "This test does not run under Linux")
307 306 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
308 307
309 308
310 309 # Decorators to skip tests if not on specific platforms.
311 310 skip_if_not_win32 = skipif(sys.platform != 'win32',
312 311 "This test only runs under Windows")
313 312 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
314 313 "This test only runs under Linux")
315 314 skip_if_not_osx = skipif(sys.platform != 'darwin',
316 315 "This test only runs under OSX")
317 316
318 317 # Other skip decorators
319 318
320 319 # generic skip without module
321 320 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
322 321
323 322 skipif_not_numpy = skip_without('numpy')
324 323
325 324 skipif_not_matplotlib = skip_without('matplotlib')
326 325
327 326 skipif_not_sympy = skip_without('sympy')
328 327
329 328 skip_known_failure = knownfailureif(True,'This test is known to fail')
330 329
331 330 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
332 331 'This test is known to fail on Python 3.')
333 332
334 333 # A null 'decorator', useful to make more readable code that needs to pick
335 334 # between different decorators based on OS or other conditions
336 335 null_deco = lambda f: f
337 336
338 337 # Some tests only run where we can use unicode paths. Note that we can't just
339 338 # check os.path.supports_unicode_filenames, which is always False on Linux.
340 339 try:
341 340 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
342 341 except UnicodeEncodeError:
343 342 unicode_paths = False
344 343 else:
345 344 unicode_paths = True
346 345 f.close()
347 346
348 347 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
349 348 "where we can use unicode in filenames."))
350 349
351 350
352 351 def onlyif_cmds_exist(*commands):
353 352 """
354 353 Decorator to skip test when at least one of `commands` is not found.
355 354 """
356 355 for cmd in commands:
357 356 if not is_cmd_found(cmd):
358 357 return skip("This test runs only if command '{0}' "
359 358 "is installed".format(cmd))
360 359 return null_deco
@@ -1,632 +1,632 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 import os
32 32 import os.path as path
33 33 import signal
34 34 import sys
35 35 import subprocess
36 36 import tempfile
37 37 import time
38 38 import warnings
39 39
40 40 # Note: monkeypatch!
41 41 # We need to monkeypatch a small problem in nose itself first, before importing
42 42 # it for actual use. This should get into nose upstream, but its release cycle
43 43 # is slow and we need it for our parametric tests to work correctly.
44 44 from IPython.testing import nosepatch
45 45
46 46 # Monkeypatch extra assert methods into nose.tools if they're not already there.
47 47 # This can be dropped once we no longer test on Python 2.6
48 48 from IPython.testing import nose_assert_methods
49 49
50 50 # Now, proceed to import nose itself
51 51 import nose.plugins.builtin
52 52 from nose.plugins.xunit import Xunit
53 53 from nose import SkipTest
54 54 from nose.core import TestProgram
55 55
56 56 # Our own imports
57 57 from IPython.utils import py3compat
58 58 from IPython.utils.importstring import import_item
59 59 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
60 from IPython.utils.process import find_cmd, pycmd2argv
60 from IPython.utils.process import pycmd2argv
61 61 from IPython.utils.sysinfo import sys_info
62 62 from IPython.utils.tempdir import TemporaryDirectory
63 63 from IPython.utils.warn import warn
64 64
65 65 from IPython.testing import globalipapp
66 66 from IPython.testing.plugin.ipdoctest import IPythonDoctest
67 67 from IPython.external.decorators import KnownFailure, knownfailureif
68 68
69 69 pjoin = path.join
70 70
71 71
72 72 #-----------------------------------------------------------------------------
73 73 # Globals
74 74 #-----------------------------------------------------------------------------
75 75
76 76
77 77 #-----------------------------------------------------------------------------
78 78 # Warnings control
79 79 #-----------------------------------------------------------------------------
80 80
81 81 # Twisted generates annoying warnings with Python 2.6, as will do other code
82 82 # that imports 'sets' as of today
83 83 warnings.filterwarnings('ignore', 'the sets module is deprecated',
84 84 DeprecationWarning )
85 85
86 86 # This one also comes from Twisted
87 87 warnings.filterwarnings('ignore', 'the sha module is deprecated',
88 88 DeprecationWarning)
89 89
90 90 # Wx on Fedora11 spits these out
91 91 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
92 92 UserWarning)
93 93
94 94 # ------------------------------------------------------------------------------
95 95 # Monkeypatch Xunit to count known failures as skipped.
96 96 # ------------------------------------------------------------------------------
97 97 def monkeypatch_xunit():
98 98 try:
99 99 knownfailureif(True)(lambda: None)()
100 100 except Exception as e:
101 101 KnownFailureTest = type(e)
102 102
103 103 def addError(self, test, err, capt=None):
104 104 if issubclass(err[0], KnownFailureTest):
105 105 err = (SkipTest,) + err[1:]
106 106 return self.orig_addError(test, err, capt)
107 107
108 108 Xunit.orig_addError = Xunit.addError
109 109 Xunit.addError = addError
110 110
111 111 #-----------------------------------------------------------------------------
112 112 # Logic for skipping doctests
113 113 #-----------------------------------------------------------------------------
114 114 def extract_version(mod):
115 115 return mod.__version__
116 116
117 117 def test_for(item, min_version=None, callback=extract_version):
118 118 """Test to see if item is importable, and optionally check against a minimum
119 119 version.
120 120
121 121 If min_version is given, the default behavior is to check against the
122 122 `__version__` attribute of the item, but specifying `callback` allows you to
123 123 extract the value you are interested in. e.g::
124 124
125 125 In [1]: import sys
126 126
127 127 In [2]: from IPython.testing.iptest import test_for
128 128
129 129 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
130 130 Out[3]: True
131 131
132 132 """
133 133 try:
134 134 check = import_item(item)
135 135 except (ImportError, RuntimeError):
136 136 # GTK reports Runtime error if it can't be initialized even if it's
137 137 # importable.
138 138 return False
139 139 else:
140 140 if min_version:
141 141 if callback:
142 142 # extra processing step to get version to compare
143 143 check = callback(check)
144 144
145 145 return check >= min_version
146 146 else:
147 147 return True
148 148
149 149 # Global dict where we can store information on what we have and what we don't
150 150 # have available at test run time
151 151 have = {}
152 152
153 153 have['curses'] = test_for('_curses')
154 154 have['matplotlib'] = test_for('matplotlib')
155 155 have['numpy'] = test_for('numpy')
156 156 have['pexpect'] = test_for('IPython.external.pexpect')
157 157 have['pymongo'] = test_for('pymongo')
158 158 have['pygments'] = test_for('pygments')
159 159 have['qt'] = test_for('IPython.external.qt')
160 160 have['rpy2'] = test_for('rpy2')
161 161 have['sqlite3'] = test_for('sqlite3')
162 162 have['cython'] = test_for('Cython')
163 163 have['oct2py'] = test_for('oct2py')
164 164 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
165 165 have['jinja2'] = test_for('jinja2')
166 166 have['wx'] = test_for('wx')
167 167 have['wx.aui'] = test_for('wx.aui')
168 168 have['azure'] = test_for('azure')
169 169 have['sphinx'] = test_for('sphinx')
170 170 have['markdown'] = test_for('markdown')
171 171
172 172 min_zmq = (2,1,11)
173 173
174 174 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
175 175
176 176 #-----------------------------------------------------------------------------
177 177 # Functions and classes
178 178 #-----------------------------------------------------------------------------
179 179
180 180 def report():
181 181 """Return a string with a summary report of test-related variables."""
182 182
183 183 out = [ sys_info(), '\n']
184 184
185 185 avail = []
186 186 not_avail = []
187 187
188 188 for k, is_avail in have.items():
189 189 if is_avail:
190 190 avail.append(k)
191 191 else:
192 192 not_avail.append(k)
193 193
194 194 if avail:
195 195 out.append('\nTools and libraries available at test time:\n')
196 196 avail.sort()
197 197 out.append(' ' + ' '.join(avail)+'\n')
198 198
199 199 if not_avail:
200 200 out.append('\nTools and libraries NOT available at test time:\n')
201 201 not_avail.sort()
202 202 out.append(' ' + ' '.join(not_avail)+'\n')
203 203
204 204 return ''.join(out)
205 205
206 206
207 207 def make_exclude():
208 208 """Make patterns of modules and packages to exclude from testing.
209 209
210 210 For the IPythonDoctest plugin, we need to exclude certain patterns that
211 211 cause testing problems. We should strive to minimize the number of
212 212 skipped modules, since this means untested code.
213 213
214 214 These modules and packages will NOT get scanned by nose at all for tests.
215 215 """
216 216 # Simple utility to make IPython paths more readably, we need a lot of
217 217 # these below
218 218 ipjoin = lambda *paths: pjoin('IPython', *paths)
219 219
220 220 exclusions = [ipjoin('external'),
221 221 ipjoin('quarantine'),
222 222 ipjoin('deathrow'),
223 223 # This guy is probably attic material
224 224 ipjoin('testing', 'mkdoctests'),
225 225 # Testing inputhook will need a lot of thought, to figure out
226 226 # how to have tests that don't lock up with the gui event
227 227 # loops in the picture
228 228 ipjoin('lib', 'inputhook'),
229 229 # Config files aren't really importable stand-alone
230 230 ipjoin('config', 'profile'),
231 231 # The notebook 'static' directory contains JS, css and other
232 232 # files for web serving. Occasionally projects may put a .py
233 233 # file in there (MathJax ships a conf.py), so we might as
234 234 # well play it safe and skip the whole thing.
235 235 ipjoin('html', 'static'),
236 236 ipjoin('html', 'fabfile'),
237 237 ]
238 238 if not have['sqlite3']:
239 239 exclusions.append(ipjoin('core', 'tests', 'test_history'))
240 240 exclusions.append(ipjoin('core', 'history'))
241 241 if not have['wx']:
242 242 exclusions.append(ipjoin('lib', 'inputhookwx'))
243 243
244 244 if 'IPython.kernel.inprocess' not in sys.argv:
245 245 exclusions.append(ipjoin('kernel', 'inprocess'))
246 246
247 247 # FIXME: temporarily disable autoreload tests, as they can produce
248 248 # spurious failures in subsequent tests (cythonmagic).
249 249 exclusions.append(ipjoin('extensions', 'autoreload'))
250 250 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
251 251
252 252 # We do this unconditionally, so that the test suite doesn't import
253 253 # gtk, changing the default encoding and masking some unicode bugs.
254 254 exclusions.append(ipjoin('lib', 'inputhookgtk'))
255 255 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
256 256
257 257 # These have to be skipped on win32 because the use echo, rm, cd, etc.
258 258 # See ticket https://github.com/ipython/ipython/issues/87
259 259 if sys.platform == 'win32':
260 260 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
261 261 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
262 262
263 263 if not have['pexpect']:
264 264 exclusions.extend([ipjoin('lib', 'irunner'),
265 265 ipjoin('lib', 'tests', 'test_irunner'),
266 266 ipjoin('terminal', 'console'),
267 267 ])
268 268
269 269 if not have['zmq']:
270 270 exclusions.append(ipjoin('kernel'))
271 271 exclusions.append(ipjoin('qt'))
272 272 exclusions.append(ipjoin('html'))
273 273 exclusions.append(ipjoin('consoleapp.py'))
274 274 exclusions.append(ipjoin('terminal', 'console'))
275 275 exclusions.append(ipjoin('parallel'))
276 276 elif not have['qt'] or not have['pygments']:
277 277 exclusions.append(ipjoin('qt'))
278 278
279 279 if not have['pymongo']:
280 280 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
281 281 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
282 282
283 283 if not have['matplotlib']:
284 284 exclusions.extend([ipjoin('core', 'pylabtools'),
285 285 ipjoin('core', 'tests', 'test_pylabtools'),
286 286 ipjoin('kernel', 'zmq', 'pylab'),
287 287 ])
288 288
289 289 if not have['cython']:
290 290 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
291 291 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
292 292
293 293 if not have['oct2py']:
294 294 exclusions.extend([ipjoin('extensions', 'octavemagic')])
295 295 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
296 296
297 297 if not have['tornado']:
298 298 exclusions.append(ipjoin('html'))
299 299
300 300 if not have['jinja2']:
301 301 exclusions.append(ipjoin('html', 'notebookapp'))
302 302
303 303 if not have['rpy2'] or not have['numpy']:
304 304 exclusions.append(ipjoin('extensions', 'rmagic'))
305 305 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
306 306
307 307 if not have['azure']:
308 308 exclusions.append(ipjoin('html', 'services', 'notebooks', 'azurenbmanager'))
309 309
310 310 if not all((have['pygments'], have['jinja2'], have['markdown'], have['sphinx'])):
311 311 exclusions.append(ipjoin('nbconvert'))
312 312
313 313 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
314 314 if sys.platform == 'win32':
315 315 exclusions = [s.replace('\\','\\\\') for s in exclusions]
316 316
317 317 # check for any exclusions that don't seem to exist:
318 318 parent, _ = os.path.split(get_ipython_package_dir())
319 319 for exclusion in exclusions:
320 320 if exclusion.endswith(('deathrow', 'quarantine')):
321 321 # ignore deathrow/quarantine, which exist in dev, but not install
322 322 continue
323 323 fullpath = pjoin(parent, exclusion)
324 324 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
325 325 warn("Excluding nonexistent file: %r" % exclusion)
326 326
327 327 return exclusions
328 328
329 329
330 330 class IPTester(object):
331 331 """Call that calls iptest or trial in a subprocess.
332 332 """
333 333 #: string, name of test runner that will be called
334 334 runner = None
335 335 #: list, parameters for test runner
336 336 params = None
337 337 #: list, arguments of system call to be made to call test runner
338 338 call_args = None
339 339 #: list, subprocesses we start (for cleanup)
340 340 processes = None
341 341 #: str, coverage xml output file
342 342 coverage_xml = None
343 343
344 344 def __init__(self, runner='iptest', params=None):
345 345 """Create new test runner."""
346 346 p = os.path
347 347 if runner == 'iptest':
348 348 iptest_app = os.path.abspath(get_ipython_module_path('IPython.testing.iptest'))
349 349 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
350 350 else:
351 351 raise Exception('Not a valid test runner: %s' % repr(runner))
352 352 if params is None:
353 353 params = []
354 354 if isinstance(params, str):
355 355 params = [params]
356 356 self.params = params
357 357
358 358 # Assemble call
359 359 self.call_args = self.runner+self.params
360 360
361 361 # Find the section we're testing (IPython.foo)
362 362 for sect in self.params:
363 363 if sect.startswith('IPython') or sect in special_test_suites: break
364 364 else:
365 365 raise ValueError("Section not found", self.params)
366 366
367 367 if '--with-xunit' in self.call_args:
368 368
369 369 self.call_args.append('--xunit-file')
370 370 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
371 371 xunit_file = path.abspath(sect+'.xunit.xml')
372 372 if sys.platform == 'win32':
373 373 xunit_file = '"%s"' % xunit_file
374 374 self.call_args.append(xunit_file)
375 375
376 376 if '--with-xml-coverage' in self.call_args:
377 377 self.coverage_xml = path.abspath(sect+".coverage.xml")
378 378 self.call_args.remove('--with-xml-coverage')
379 379 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
380 380
381 381 # Store anything we start to clean up on deletion
382 382 self.processes = []
383 383
384 384 def _run_cmd(self):
385 385 with TemporaryDirectory() as IPYTHONDIR:
386 386 env = os.environ.copy()
387 387 env['IPYTHONDIR'] = IPYTHONDIR
388 388 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
389 389 subp = subprocess.Popen(self.call_args, env=env)
390 390 self.processes.append(subp)
391 391 # If this fails, the process will be left in self.processes and
392 392 # cleaned up later, but if the wait call succeeds, then we can
393 393 # clear the stored process.
394 394 retcode = subp.wait()
395 395 self.processes.pop()
396 396 return retcode
397 397
398 398 def run(self):
399 399 """Run the stored commands"""
400 400 try:
401 401 retcode = self._run_cmd()
402 402 except KeyboardInterrupt:
403 403 return -signal.SIGINT
404 404 except:
405 405 import traceback
406 406 traceback.print_exc()
407 407 return 1 # signal failure
408 408
409 409 if self.coverage_xml:
410 410 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
411 411 return retcode
412 412
413 413 def __del__(self):
414 414 """Cleanup on exit by killing any leftover processes."""
415 415 for subp in self.processes:
416 416 if subp.poll() is not None:
417 417 continue # process is already dead
418 418
419 419 try:
420 420 print('Cleaning up stale PID: %d' % subp.pid)
421 421 subp.kill()
422 422 except: # (OSError, WindowsError) ?
423 423 # This is just a best effort, if we fail or the process was
424 424 # really gone, ignore it.
425 425 pass
426 426 else:
427 427 for i in range(10):
428 428 if subp.poll() is None:
429 429 time.sleep(0.1)
430 430 else:
431 431 break
432 432
433 433 if subp.poll() is None:
434 434 # The process did not die...
435 435 print('... failed. Manual cleanup may be required.')
436 436
437 437
438 438 special_test_suites = {
439 439 'autoreload': ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'],
440 440 }
441 441
442 442 def make_runners(inc_slow=False):
443 443 """Define the top-level packages that need to be tested.
444 444 """
445 445
446 446 # Packages to be tested via nose, that only depend on the stdlib
447 447 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
448 448 'testing', 'utils', 'nbformat']
449 449
450 450 if have['qt']:
451 451 nose_pkg_names.append('qt')
452 452
453 453 if have['tornado']:
454 454 nose_pkg_names.append('html')
455 455
456 456 if have['zmq']:
457 457 nose_pkg_names.append('kernel')
458 458 nose_pkg_names.append('kernel.inprocess')
459 459 if inc_slow:
460 460 nose_pkg_names.append('parallel')
461 461
462 462 if all((have['pygments'], have['jinja2'], have['markdown'], have['sphinx'])):
463 463 nose_pkg_names.append('nbconvert')
464 464
465 465 # For debugging this code, only load quick stuff
466 466 #nose_pkg_names = ['core', 'extensions'] # dbg
467 467
468 468 # Make fully qualified package names prepending 'IPython.' to our name lists
469 469 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
470 470
471 471 # Make runners
472 472 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
473 473
474 474 for name in special_test_suites:
475 475 runners.append((name, IPTester('iptest', params=name)))
476 476
477 477 return runners
478 478
479 479
480 480 def run_iptest():
481 481 """Run the IPython test suite using nose.
482 482
483 483 This function is called when this script is **not** called with the form
484 484 `iptest all`. It simply calls nose with appropriate command line flags
485 485 and accepts all of the standard nose arguments.
486 486 """
487 487 # Apply our monkeypatch to Xunit
488 488 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
489 489 monkeypatch_xunit()
490 490
491 491 warnings.filterwarnings('ignore',
492 492 'This will be removed soon. Use IPython.testing.util instead')
493 493
494 494 if sys.argv[1] in special_test_suites:
495 495 sys.argv[1:2] = special_test_suites[sys.argv[1]]
496 496 special_suite = True
497 497 else:
498 498 special_suite = False
499 499
500 500 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
501 501
502 502 '--with-ipdoctest',
503 503 '--ipdoctest-tests','--ipdoctest-extension=txt',
504 504
505 505 # We add --exe because of setuptools' imbecility (it
506 506 # blindly does chmod +x on ALL files). Nose does the
507 507 # right thing and it tries to avoid executables,
508 508 # setuptools unfortunately forces our hand here. This
509 509 # has been discussed on the distutils list and the
510 510 # setuptools devs refuse to fix this problem!
511 511 '--exe',
512 512 ]
513 513 if '-a' not in argv and '-A' not in argv:
514 514 argv = argv + ['-a', '!crash']
515 515
516 516 if nose.__version__ >= '0.11':
517 517 # I don't fully understand why we need this one, but depending on what
518 518 # directory the test suite is run from, if we don't give it, 0 tests
519 519 # get run. Specifically, if the test suite is run from the source dir
520 520 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
521 521 # even if the same call done in this directory works fine). It appears
522 522 # that if the requested package is in the current dir, nose bails early
523 523 # by default. Since it's otherwise harmless, leave it in by default
524 524 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
525 525 argv.append('--traverse-namespace')
526 526
527 527 # use our plugin for doctesting. It will remove the standard doctest plugin
528 528 # if it finds it enabled
529 529 ipdt = IPythonDoctest() if special_suite else IPythonDoctest(make_exclude())
530 530 plugins = [ipdt, KnownFailure()]
531 531
532 532 # We need a global ipython running in this process, but the special
533 533 # in-process group spawns its own IPython kernels, so for *that* group we
534 534 # must avoid also opening the global one (otherwise there's a conflict of
535 535 # singletons). Ultimately the solution to this problem is to refactor our
536 536 # assumptions about what needs to be a singleton and what doesn't (app
537 537 # objects should, individual shells shouldn't). But for now, this
538 538 # workaround allows the test suite for the inprocess module to complete.
539 539 if not 'IPython.kernel.inprocess' in sys.argv:
540 540 globalipapp.start_ipython()
541 541
542 542 # Now nose can run
543 543 TestProgram(argv=argv, addplugins=plugins)
544 544
545 545
546 546 def run_iptestall(inc_slow=False):
547 547 """Run the entire IPython test suite by calling nose and trial.
548 548
549 549 This function constructs :class:`IPTester` instances for all IPython
550 550 modules and package and then runs each of them. This causes the modules
551 551 and packages of IPython to be tested each in their own subprocess using
552 552 nose.
553 553
554 554 Parameters
555 555 ----------
556 556
557 557 inc_slow : bool, optional
558 558 Include slow tests, like IPython.parallel. By default, these tests aren't
559 559 run.
560 560 """
561 561
562 562 runners = make_runners(inc_slow=inc_slow)
563 563
564 564 # Run the test runners in a temporary dir so we can nuke it when finished
565 565 # to clean up any junk files left over by accident. This also makes it
566 566 # robust against being run in non-writeable directories by mistake, as the
567 567 # temp dir will always be user-writeable.
568 568 curdir = os.getcwdu()
569 569 testdir = tempfile.gettempdir()
570 570 os.chdir(testdir)
571 571
572 572 # Run all test runners, tracking execution time
573 573 failed = []
574 574 t_start = time.time()
575 575 try:
576 576 for (name, runner) in runners:
577 577 print('*'*70)
578 578 print('IPython test group:',name)
579 579 res = runner.run()
580 580 if res:
581 581 failed.append( (name, runner) )
582 582 if res == -signal.SIGINT:
583 583 print("Interrupted")
584 584 break
585 585 finally:
586 586 os.chdir(curdir)
587 587 t_end = time.time()
588 588 t_tests = t_end - t_start
589 589 nrunners = len(runners)
590 590 nfail = len(failed)
591 591 # summarize results
592 592 print()
593 593 print('*'*70)
594 594 print('Test suite completed for system with the following information:')
595 595 print(report())
596 596 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
597 597 print()
598 598 print('Status:')
599 599 if not failed:
600 600 print('OK')
601 601 else:
602 602 # If anything went wrong, point out what command to rerun manually to
603 603 # see the actual errors and individual summary
604 604 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
605 605 for name, failed_runner in failed:
606 606 print('-'*40)
607 607 print('Runner failed:',name)
608 608 print('You may wish to rerun this one individually, with:')
609 609 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
610 610 print(u' '.join(failed_call_args))
611 611 print()
612 612 # Ensure that our exit code indicates failure
613 613 sys.exit(1)
614 614
615 615
616 616 def main():
617 617 for arg in sys.argv[1:]:
618 618 if arg.startswith('IPython') or arg in special_test_suites:
619 619 # This is in-process
620 620 run_iptest()
621 621 else:
622 622 if "--all" in sys.argv:
623 623 sys.argv.remove("--all")
624 624 inc_slow = True
625 625 else:
626 626 inc_slow = False
627 627 # This starts subprocesses
628 628 run_iptestall(inc_slow=inc_slow)
629 629
630 630
631 631 if __name__ == '__main__':
632 632 main()
@@ -1,188 +1,187 b''
1 1 """Tests for the decorators we've created for IPython.
2 2 """
3 3
4 4 # Module imports
5 5 # Std lib
6 6 import inspect
7 7 import sys
8 import unittest
9 8
10 9 # Third party
11 10 import nose.tools as nt
12 11
13 12 # Our own
14 13 from IPython.testing import decorators as dec
15 14 from IPython.testing.skipdoctest import skip_doctest
16 15 from IPython.testing.ipunittest import ParametricTestCase
17 16
18 17 #-----------------------------------------------------------------------------
19 18 # Utilities
20 19
21 20 # Note: copied from OInspect, kept here so the testing stuff doesn't create
22 21 # circular dependencies and is easier to reuse.
23 22 def getargspec(obj):
24 23 """Get the names and default values of a function's arguments.
25 24
26 25 A tuple of four things is returned: (args, varargs, varkw, defaults).
27 26 'args' is a list of the argument names (it may contain nested lists).
28 27 'varargs' and 'varkw' are the names of the * and ** arguments or None.
29 28 'defaults' is an n-tuple of the default values of the last n arguments.
30 29
31 30 Modified version of inspect.getargspec from the Python Standard
32 31 Library."""
33 32
34 33 if inspect.isfunction(obj):
35 34 func_obj = obj
36 35 elif inspect.ismethod(obj):
37 36 func_obj = obj.im_func
38 37 else:
39 38 raise TypeError('arg is not a Python function')
40 39 args, varargs, varkw = inspect.getargs(func_obj.func_code)
41 40 return args, varargs, varkw, func_obj.func_defaults
42 41
43 42 #-----------------------------------------------------------------------------
44 43 # Testing functions
45 44
46 45 @dec.as_unittest
47 46 def trivial():
48 47 """A trivial test"""
49 48 pass
50 49
51 50 # Some examples of parametric tests.
52 51
53 52 def is_smaller(i,j):
54 53 assert i<j,"%s !< %s" % (i,j)
55 54
56 55 class Tester(ParametricTestCase):
57 56
58 57 def test_parametric(self):
59 58 yield is_smaller(3, 4)
60 59 x, y = 1, 2
61 60 yield is_smaller(x, y)
62 61
63 62 @dec.parametric
64 63 def test_par_standalone():
65 64 yield is_smaller(3, 4)
66 65 x, y = 1, 2
67 66 yield is_smaller(x, y)
68 67
69 68
70 69 @dec.skip
71 70 def test_deliberately_broken():
72 71 """A deliberately broken test - we want to skip this one."""
73 72 1/0
74 73
75 74 @dec.skip('Testing the skip decorator')
76 75 def test_deliberately_broken2():
77 76 """Another deliberately broken test - we want to skip this one."""
78 77 1/0
79 78
80 79
81 80 # Verify that we can correctly skip the doctest for a function at will, but
82 81 # that the docstring itself is NOT destroyed by the decorator.
83 82 @skip_doctest
84 83 def doctest_bad(x,y=1,**k):
85 84 """A function whose doctest we need to skip.
86 85
87 86 >>> 1+1
88 87 3
89 88 """
90 89 print 'x:',x
91 90 print 'y:',y
92 91 print 'k:',k
93 92
94 93
95 94 def call_doctest_bad():
96 95 """Check that we can still call the decorated functions.
97 96
98 97 >>> doctest_bad(3,y=4)
99 98 x: 3
100 99 y: 4
101 100 k: {}
102 101 """
103 102 pass
104 103
105 104
106 105 def test_skip_dt_decorator():
107 106 """Doctest-skipping decorator should preserve the docstring.
108 107 """
109 108 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
110 109 check = """A function whose doctest we need to skip.
111 110
112 111 >>> 1+1
113 112 3
114 113 """
115 114 # Fetch the docstring from doctest_bad after decoration.
116 115 val = doctest_bad.__doc__
117 116
118 117 nt.assert_equal(check,val,"doctest_bad docstrings don't match")
119 118
120 119
121 120 # Doctest skipping should work for class methods too
122 121 class FooClass(object):
123 122 """FooClass
124 123
125 124 Example:
126 125
127 126 >>> 1+1
128 127 2
129 128 """
130 129
131 130 @skip_doctest
132 131 def __init__(self,x):
133 132 """Make a FooClass.
134 133
135 134 Example:
136 135
137 136 >>> f = FooClass(3)
138 137 junk
139 138 """
140 139 print 'Making a FooClass.'
141 140 self.x = x
142 141
143 142 @skip_doctest
144 143 def bar(self,y):
145 144 """Example:
146 145
147 146 >>> ff = FooClass(3)
148 147 >>> ff.bar(0)
149 148 boom!
150 149 >>> 1/0
151 150 bam!
152 151 """
153 152 return 1/y
154 153
155 154 def baz(self,y):
156 155 """Example:
157 156
158 157 >>> ff2 = FooClass(3)
159 158 Making a FooClass.
160 159 >>> ff2.baz(3)
161 160 True
162 161 """
163 162 return self.x==y
164 163
165 164
166 165 def test_skip_dt_decorator2():
167 166 """Doctest-skipping decorator should preserve function signature.
168 167 """
169 168 # Hardcoded correct answer
170 169 dtargs = (['x', 'y'], None, 'k', (1,))
171 170 # Introspect out the value
172 171 dtargsr = getargspec(doctest_bad)
173 172 assert dtargsr==dtargs, \
174 173 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
175 174
176 175
177 176 @dec.skip_linux
178 177 def test_linux():
179 178 nt.assert_false(sys.platform.startswith('linux'),"This test can't run under linux")
180 179
181 180 @dec.skip_win32
182 181 def test_win32():
183 182 nt.assert_not_equal(sys.platform,'win32',"This test can't run under windows")
184 183
185 184 @dec.skip_osx
186 185 def test_osx():
187 186 nt.assert_not_equal(sys.platform,'darwin',"This test can't run under osx")
188 187
@@ -1,136 +1,135 b''
1 1 # encoding: utf-8
2 2 """
3 3 Tests for testing.tools
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import with_statement
17 17
18 18 import os
19 import sys
20 19 import unittest
21 20
22 21 import nose.tools as nt
23 22
24 23 from IPython.testing import decorators as dec
25 24 from IPython.testing import tools as tt
26 25
27 26 #-----------------------------------------------------------------------------
28 27 # Tests
29 28 #-----------------------------------------------------------------------------
30 29
31 30 @dec.skip_win32
32 31 def test_full_path_posix():
33 32 spath = '/foo/bar.py'
34 33 result = tt.full_path(spath,['a.txt','b.txt'])
35 34 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
36 35 spath = '/foo'
37 36 result = tt.full_path(spath,['a.txt','b.txt'])
38 37 nt.assert_equal(result, ['/a.txt', '/b.txt'])
39 38 result = tt.full_path(spath,'a.txt')
40 39 nt.assert_equal(result, ['/a.txt'])
41 40
42 41
43 42 @dec.skip_if_not_win32
44 43 def test_full_path_win32():
45 44 spath = 'c:\\foo\\bar.py'
46 45 result = tt.full_path(spath,['a.txt','b.txt'])
47 46 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
48 47 spath = 'c:\\foo'
49 48 result = tt.full_path(spath,['a.txt','b.txt'])
50 49 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
51 50 result = tt.full_path(spath,'a.txt')
52 51 nt.assert_equal(result, ['c:\\a.txt'])
53 52
54 53
55 54 @dec.parametric
56 55 def test_parser():
57 56 err = ("FAILED (errors=1)", 1, 0)
58 57 fail = ("FAILED (failures=1)", 0, 1)
59 58 both = ("FAILED (errors=1, failures=1)", 1, 1)
60 59 for txt, nerr, nfail in [err, fail, both]:
61 60 nerr1, nfail1 = tt.parse_test_output(txt)
62 61 yield nt.assert_equal(nerr, nerr1)
63 62 yield nt.assert_equal(nfail, nfail1)
64 63
65 64
66 65 @dec.parametric
67 66 def test_temp_pyfile():
68 67 src = 'pass\n'
69 68 fname, fh = tt.temp_pyfile(src)
70 69 yield nt.assert_true(os.path.isfile(fname))
71 70 fh.close()
72 71 with open(fname) as fh2:
73 72 src2 = fh2.read()
74 73 yield nt.assert_equal(src2, src)
75 74
76 75 class TestAssertPrints(unittest.TestCase):
77 76 def test_passing(self):
78 77 with tt.AssertPrints("abc"):
79 78 print "abcd"
80 79 print "def"
81 80 print b"ghi"
82 81
83 82 def test_failing(self):
84 83 def func():
85 84 with tt.AssertPrints("abc"):
86 85 print "acd"
87 86 print "def"
88 87 print b"ghi"
89 88
90 89 self.assertRaises(AssertionError, func)
91 90
92 91
93 92 class Test_ipexec_validate(unittest.TestCase, tt.TempFileMixin):
94 93 def test_main_path(self):
95 94 """Test with only stdout results.
96 95 """
97 96 self.mktmp("print('A')\n"
98 97 "print('B')\n"
99 98 )
100 99 out = "A\nB"
101 100 tt.ipexec_validate(self.fname, out)
102 101
103 102 def test_main_path2(self):
104 103 """Test with only stdout results, expecting windows line endings.
105 104 """
106 105 self.mktmp("print('A')\n"
107 106 "print('B')\n"
108 107 )
109 108 out = "A\r\nB"
110 109 tt.ipexec_validate(self.fname, out)
111 110
112 111 def test_exception_path(self):
113 112 """Test exception path in exception_validate.
114 113 """
115 114 self.mktmp("from __future__ import print_function\n"
116 115 "import sys\n"
117 116 "print('A')\n"
118 117 "print('B')\n"
119 118 "print('C', file=sys.stderr)\n"
120 119 "print('D', file=sys.stderr)\n"
121 120 )
122 121 out = "A\nB"
123 122 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
124 123
125 def test_exception_path(self):
124 def test_exception_path2(self):
126 125 """Test exception path in exception_validate, expecting windows line endings.
127 126 """
128 127 self.mktmp("from __future__ import print_function\n"
129 128 "import sys\n"
130 129 "print('A')\n"
131 130 "print('B')\n"
132 131 "print('C', file=sys.stderr)\n"
133 132 "print('D', file=sys.stderr)\n"
134 133 )
135 134 out = "A\r\nB"
136 135 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
General Comments 0
You need to be logged in to leave comments. Login now