##// END OF EJS Templates
added history tests
Narahari -
Show More
@@ -1,443 +1,456 b''
1 1 """Test suite for our zeromq-based message specification."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import re
7 7 import sys
8 8 from distutils.version import LooseVersion as V
9 9 try:
10 10 from queue import Empty # Py 3
11 11 except ImportError:
12 12 from Queue import Empty # Py 2
13 13
14 14 import nose.tools as nt
15 15
16 16 from IPython.utils.traitlets import (
17 17 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
18 18 )
19 19 from IPython.utils.py3compat import string_types, iteritems
20 20
21 21 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Globals
25 25 #-----------------------------------------------------------------------------
26 26 KC = None
27 27
28 28 def setup():
29 29 global KC
30 30 KC = start_global_kernel()
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Message Spec References
34 34 #-----------------------------------------------------------------------------
35 35
36 36 class Reference(HasTraits):
37 37
38 38 """
39 39 Base class for message spec specification testing.
40 40
41 41 This class is the core of the message specification test. The
42 42 idea is that child classes implement trait attributes for each
43 43 message keys, so that message keys can be tested against these
44 44 traits using :meth:`check` method.
45 45
46 46 """
47 47
48 48 def check(self, d):
49 49 """validate a dict against our traits"""
50 50 for key in self.trait_names():
51 51 nt.assert_in(key, d)
52 52 # FIXME: always allow None, probably not a good idea
53 53 if d[key] is None:
54 54 continue
55 55 try:
56 56 setattr(self, key, d[key])
57 57 except TraitError as e:
58 58 assert False, str(e)
59 59
60 60
61 61 class Version(Unicode):
62 62 def __init__(self, *args, **kwargs):
63 63 self.min = kwargs.pop('min', None)
64 64 self.max = kwargs.pop('max', None)
65 65 kwargs['default_value'] = self.min
66 66 super(Version, self).__init__(*args, **kwargs)
67 67
68 68 def validate(self, obj, value):
69 69 if self.min and V(value) < V(self.min):
70 70 raise TraitError("bad version: %s < %s" % (value, self.min))
71 71 if self.max and (V(value) > V(self.max)):
72 72 raise TraitError("bad version: %s > %s" % (value, self.max))
73 73
74 74
75 75 class RMessage(Reference):
76 76 msg_id = Unicode()
77 77 msg_type = Unicode()
78 78 header = Dict()
79 79 parent_header = Dict()
80 80 content = Dict()
81 81
82 82 def check(self, d):
83 83 super(RMessage, self).check(d)
84 84 RHeader().check(self.header)
85 85 if self.parent_header:
86 86 RHeader().check(self.parent_header)
87 87
88 88 class RHeader(Reference):
89 89 msg_id = Unicode()
90 90 msg_type = Unicode()
91 91 session = Unicode()
92 92 username = Unicode()
93 93 version = Version(min='5.0')
94 94
95 95 mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$')
96 96
97 97 class MimeBundle(Reference):
98 98 metadata = Dict()
99 99 data = Dict()
100 100 def _data_changed(self, name, old, new):
101 101 for k,v in iteritems(new):
102 102 assert mime_pat.match(k)
103 103 nt.assert_is_instance(v, string_types)
104 104
105 105 # shell replies
106 106
107 107 class ExecuteReply(Reference):
108 108 execution_count = Integer()
109 109 status = Enum((u'ok', u'error'))
110 110
111 111 def check(self, d):
112 112 Reference.check(self, d)
113 113 if d['status'] == 'ok':
114 114 ExecuteReplyOkay().check(d)
115 115 elif d['status'] == 'error':
116 116 ExecuteReplyError().check(d)
117 117
118 118
119 119 class ExecuteReplyOkay(Reference):
120 120 payload = List(Dict)
121 121 user_expressions = Dict()
122 122
123 123
124 124 class ExecuteReplyError(Reference):
125 125 ename = Unicode()
126 126 evalue = Unicode()
127 127 traceback = List(Unicode)
128 128
129 129
130 130 class InspectReply(MimeBundle):
131 131 found = Bool()
132 132
133 133
134 134 class ArgSpec(Reference):
135 135 args = List(Unicode)
136 136 varargs = Unicode()
137 137 varkw = Unicode()
138 138 defaults = List()
139 139
140 140
141 141 class Status(Reference):
142 142 execution_state = Enum((u'busy', u'idle', u'starting'))
143 143
144 144
145 145 class CompleteReply(Reference):
146 146 matches = List(Unicode)
147 147 cursor_start = Integer()
148 148 cursor_end = Integer()
149 149 status = Unicode()
150 150
151 151 class LanguageInfo(Reference):
152 152 name = Unicode('python')
153 153 version = Unicode(sys.version.split()[0])
154 154
155 155 class KernelInfoReply(Reference):
156 156 protocol_version = Version(min='5.0')
157 157 implementation = Unicode('ipython')
158 158 implementation_version = Version(min='2.1')
159 159 language_info = Dict()
160 160 banner = Unicode()
161 161
162 162 def check(self, d):
163 163 Reference.check(self, d)
164 164 LanguageInfo().check(d['language_info'])
165 165
166 166
167 167 class IsCompleteReply(Reference):
168 168 status = Enum((u'complete', u'incomplete', u'invalid', u'unknown'))
169 169
170 170 def check(self, d):
171 171 Reference.check(self, d)
172 172 if d['status'] == 'incomplete':
173 173 IsCompleteReplyIncomplete().check(d)
174 174
175 175 class IsCompleteReplyIncomplete(Reference):
176 176 indent = Unicode()
177 177
178 178
179 179 # IOPub messages
180 180
181 181 class ExecuteInput(Reference):
182 182 code = Unicode()
183 183 execution_count = Integer()
184 184
185 185
186 186 Error = ExecuteReplyError
187 187
188 188
189 189 class Stream(Reference):
190 190 name = Enum((u'stdout', u'stderr'))
191 191 text = Unicode()
192 192
193 193
194 194 class DisplayData(MimeBundle):
195 195 pass
196 196
197 197
198 198 class ExecuteResult(MimeBundle):
199 199 execution_count = Integer()
200 200
201 class HistoryReply(MimeBundle):
201 class HistoryReply(Reference):
202 202 history = List(Unicode)
203 203
204 204
205 205 references = {
206 206 'execute_reply' : ExecuteReply(),
207 207 'inspect_reply' : InspectReply(),
208 208 'status' : Status(),
209 209 'complete_reply' : CompleteReply(),
210 210 'kernel_info_reply': KernelInfoReply(),
211 211 'is_complete_reply': IsCompleteReply(),
212 212 'execute_input' : ExecuteInput(),
213 213 'execute_result' : ExecuteResult(),
214 214 'history_reply' : HistoryReply(),
215 215 'error' : Error(),
216 216 'stream' : Stream(),
217 217 'display_data' : DisplayData(),
218 218 'header' : RHeader(),
219 219 }
220 220 """
221 221 Specifications of `content` part of the reply messages.
222 222 """
223 223
224 224
225 225 def validate_message(msg, msg_type=None, parent=None):
226 226 """validate a message
227 227
228 228 This is a generator, and must be iterated through to actually
229 229 trigger each test.
230 230
231 231 If msg_type and/or parent are given, the msg_type and/or parent msg_id
232 232 are compared with the given values.
233 233 """
234 234 RMessage().check(msg)
235 235 if msg_type:
236 236 nt.assert_equal(msg['msg_type'], msg_type)
237 237 if parent:
238 238 nt.assert_equal(msg['parent_header']['msg_id'], parent)
239 239 content = msg['content']
240 240 ref = references[msg['msg_type']]
241 241 ref.check(content)
242 242
243 243
244 244 #-----------------------------------------------------------------------------
245 245 # Tests
246 246 #-----------------------------------------------------------------------------
247 247
248 248 # Shell channel
249 249
250 250 def test_execute():
251 251 flush_channels()
252 252
253 253 msg_id = KC.execute(code='x=1')
254 254 reply = KC.get_shell_msg(timeout=TIMEOUT)
255 255 validate_message(reply, 'execute_reply', msg_id)
256 256
257 257
258 258 def test_execute_silent():
259 259 flush_channels()
260 260 msg_id, reply = execute(code='x=1', silent=True)
261 261
262 262 # flush status=idle
263 263 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
264 264 validate_message(status, 'status', msg_id)
265 265 nt.assert_equal(status['content']['execution_state'], 'idle')
266 266
267 267 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
268 268 count = reply['execution_count']
269 269
270 270 msg_id, reply = execute(code='x=2', silent=True)
271 271
272 272 # flush status=idle
273 273 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
274 274 validate_message(status, 'status', msg_id)
275 275 nt.assert_equal(status['content']['execution_state'], 'idle')
276 276
277 277 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
278 278 count_2 = reply['execution_count']
279 279 nt.assert_equal(count_2, count)
280 280
281 281
282 282 def test_execute_error():
283 283 flush_channels()
284 284
285 285 msg_id, reply = execute(code='1/0')
286 286 nt.assert_equal(reply['status'], 'error')
287 287 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
288 288
289 289 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
290 290 validate_message(error, 'error', msg_id)
291 291
292 292
293 293 def test_execute_inc():
294 294 """execute request should increment execution_count"""
295 295 flush_channels()
296 296
297 297 msg_id, reply = execute(code='x=1')
298 298 count = reply['execution_count']
299 299
300 300 flush_channels()
301 301
302 302 msg_id, reply = execute(code='x=2')
303 303 count_2 = reply['execution_count']
304 304 nt.assert_equal(count_2, count+1)
305 305
306 306
307 307 def test_user_expressions():
308 308 flush_channels()
309 309
310 310 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
311 311 user_expressions = reply['user_expressions']
312 312 nt.assert_equal(user_expressions, {u'foo': {
313 313 u'status': u'ok',
314 314 u'data': {u'text/plain': u'2'},
315 315 u'metadata': {},
316 316 }})
317 317
318 318
319 319 def test_user_expressions_fail():
320 320 flush_channels()
321 321
322 322 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
323 323 user_expressions = reply['user_expressions']
324 324 foo = user_expressions['foo']
325 325 nt.assert_equal(foo['status'], 'error')
326 326 nt.assert_equal(foo['ename'], 'NameError')
327 327
328 328
329 329 def test_oinfo():
330 330 flush_channels()
331 331
332 332 msg_id = KC.inspect('a')
333 333 reply = KC.get_shell_msg(timeout=TIMEOUT)
334 334 validate_message(reply, 'inspect_reply', msg_id)
335 335
336 336
337 337 def test_oinfo_found():
338 338 flush_channels()
339 339
340 340 msg_id, reply = execute(code='a=5')
341 341
342 342 msg_id = KC.inspect('a')
343 343 reply = KC.get_shell_msg(timeout=TIMEOUT)
344 344 validate_message(reply, 'inspect_reply', msg_id)
345 345 content = reply['content']
346 346 assert content['found']
347 347 text = content['data']['text/plain']
348 348 nt.assert_in('Type:', text)
349 349 nt.assert_in('Docstring:', text)
350 350
351 351
352 352 def test_oinfo_detail():
353 353 flush_channels()
354 354
355 355 msg_id, reply = execute(code='ip=get_ipython()')
356 356
357 357 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
358 358 reply = KC.get_shell_msg(timeout=TIMEOUT)
359 359 validate_message(reply, 'inspect_reply', msg_id)
360 360 content = reply['content']
361 361 assert content['found']
362 362 text = content['data']['text/plain']
363 363 nt.assert_in('Definition:', text)
364 364 nt.assert_in('Source:', text)
365 365
366 366
367 367 def test_oinfo_not_found():
368 368 flush_channels()
369 369
370 370 msg_id = KC.inspect('dne')
371 371 reply = KC.get_shell_msg(timeout=TIMEOUT)
372 372 validate_message(reply, 'inspect_reply', msg_id)
373 373 content = reply['content']
374 374 nt.assert_false(content['found'])
375 375
376 376
377 377 def test_complete():
378 378 flush_channels()
379 379
380 380 msg_id, reply = execute(code="alpha = albert = 5")
381 381
382 382 msg_id = KC.complete('al', 2)
383 383 reply = KC.get_shell_msg(timeout=TIMEOUT)
384 384 validate_message(reply, 'complete_reply', msg_id)
385 385 matches = reply['content']['matches']
386 386 for name in ('alpha', 'albert'):
387 387 nt.assert_in(name, matches)
388 388
389 389
390 390 def test_kernel_info_request():
391 391 flush_channels()
392 392
393 393 msg_id = KC.kernel_info()
394 394 reply = KC.get_shell_msg(timeout=TIMEOUT)
395 395 validate_message(reply, 'kernel_info_reply', msg_id)
396 396
397 397
398 398 def test_single_payload():
399 399 flush_channels()
400 400 msg_id, reply = execute(code="for i in range(3):\n"+
401 401 " x=range?\n")
402 402 payload = reply['payload']
403 403 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
404 404 nt.assert_equal(len(next_input_pls), 1)
405 405
406 406 def test_is_complete():
407 407 flush_channels()
408 a
409 408 msg_id = KC.is_complete("a = 1")
410 409 reply = KC.get_shell_msg(timeout=TIMEOUT)
411 410 validate_message(reply, 'is_complete_reply', msg_id)
412 411
413 def test_history():
412 def test_history_range():
414 413 flush_channels()
415 414
416 415 msg_id = KC.history("range", True, True)
417 416 reply = KC.get_shell_msg(timeout=TIMEOUT)
418 417 validate_message(reply, 'history_reply', msg_id)
419 418
419 def test_history_tail():
420 flush_channels()
421
422 msg_id = KC.history("tail", True, True, n = 1)
423 reply = KC.get_shell_msg(timeout=TIMEOUT)
424 validate_message(reply, 'history_reply', msg_id)
425
426 def test_history_search():
427 flush_channels()
428
429 msg_id = KC.history("search", True, True, n = 1, pattern = "*")
430 reply = KC.get_shell_msg(timeout=TIMEOUT)
431 validate_message(reply, 'history_reply', msg_id)
432
420 433 # IOPub channel
421 434
422 435
423 436 def test_stream():
424 437 flush_channels()
425 438
426 439 msg_id, reply = execute("print('hi')")
427 440
428 441 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
429 442 validate_message(stdout, 'stream', msg_id)
430 443 content = stdout['content']
431 444 nt.assert_equal(content['text'], u'hi\n')
432 445
433 446
434 447 def test_display_data():
435 448 flush_channels()
436 449
437 450 msg_id, reply = execute("from IPython.core.display import display; display(1)")
438 451
439 452 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
440 453 validate_message(display, 'display_data', parent=msg_id)
441 454 data = display['content']['data']
442 455 nt.assert_equal(data['text/plain'], u'1')
443 456
General Comments 0
You need to be logged in to leave comments. Login now