##// END OF EJS Templates
Merge pull request #1629 from minrk/msgspectest...
Fernando Perez -
r6576:48da8dcc merge
parent child Browse files
Show More
@@ -1,430 +1,437
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 20
21 21 from IPython.testing import decorators as dec
22 22 from IPython.utils import io
23 23 from IPython.utils.traitlets import (
24 24 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
25 25 )
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Global setup and utilities
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def setup():
32 32 global KM
33 33 KM = BlockingKernelManager()
34 34
35 35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
36 36 KM.start_channels()
37 37
38 38
39 39 def teardown():
40 40 KM.stop_channels()
41 41 KM.shutdown_kernel()
42 42
43 43
44 44 def flush_channels():
45 45 """flush any messages waiting on the queue"""
46 46 for channel in (KM.shell_channel, KM.sub_channel):
47 47 while True:
48 48 try:
49 49 msg = channel.get_msg(block=True, timeout=0.1)
50 50 except Empty:
51 51 break
52 52 else:
53 validate_message(msg)
53 list(validate_message(msg))
54 54
55 55
56 56 def execute(code='', **kwargs):
57 57 """wrapper for doing common steps for validating an execution request"""
58 58 shell = KM.shell_channel
59 59 sub = KM.sub_channel
60 60
61 61 msg_id = shell.execute(code=code, **kwargs)
62 62 reply = shell.get_msg(timeout=2)
63 validate_message(reply, 'execute_reply', msg_id)
63 list(validate_message(reply, 'execute_reply', msg_id))
64 64 busy = sub.get_msg(timeout=2)
65 validate_message(busy, 'status', msg_id)
65 list(validate_message(busy, 'status', msg_id))
66 66 nt.assert_equals(busy['content']['execution_state'], 'busy')
67 67
68 68 if not kwargs.get('silent'):
69 69 pyin = sub.get_msg(timeout=2)
70 validate_message(pyin, 'pyin', msg_id)
70 list(validate_message(pyin, 'pyin', msg_id))
71 71 nt.assert_equals(pyin['content']['code'], code)
72 72
73 73 return msg_id, reply['content']
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # MSG Spec References
77 77 #-----------------------------------------------------------------------------
78 78
79 79
80 80 class Reference(HasTraits):
81 81
82 82 def check(self, d):
83 83 """validate a dict against our traits"""
84 84 for key in self.trait_names():
85 85 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
86 86 # FIXME: always allow None, probably not a good idea
87 87 if d[key] is None:
88 88 continue
89 89 try:
90 90 setattr(self, key, d[key])
91 91 except TraitError as e:
92 92 yield nt.assert_true(False, str(e))
93 93
94 94
95 95 class RMessage(Reference):
96 96 msg_id = Unicode()
97 97 msg_type = Unicode()
98 98 header = Dict()
99 99 parent_header = Dict()
100 100 content = Dict()
101 101
102 102 class RHeader(Reference):
103 103 msg_id = Unicode()
104 104 msg_type = Unicode()
105 105 session = Unicode()
106 106 username = Unicode()
107 107
108 108 class RContent(Reference):
109 109 status = Enum((u'ok', u'error'))
110 110
111 111
112 112 class ExecuteReply(Reference):
113 113 execution_count = Integer()
114 114 status = Enum((u'ok', u'error'))
115 115
116 116 def check(self, d):
117 117 for tst in Reference.check(self, d):
118 118 yield tst
119 119 if d['status'] == 'ok':
120 120 for tst in ExecuteReplyOkay().check(d):
121 121 yield tst
122 122 elif d['status'] == 'error':
123 123 for tst in ExecuteReplyError().check(d):
124 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 161 for tst in Reference.check(self, d):
162 162 yield tst
163 163 if d['argspec'] is not None:
164 164 for tst in ArgSpec().check(d['argspec']):
165 165 yield tst
166 166
167 167
168 168 class ArgSpec(Reference):
169 169 args = List(Unicode)
170 170 varargs = Unicode()
171 171 varkw = Unicode()
172 172 defaults = List()
173 173
174 174
175 175 class Status(Reference):
176 176 execution_state = Enum((u'busy', u'idle'))
177 177
178 178
179 179 class CompleteReply(Reference):
180 180 matches = List(Unicode)
181 181
182 182
183 183 # IOPub messages
184 184
185 185 class PyIn(Reference):
186 186 code = Unicode()
187 187 execution_count = Integer()
188 188
189 189
190 190 PyErr = ExecuteReplyError
191 191
192 192
193 193 class Stream(Reference):
194 194 name = Enum((u'stdout', u'stderr'))
195 195 data = Unicode()
196 196
197 197
198 198 mime_pat = re.compile(r'\w+/\w+')
199 199
200 200 class DisplayData(Reference):
201 201 source = Unicode()
202 202 metadata = Dict()
203 203 data = Dict()
204 204 def _data_changed(self, name, old, new):
205 205 for k,v in new.iteritems():
206 206 nt.assert_true(mime_pat.match(k))
207 207 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
208 208
209 209
210 210 references = {
211 211 'execute_reply' : ExecuteReply(),
212 212 'object_info_reply' : OInfoReply(),
213 213 'status' : Status(),
214 214 'complete_reply' : CompleteReply(),
215 215 'pyin' : PyIn(),
216 216 'pyerr' : PyErr(),
217 217 'stream' : Stream(),
218 218 'display_data' : DisplayData(),
219 219 }
220 220
221 221
222 222 def validate_message(msg, msg_type=None, parent=None):
223 """validate a message"""
223 """validate a message
224
225 This is a generator, and must be iterated through to actually
226 trigger each test.
227
228 If msg_type and/or parent are given, the msg_type and/or parent msg_id
229 are compared with the given values.
230 """
224 231 RMessage().check(msg)
225 232 if msg_type:
226 233 yield nt.assert_equals(msg['msg_type'], msg_type)
227 234 if parent:
228 235 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
229 236 content = msg['content']
230 237 ref = references[msg['msg_type']]
231 238 for tst in ref.check(content):
232 239 yield tst
233 240
234 241
235 242 #-----------------------------------------------------------------------------
236 243 # Tests
237 244 #-----------------------------------------------------------------------------
238 245
239 246 # Shell channel
240 247
241 248 @dec.parametric
242 249 def test_execute():
243 250 flush_channels()
244 251
245 252 shell = KM.shell_channel
246 253 msg_id = shell.execute(code='x=1')
247 254 reply = shell.get_msg(timeout=2)
248 255 for tst in validate_message(reply, 'execute_reply', msg_id):
249 256 yield tst
250 257
251 258
252 259 @dec.parametric
253 260 def test_execute_silent():
254 261 flush_channels()
255 262 msg_id, reply = execute(code='x=1', silent=True)
256 263
257 264 # flush status=idle
258 265 status = KM.sub_channel.get_msg(timeout=2)
259 266 for tst in validate_message(status, 'status', msg_id):
260 267 yield tst
261 268 nt.assert_equals(status['content']['execution_state'], 'idle')
262 269
263 270 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
264 271 count = reply['execution_count']
265 272
266 273 msg_id, reply = execute(code='x=2', silent=True)
267 274
268 275 # flush status=idle
269 276 status = KM.sub_channel.get_msg(timeout=2)
270 277 for tst in validate_message(status, 'status', msg_id):
271 278 yield tst
272 279 yield nt.assert_equals(status['content']['execution_state'], 'idle')
273 280
274 281 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
275 282 count_2 = reply['execution_count']
276 283 yield nt.assert_equals(count_2, count)
277 284
278 285
279 286 @dec.parametric
280 287 def test_execute_error():
281 288 flush_channels()
282 289
283 290 msg_id, reply = execute(code='1/0')
284 291 yield nt.assert_equals(reply['status'], 'error')
285 292 yield nt.assert_equals(reply['ename'], 'ZeroDivisionError')
286 293
287 294 pyerr = KM.sub_channel.get_msg(timeout=2)
288 295 for tst in validate_message(pyerr, 'pyerr', msg_id):
289 296 yield tst
290 297
291 298
292 299 def test_execute_inc():
293 300 """execute request should increment execution_count"""
294 301 flush_channels()
295 302
296 303 msg_id, reply = execute(code='x=1')
297 304 count = reply['execution_count']
298 305
299 306 flush_channels()
300 307
301 308 msg_id, reply = execute(code='x=2')
302 309 count_2 = reply['execution_count']
303 310 nt.assert_equals(count_2, count+1)
304 311
305 312
306 313 def test_user_variables():
307 314 flush_channels()
308 315
309 316 msg_id, reply = execute(code='x=1', user_variables=['x'])
310 317 user_variables = reply['user_variables']
311 318 nt.assert_equals(user_variables, {u'x' : u'1'})
312 319
313 320
314 321 def test_user_expressions():
315 322 flush_channels()
316 323
317 324 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
318 325 user_expressions = reply['user_expressions']
319 326 nt.assert_equals(user_expressions, {u'foo' : u'2'})
320 327
321 328
322 329 @dec.parametric
323 330 def test_oinfo():
324 331 flush_channels()
325 332
326 333 shell = KM.shell_channel
327 334
328 335 msg_id = shell.object_info('a')
329 336 reply = shell.get_msg(timeout=2)
330 337 for tst in validate_message(reply, 'object_info_reply', msg_id):
331 338 yield tst
332 339
333 340
334 341 @dec.parametric
335 342 def test_oinfo_found():
336 343 flush_channels()
337 344
338 345 shell = KM.shell_channel
339 346
340 347 msg_id, reply = execute(code='a=5')
341 348
342 349 msg_id = shell.object_info('a')
343 350 reply = shell.get_msg(timeout=2)
344 351 for tst in validate_message(reply, 'object_info_reply', msg_id):
345 352 yield tst
346 353 content = reply['content']
347 354 yield nt.assert_true(content['found'])
348 355 argspec = content['argspec']
349 356 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
350 357
351 358
352 359 @dec.parametric
353 360 def test_oinfo_detail():
354 361 flush_channels()
355 362
356 363 shell = KM.shell_channel
357 364
358 365 msg_id, reply = execute(code='ip=get_ipython()')
359 366
360 367 msg_id = shell.object_info('ip.object_inspect', detail_level=2)
361 368 reply = shell.get_msg(timeout=2)
362 369 for tst in validate_message(reply, 'object_info_reply', msg_id):
363 370 yield tst
364 371 content = reply['content']
365 372 yield nt.assert_true(content['found'])
366 373 argspec = content['argspec']
367 374 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
368 375 yield nt.assert_equals(argspec['defaults'], [0])
369 376
370 377
371 378 @dec.parametric
372 379 def test_oinfo_not_found():
373 380 flush_channels()
374 381
375 382 shell = KM.shell_channel
376 383
377 384 msg_id = shell.object_info('dne')
378 385 reply = shell.get_msg(timeout=2)
379 386 for tst in validate_message(reply, 'object_info_reply', msg_id):
380 387 yield tst
381 388 content = reply['content']
382 389 yield nt.assert_false(content['found'])
383 390
384 391
385 392 @dec.parametric
386 393 def test_complete():
387 394 flush_channels()
388 395
389 396 shell = KM.shell_channel
390 397
391 398 msg_id, reply = execute(code="alpha = albert = 5")
392 399
393 400 msg_id = shell.complete('al', 'al', 2)
394 401 reply = shell.get_msg(timeout=2)
395 402 for tst in validate_message(reply, 'complete_reply', msg_id):
396 403 yield tst
397 404 matches = reply['content']['matches']
398 405 for name in ('alpha', 'albert'):
399 406 yield nt.assert_true(name in matches, "Missing match: %r" % name)
400 407
401 408
402 409 # IOPub channel
403 410
404 411
405 412 @dec.parametric
406 413 def test_stream():
407 414 flush_channels()
408 415
409 416 msg_id, reply = execute("print('hi')")
410 417
411 418 stdout = KM.sub_channel.get_msg(timeout=2)
412 419 for tst in validate_message(stdout, 'stream', msg_id):
413 420 yield tst
414 421 content = stdout['content']
415 422 yield nt.assert_equals(content['name'], u'stdout')
416 423 yield nt.assert_equals(content['data'], u'hi\n')
417 424
418 425
419 426 @dec.parametric
420 427 def test_display_data():
421 428 flush_channels()
422 429
423 430 msg_id, reply = execute("from IPython.core.display import display; display(1)")
424 431
425 432 display = KM.sub_channel.get_msg(timeout=2)
426 433 for tst in validate_message(display, 'display_data', parent=msg_id):
427 434 yield tst
428 435 data = display['content']['data']
429 436 yield nt.assert_equals(data['text/plain'], u'1')
430 437
General Comments 0
You need to be logged in to leave comments. Login now