##// END OF EJS Templates
use parametric tests in message_spec...
MinRK -
Show More
@@ -1,401 +1,421 b''
1 1 """Test suite for our zeromq-based messaging specification.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2010-2011 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING.txt, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 import re
11 11 import sys
12 12 import time
13 13 from subprocess import PIPE
14 14 from Queue import Empty
15 15
16 16 import nose.tools as nt
17 17
18 18 from ..blockingkernelmanager import BlockingKernelManager
19 19
20
21 from IPython.testing import decorators as dec
20 22 from IPython.utils import io
21 23 from IPython.utils.traitlets import (
22 24 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
23 25 )
24 26
25 27 #-----------------------------------------------------------------------------
26 28 # Global setup and utilities
27 29 #-----------------------------------------------------------------------------
28 30
29 31 def setup():
30 32 global KM
31 33 KM = BlockingKernelManager()
32 34
33 35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
34 36 KM.start_channels()
35 37
38
36 39 def teardown():
37 40 KM.stop_channels()
38 41 KM.shutdown_kernel()
39 42
43
40 44 def flush_channels():
41 45 """flush any messages waiting on the queue"""
42 46 for channel in (KM.shell_channel, KM.sub_channel):
43 for msg in channel.get_msgs():
47 while True:
48 try:
49 msg = channel.get_msg(block=True, timeout=0.1)
50 except Empty:
51 break
52 else:
44 53 validate_message(msg)
45 54
46 def flush(f):
47 """decorator for flushing any incoming messages unhandled after the test"""
48
49 def wrapped(*args, **kwargs):
50 result = f(*args, **kwargs)
51 flush_channels()
52 return result
53
54 return wrapped
55
56 def flush_busy_pyin(msg_id=None):
57 """flush status=busy / pyin messages"""
58 55
59 56 def execute(code='', **kwargs):
60 57 """wrapper for doing common steps for validating an execution request"""
61 58 shell = KM.shell_channel
62 59 sub = KM.sub_channel
63 60
64 61 msg_id = shell.execute(code=code, **kwargs)
65 62 reply = shell.get_msg(timeout=2)
66 63 validate_message(reply, 'execute_reply', msg_id)
67 64 busy = sub.get_msg(timeout=2)
68 65 validate_message(busy, 'status', msg_id)
69 66 nt.assert_equals(busy['content']['execution_state'], 'busy')
70 67
71 68 if not kwargs.get('silent'):
72 69 pyin = sub.get_msg(timeout=2)
73 70 validate_message(pyin, 'pyin', msg_id)
74 71 nt.assert_equals(pyin['content']['code'], code)
75 72
76 73 return msg_id, reply['content']
77 74
78 75 #-----------------------------------------------------------------------------
79 76 # MSG Spec References
80 77 #-----------------------------------------------------------------------------
81 78
82 79
83 80 class Reference(HasTraits):
84 81
85 82 def check(self, d):
86 83 """validate a dict against our traits"""
87 84 for key in self.trait_names():
88 nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
85 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
89 86 # FIXME: always allow None, probably not a good idea
90 87 if d[key] is None:
91 88 continue
92 89 try:
93 90 setattr(self, key, d[key])
94 91 except TraitError as e:
95 nt.assert_true(False, str(e))
92 yield nt.assert_true(False, str(e))
96 93
97 94
98 95 class RMessage(Reference):
99 96 msg_id = Unicode()
100 97 msg_type = Unicode()
101 98 header = Dict()
102 99 parent_header = Dict()
103 100 content = Dict()
104 101
105 102 class RHeader(Reference):
106 103 msg_id = Unicode()
107 104 msg_type = Unicode()
108 105 session = Unicode()
109 106 username = Unicode()
110 107
111 108 class RContent(Reference):
112 109 status = Enum((u'ok', u'error'))
113 110
114 111
115 112 class ExecuteReply(Reference):
116 113 execution_count = Integer()
117 114 status = Enum((u'ok', u'error'))
118 115
119 116 def check(self, d):
120 Reference.check(self, d)
117 for tst in Reference.check(self, d):
118 yield tst
121 119 if d['status'] == 'ok':
122 ExecuteReplyOkay().check(d)
120 for tst in ExecuteReplyOkay().check(d):
121 yield tst
123 122 elif d['status'] == 'error':
124 ExecuteReplyError().check(d)
123 for tst in ExecuteReplyError().check(d):
124 yield tst
125 125
126 126
127 127 class ExecuteReplyOkay(Reference):
128 128 payload = List(Dict)
129 129 user_variables = Dict()
130 130 user_expressions = Dict()
131 131
132 132
133 133 class ExecuteReplyError(Reference):
134 134 ename = Unicode()
135 135 evalue = Unicode()
136 136 traceback = List(Unicode)
137 137
138 138
139 139 class OInfoReply(Reference):
140 140 name = Unicode()
141 141 found = Bool()
142 142 ismagic = Bool()
143 143 isalias = Bool()
144 144 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
145 145 type_name = Unicode()
146 146 string_form = Unicode()
147 147 base_class = Unicode()
148 148 length = Integer()
149 149 file = Unicode()
150 150 definition = Unicode()
151 151 argspec = Dict()
152 152 init_definition = Unicode()
153 153 docstring = Unicode()
154 154 init_docstring = Unicode()
155 155 class_docstring = Unicode()
156 156 call_def = Unicode()
157 157 call_docstring = Unicode()
158 158 source = Unicode()
159 159
160 160 def check(self, d):
161 Reference.check(self, d)
161 for tst in Reference.check(self, d):
162 yield tst
162 163 if d['argspec'] is not None:
163 ArgSpec().check(d['argspec'])
164 for tst in ArgSpec().check(d['argspec']):
165 yield tst
164 166
165 167
166 168 class ArgSpec(Reference):
167 169 args = List(Unicode)
168 170 varargs = Unicode()
169 171 varkw = Unicode()
170 defaults = List(Unicode)
172 defaults = List()
171 173
172 174
173 175 class Status(Reference):
174 176 execution_state = Enum((u'busy', u'idle'))
175 177
176 178
177 179 class CompleteReply(Reference):
178 180 matches = List(Unicode)
179 181
180 182
181 183 # IOPub messages
182 184
183 185 class PyIn(Reference):
184 186 code = Unicode()
185 187
186 188
187 189 PyErr = ExecuteReplyError
188 190
189 191
190 192 class Stream(Reference):
191 193 name = Enum((u'stdout', u'stderr'))
192 194 data = Unicode()
193 195
194 196
195 197 mime_pat = re.compile(r'\w+/\w+')
196 198
197 199 class DisplayData(Reference):
198 200 source = Unicode()
199 201 metadata = Dict()
200 202 data = Dict()
201 203 def _data_changed(self, name, old, new):
202 204 for k,v in new.iteritems():
203 205 nt.assert_true(mime_pat.match(k))
204 206 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
205 207
206 208
207 209 references = {
208 210 'execute_reply' : ExecuteReply(),
209 211 'object_info_reply' : OInfoReply(),
210 212 'status' : Status(),
211 213 'complete_reply' : CompleteReply(),
212 214 'pyin' : PyIn(),
213 215 'pyerr' : PyErr(),
214 216 'stream' : Stream(),
215 217 'display_data' : DisplayData(),
216 218 }
217 219
218 220
219 221 def validate_message(msg, msg_type=None, parent=None):
220 222 """validate a message"""
221 223 RMessage().check(msg)
222 224 if msg_type:
223 nt.assert_equals(msg['msg_type'], msg_type)
225 yield nt.assert_equals(msg['msg_type'], msg_type)
224 226 if parent:
225 nt.assert_equal(msg['parent_header']['msg_id'], parent)
227 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
226 228 content = msg['content']
227 229 ref = references[msg['msg_type']]
228 ref.check(content)
230 for tst in ref.check(content):
231 yield tst
229 232
230 233
231 234 #-----------------------------------------------------------------------------
232 235 # Tests
233 236 #-----------------------------------------------------------------------------
234 237
235 238 # Shell channel
236 239
240 @dec.parametric
237 241 def test_execute():
242 flush_channels()
243
238 244 shell = KM.shell_channel
239 245 msg_id = shell.execute(code='x=1')
240 246 reply = shell.get_msg(timeout=2)
241 validate_message(reply, 'execute_reply', msg_id)
242
243 flush_channels()
247 for tst in validate_message(reply, 'execute_reply', msg_id):
248 yield tst
244 249
245 250
251 @dec.parametric
246 252 def test_execute_silent():
253 flush_channels()
247 254 msg_id, reply = execute(code='x=1', silent=True)
248 255
249 256 # flush status=idle
250 257 status = KM.sub_channel.get_msg(timeout=2)
251 validate_message(status, 'status', msg_id)
258 for tst in validate_message(status, 'status', msg_id):
259 yield tst
252 260 nt.assert_equals(status['content']['execution_state'], 'idle')
253 261
254 nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
262 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
255 263 count = reply['execution_count']
256 264
257 265 msg_id, reply = execute(code='x=2', silent=True)
258 266
259 267 # flush status=idle
260 268 status = KM.sub_channel.get_msg(timeout=2)
261 validate_message(status, 'status', msg_id)
262 nt.assert_equals(status['content']['execution_state'], 'idle')
269 for tst in validate_message(status, 'status', msg_id):
270 yield tst
271 yield nt.assert_equals(status['content']['execution_state'], 'idle')
263 272
264 nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
273 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
265 274 count_2 = reply['execution_count']
266 nt.assert_equals(count_2, count)
267
268 flush_channels()
275 yield nt.assert_equals(count_2, count)
269 276
270 277
278 @dec.parametric
271 279 def test_execute_error():
280 flush_channels()
272 281
273 282 msg_id, reply = execute(code='1/0')
274 nt.assert_equals(reply['status'], 'error')
275 nt.assert_equals(reply['ename'], 'ZeroDivisionError')
283 yield nt.assert_equals(reply['status'], 'error')
284 yield nt.assert_equals(reply['ename'], 'ZeroDivisionError')
276 285
277 286 pyerr = KM.sub_channel.get_msg(timeout=2)
278 validate_message(pyerr, 'pyerr', msg_id)
279
280 flush_channels()
287 for tst in validate_message(pyerr, 'pyerr', msg_id):
288 yield tst
281 289
282 290
283 291 def test_execute_inc():
284 292 """execute request should increment execution_count"""
293 flush_channels()
294
285 295 msg_id, reply = execute(code='x=1')
286 296 count = reply['execution_count']
287 297
288 298 flush_channels()
289 299
290 300 msg_id, reply = execute(code='x=2')
291 301 count_2 = reply['execution_count']
292 302 nt.assert_equals(count_2, count+1)
293 303
294 flush_channels()
295
296 304
297 305 def test_user_variables():
306 flush_channels()
307
298 308 msg_id, reply = execute(code='x=1', user_variables=['x'])
299 309 user_variables = reply['user_variables']
300 310 nt.assert_equals(user_variables, {u'x' : u'1'})
301 311
302 flush_channels()
303
304 312
305 313 def test_user_expressions():
314 flush_channels()
315
306 316 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
307 317 user_expressions = reply['user_expressions']
308 318 nt.assert_equals(user_expressions, {u'foo' : u'2'})
309 319
310 flush_channels()
311
312 320
321 @dec.parametric
313 322 def test_oinfo():
323 flush_channels()
324
314 325 shell = KM.shell_channel
315 326
316 327 msg_id = shell.object_info('a')
317 328 reply = shell.get_msg(timeout=2)
318 validate_message(reply, 'object_info_reply', msg_id)
319
320 flush_channels()
329 for tst in validate_message(reply, 'object_info_reply', msg_id):
330 yield tst
321 331
322 332
333 @dec.parametric
323 334 def test_oinfo_found():
335 flush_channels()
336
324 337 shell = KM.shell_channel
325 338
326 339 msg_id, reply = execute(code='a=5')
327 340
328 341 msg_id = shell.object_info('a')
329 342 reply = shell.get_msg(timeout=2)
330 validate_message(reply, 'object_info_reply', msg_id)
343 for tst in validate_message(reply, 'object_info_reply', msg_id):
344 yield tst
331 345 content = reply['content']
332 nt.assert_true(content['found'])
333
334 flush_channels()
346 yield nt.assert_true(content['found'])
335 347
336 348
349 @dec.parametric
337 350 def test_oinfo_detail():
351 flush_channels()
352
338 353 shell = KM.shell_channel
339 354
340 355 msg_id, reply = execute(code='ip=get_ipython()')
341 356
342 357 msg_id = shell.object_info('ip.object_inspect', detail_level=2)
343 358 reply = shell.get_msg(timeout=2)
344 validate_message(reply, 'object_info_reply', msg_id)
359 for tst in validate_message(reply, 'object_info_reply', msg_id):
360 yield tst
345 361 content = reply['content']
346 nt.assert_true(content['found'])
347
348 flush_channels()
362 yield nt.assert_true(content['found'])
349 363
350 364
365 @dec.parametric
351 366 def test_oinfo_not_found():
367 flush_channels()
368
352 369 shell = KM.shell_channel
353 370
354 371 msg_id = shell.object_info('dne')
355 372 reply = shell.get_msg(timeout=2)
356 validate_message(reply, 'object_info_reply', msg_id)
373 for tst in validate_message(reply, 'object_info_reply', msg_id):
374 yield tst
357 375 content = reply['content']
358 nt.assert_false(content['found'])
359
360 flush_channels()
376 yield nt.assert_false(content['found'])
361 377
362 378
379 @dec.parametric
363 380 def test_complete():
381 flush_channels()
382
364 383 shell = KM.shell_channel
365 384
366 385 msg_id, reply = execute(code="alpha = albert = 5")
367 386
368 387 msg_id = shell.complete('al', 'al', 2)
369 388 reply = shell.get_msg(timeout=2)
370 validate_message(reply, 'complete_reply', msg_id)
389 for tst in validate_message(reply, 'complete_reply', msg_id):
390 yield tst
371 391 matches = reply['content']['matches']
372 392 for name in ('alpha', 'albert'):
373 nt.assert_true(name in matches, "Missing match: %r" % name)
374
375 flush_channels()
393 yield nt.assert_true(name in matches, "Missing match: %r" % name)
376 394
377 395
396 @dec.parametric
378 397 def test_stream():
398 flush_channels()
399
379 400 msg_id, reply = execute("print('hi')")
380 401
381 402 stdout = KM.sub_channel.get_msg(timeout=2)
382 validate_message(stdout, 'stream', msg_id)
403 for tst in validate_message(stdout, 'stream', msg_id):
404 yield tst
383 405 content = stdout['content']
384 nt.assert_equals(content['name'], u'stdout')
385 nt.assert_equals(content['data'], u'hi\n')
386
387 flush_channels()
406 yield nt.assert_equals(content['name'], u'stdout')
407 yield nt.assert_equals(content['data'], u'hi\n')
388 408
389 409
390 def test_display():
410 @dec.parametric
411 def test_display_data():
412 flush_channels()
391 413
392 414 msg_id, reply = execute("from IPython.core.display import display; display(1)")
393 415
394 416 display = KM.sub_channel.get_msg(timeout=2)
395 validate_message(display, 'display_data', parent=msg_id)
417 for tst in validate_message(display, 'display_data', parent=msg_id):
418 yield tst
396 419 data = display['content']['data']
397 nt.assert_equals(data['text/plain'], u'1')
398
399 flush_channels()
400
420 yield nt.assert_equals(data['text/plain'], u'1')
401 421
General Comments 0
You need to be logged in to leave comments. Login now