##// END OF EJS Templates
add missing "starting" status to test_message_spec
MinRK -
Show More
@@ -1,484 +1,484
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 IPython.kernel import KernelManager, BlockingKernelClient
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, Any,
25 25 )
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Global setup and utilities
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def setup():
32 32 global KM, KC
33 33 KM = KernelManager()
34 34 KM.client_factory = BlockingKernelClient
35 35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
36 36 KC = KM.client()
37 37 KC.start_channels()
38 38
39 39 # wait for kernel to be ready
40 40 KC.execute("pass")
41 41 KC.get_shell_msg(block=True, timeout=5)
42 42 flush_channels()
43 43
44 44
45 45 def teardown():
46 46 KC.stop_channels()
47 47 KM.shutdown_kernel()
48 48
49 49
50 50 def flush_channels(kc=None):
51 51 """flush any messages waiting on the queue"""
52 52 if kc is None:
53 53 kc = KC
54 54 for channel in (kc.shell_channel, kc.iopub_channel):
55 55 while True:
56 56 try:
57 57 msg = channel.get_msg(block=True, timeout=0.1)
58 58 except Empty:
59 59 break
60 60 else:
61 61 list(validate_message(msg))
62 62
63 63
64 64 def execute(code='', kc=None, **kwargs):
65 65 """wrapper for doing common steps for validating an execution request"""
66 66 msg_id = KC.execute(code=code, **kwargs)
67 67 reply = KC.get_shell_msg(timeout=2)
68 68 list(validate_message(reply, 'execute_reply', msg_id))
69 69 busy = KC.get_iopub_msg(timeout=2)
70 70 list(validate_message(busy, 'status', msg_id))
71 71 nt.assert_equal(busy['content']['execution_state'], 'busy')
72 72
73 73 if not kwargs.get('silent'):
74 74 pyin = KC.get_iopub_msg(timeout=2)
75 75 list(validate_message(pyin, 'pyin', msg_id))
76 76 nt.assert_equal(pyin['content']['code'], code)
77 77
78 78 return msg_id, reply['content']
79 79
80 80 #-----------------------------------------------------------------------------
81 81 # MSG Spec References
82 82 #-----------------------------------------------------------------------------
83 83
84 84
85 85 class Reference(HasTraits):
86 86
87 87 """
88 88 Base class for message spec specification testing.
89 89
90 90 This class is the core of the message specification test. The
91 91 idea is that child classes implement trait attributes for each
92 92 message keys, so that message keys can be tested against these
93 93 traits using :meth:`check` method.
94 94
95 95 """
96 96
97 97 def check(self, d):
98 98 """validate a dict against our traits"""
99 99 for key in self.trait_names():
100 100 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
101 101 # FIXME: always allow None, probably not a good idea
102 102 if d[key] is None:
103 103 continue
104 104 try:
105 105 setattr(self, key, d[key])
106 106 except TraitError as e:
107 107 yield nt.assert_true(False, str(e))
108 108
109 109
110 110 class RMessage(Reference):
111 111 msg_id = Unicode()
112 112 msg_type = Unicode()
113 113 header = Dict()
114 114 parent_header = Dict()
115 115 content = Dict()
116 116
117 117 class RHeader(Reference):
118 118 msg_id = Unicode()
119 119 msg_type = Unicode()
120 120 session = Unicode()
121 121 username = Unicode()
122 122
123 123 class RContent(Reference):
124 124 status = Enum((u'ok', u'error'))
125 125
126 126
127 127 class ExecuteReply(Reference):
128 128 execution_count = Integer()
129 129 status = Enum((u'ok', u'error'))
130 130
131 131 def check(self, d):
132 132 for tst in Reference.check(self, d):
133 133 yield tst
134 134 if d['status'] == 'ok':
135 135 for tst in ExecuteReplyOkay().check(d):
136 136 yield tst
137 137 elif d['status'] == 'error':
138 138 for tst in ExecuteReplyError().check(d):
139 139 yield tst
140 140
141 141
142 142 class ExecuteReplyOkay(Reference):
143 143 payload = List(Dict)
144 144 user_variables = Dict()
145 145 user_expressions = Dict()
146 146
147 147
148 148 class ExecuteReplyError(Reference):
149 149 ename = Unicode()
150 150 evalue = Unicode()
151 151 traceback = List(Unicode)
152 152
153 153
154 154 class OInfoReply(Reference):
155 155 name = Unicode()
156 156 found = Bool()
157 157 ismagic = Bool()
158 158 isalias = Bool()
159 159 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
160 160 type_name = Unicode()
161 161 string_form = Unicode()
162 162 base_class = Unicode()
163 163 length = Integer()
164 164 file = Unicode()
165 165 definition = Unicode()
166 166 argspec = Dict()
167 167 init_definition = Unicode()
168 168 docstring = Unicode()
169 169 init_docstring = Unicode()
170 170 class_docstring = Unicode()
171 171 call_def = Unicode()
172 172 call_docstring = Unicode()
173 173 source = Unicode()
174 174
175 175 def check(self, d):
176 176 for tst in Reference.check(self, d):
177 177 yield tst
178 178 if d['argspec'] is not None:
179 179 for tst in ArgSpec().check(d['argspec']):
180 180 yield tst
181 181
182 182
183 183 class ArgSpec(Reference):
184 184 args = List(Unicode)
185 185 varargs = Unicode()
186 186 varkw = Unicode()
187 187 defaults = List()
188 188
189 189
190 190 class Status(Reference):
191 execution_state = Enum((u'busy', u'idle'))
191 execution_state = Enum((u'busy', u'idle', u'starting'))
192 192
193 193
194 194 class CompleteReply(Reference):
195 195 matches = List(Unicode)
196 196
197 197
198 198 def Version(num, trait=Integer):
199 199 return List(trait, default_value=[0] * num, minlen=num, maxlen=num)
200 200
201 201
202 202 class KernelInfoReply(Reference):
203 203
204 204 protocol_version = Version(2)
205 205 ipython_version = Version(4, Any)
206 206 language_version = Version(3)
207 207 language = Unicode()
208 208
209 209 def _ipython_version_changed(self, name, old, new):
210 210 for v in new:
211 211 nt.assert_true(
212 212 isinstance(v, int) or isinstance(v, basestring),
213 213 'expected int or string as version component, got {0!r}'
214 214 .format(v))
215 215
216 216
217 217 # IOPub messages
218 218
219 219 class PyIn(Reference):
220 220 code = Unicode()
221 221 execution_count = Integer()
222 222
223 223
224 224 PyErr = ExecuteReplyError
225 225
226 226
227 227 class Stream(Reference):
228 228 name = Enum((u'stdout', u'stderr'))
229 229 data = Unicode()
230 230
231 231
232 232 mime_pat = re.compile(r'\w+/\w+')
233 233
234 234 class DisplayData(Reference):
235 235 source = Unicode()
236 236 metadata = Dict()
237 237 data = Dict()
238 238 def _data_changed(self, name, old, new):
239 239 for k,v in new.iteritems():
240 240 nt.assert_true(mime_pat.match(k))
241 241 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
242 242
243 243
244 244 class PyOut(Reference):
245 245 execution_count = Integer()
246 246 data = Dict()
247 247 def _data_changed(self, name, old, new):
248 248 for k,v in new.iteritems():
249 249 nt.assert_true(mime_pat.match(k))
250 250 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
251 251
252 252
253 253 references = {
254 254 'execute_reply' : ExecuteReply(),
255 255 'object_info_reply' : OInfoReply(),
256 256 'status' : Status(),
257 257 'complete_reply' : CompleteReply(),
258 258 'kernel_info_reply': KernelInfoReply(),
259 259 'pyin' : PyIn(),
260 260 'pyout' : PyOut(),
261 261 'pyerr' : PyErr(),
262 262 'stream' : Stream(),
263 263 'display_data' : DisplayData(),
264 264 }
265 265 """
266 266 Specifications of `content` part of the reply messages.
267 267 """
268 268
269 269
270 270 def validate_message(msg, msg_type=None, parent=None):
271 271 """validate a message
272 272
273 273 This is a generator, and must be iterated through to actually
274 274 trigger each test.
275 275
276 276 If msg_type and/or parent are given, the msg_type and/or parent msg_id
277 277 are compared with the given values.
278 278 """
279 279 RMessage().check(msg)
280 280 if msg_type:
281 281 yield nt.assert_equal(msg['msg_type'], msg_type)
282 282 if parent:
283 283 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
284 284 content = msg['content']
285 285 ref = references[msg['msg_type']]
286 286 for tst in ref.check(content):
287 287 yield tst
288 288
289 289
290 290 #-----------------------------------------------------------------------------
291 291 # Tests
292 292 #-----------------------------------------------------------------------------
293 293
294 294 # Shell channel
295 295
296 296 @dec.parametric
297 297 def test_execute():
298 298 flush_channels()
299 299
300 300 msg_id = KC.execute(code='x=1')
301 301 reply = KC.get_shell_msg(timeout=2)
302 302 for tst in validate_message(reply, 'execute_reply', msg_id):
303 303 yield tst
304 304
305 305
306 306 @dec.parametric
307 307 def test_execute_silent():
308 308 flush_channels()
309 309 msg_id, reply = execute(code='x=1', silent=True)
310 310
311 311 # flush status=idle
312 312 status = KC.iopub_channel.get_msg(timeout=2)
313 313 for tst in validate_message(status, 'status', msg_id):
314 314 yield tst
315 315 nt.assert_equal(status['content']['execution_state'], 'idle')
316 316
317 317 yield nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
318 318 count = reply['execution_count']
319 319
320 320 msg_id, reply = execute(code='x=2', silent=True)
321 321
322 322 # flush status=idle
323 323 status = KC.iopub_channel.get_msg(timeout=2)
324 324 for tst in validate_message(status, 'status', msg_id):
325 325 yield tst
326 326 yield nt.assert_equal(status['content']['execution_state'], 'idle')
327 327
328 328 yield nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
329 329 count_2 = reply['execution_count']
330 330 yield nt.assert_equal(count_2, count)
331 331
332 332
333 333 @dec.parametric
334 334 def test_execute_error():
335 335 flush_channels()
336 336
337 337 msg_id, reply = execute(code='1/0')
338 338 yield nt.assert_equal(reply['status'], 'error')
339 339 yield nt.assert_equal(reply['ename'], 'ZeroDivisionError')
340 340
341 341 pyerr = KC.iopub_channel.get_msg(timeout=2)
342 342 for tst in validate_message(pyerr, 'pyerr', msg_id):
343 343 yield tst
344 344
345 345
346 346 def test_execute_inc():
347 347 """execute request should increment execution_count"""
348 348 flush_channels()
349 349
350 350 msg_id, reply = execute(code='x=1')
351 351 count = reply['execution_count']
352 352
353 353 flush_channels()
354 354
355 355 msg_id, reply = execute(code='x=2')
356 356 count_2 = reply['execution_count']
357 357 nt.assert_equal(count_2, count+1)
358 358
359 359
360 360 def test_user_variables():
361 361 flush_channels()
362 362
363 363 msg_id, reply = execute(code='x=1', user_variables=['x'])
364 364 user_variables = reply['user_variables']
365 365 nt.assert_equal(user_variables, {u'x' : u'1'})
366 366
367 367
368 368 def test_user_expressions():
369 369 flush_channels()
370 370
371 371 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
372 372 user_expressions = reply['user_expressions']
373 373 nt.assert_equal(user_expressions, {u'foo' : u'2'})
374 374
375 375
376 376 @dec.parametric
377 377 def test_oinfo():
378 378 flush_channels()
379 379
380 380 msg_id = KC.object_info('a')
381 381 reply = KC.get_shell_msg(timeout=2)
382 382 for tst in validate_message(reply, 'object_info_reply', msg_id):
383 383 yield tst
384 384
385 385
386 386 @dec.parametric
387 387 def test_oinfo_found():
388 388 flush_channels()
389 389
390 390 msg_id, reply = execute(code='a=5')
391 391
392 392 msg_id = KC.object_info('a')
393 393 reply = KC.get_shell_msg(timeout=2)
394 394 for tst in validate_message(reply, 'object_info_reply', msg_id):
395 395 yield tst
396 396 content = reply['content']
397 397 yield nt.assert_true(content['found'])
398 398 argspec = content['argspec']
399 399 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
400 400
401 401
402 402 @dec.parametric
403 403 def test_oinfo_detail():
404 404 flush_channels()
405 405
406 406 msg_id, reply = execute(code='ip=get_ipython()')
407 407
408 408 msg_id = KC.object_info('ip.object_inspect', detail_level=2)
409 409 reply = KC.get_shell_msg(timeout=2)
410 410 for tst in validate_message(reply, 'object_info_reply', msg_id):
411 411 yield tst
412 412 content = reply['content']
413 413 yield nt.assert_true(content['found'])
414 414 argspec = content['argspec']
415 415 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
416 416 yield nt.assert_equal(argspec['defaults'], [0])
417 417
418 418
419 419 @dec.parametric
420 420 def test_oinfo_not_found():
421 421 flush_channels()
422 422
423 423 msg_id = KC.object_info('dne')
424 424 reply = KC.get_shell_msg(timeout=2)
425 425 for tst in validate_message(reply, 'object_info_reply', msg_id):
426 426 yield tst
427 427 content = reply['content']
428 428 yield nt.assert_false(content['found'])
429 429
430 430
431 431 @dec.parametric
432 432 def test_complete():
433 433 flush_channels()
434 434
435 435 msg_id, reply = execute(code="alpha = albert = 5")
436 436
437 437 msg_id = KC.complete('al', 'al', 2)
438 438 reply = KC.get_shell_msg(timeout=2)
439 439 for tst in validate_message(reply, 'complete_reply', msg_id):
440 440 yield tst
441 441 matches = reply['content']['matches']
442 442 for name in ('alpha', 'albert'):
443 443 yield nt.assert_true(name in matches, "Missing match: %r" % name)
444 444
445 445
446 446 @dec.parametric
447 447 def test_kernel_info_request():
448 448 flush_channels()
449 449
450 450 msg_id = KC.kernel_info()
451 451 reply = KC.get_shell_msg(timeout=2)
452 452 for tst in validate_message(reply, 'kernel_info_reply', msg_id):
453 453 yield tst
454 454
455 455
456 456 # IOPub channel
457 457
458 458
459 459 @dec.parametric
460 460 def test_stream():
461 461 flush_channels()
462 462
463 463 msg_id, reply = execute("print('hi')")
464 464
465 465 stdout = KC.iopub_channel.get_msg(timeout=2)
466 466 for tst in validate_message(stdout, 'stream', msg_id):
467 467 yield tst
468 468 content = stdout['content']
469 469 yield nt.assert_equal(content['name'], u'stdout')
470 470 yield nt.assert_equal(content['data'], u'hi\n')
471 471
472 472
473 473 @dec.parametric
474 474 def test_display_data():
475 475 flush_channels()
476 476
477 477 msg_id, reply = execute("from IPython.core.display import display; display(1)")
478 478
479 479 display = KC.iopub_channel.get_msg(timeout=2)
480 480 for tst in validate_message(display, 'display_data', parent=msg_id):
481 481 yield tst
482 482 data = display['content']['data']
483 483 yield nt.assert_equal(data['text/plain'], u'1')
484 484
General Comments 0
You need to be logged in to leave comments. Login now