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