1
# Copyright (c) 2010, Jeremy Thurgood
2
# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
3
from datetime import datetime, timedelta
5
from twisted.trial import unittest
6
from twisted.internet import defer, reactor
9
from ibid import core, event
12
def _defer_cb(dfr, *args, **kw):
13
"Call in the future to allow thread stuff to happen."
14
reactor.callLater(0.01, dfr.callback, *args, **kw)
17
class TestProcessor(object):
19
A processor object stub.
21
name = 'testprocessor'
23
def __init__(self, proc_func):
24
self.proc_func = proc_func
26
def process(self, event):
30
class TestSource(object):
37
def send(self, response):
38
self._msgs.append(response)
41
class TestDispatcher(unittest.TestCase):
43
Test the Dispatcher class.
47
ibid.processors[:] = []
49
self.dispatcher = core.Dispatcher()
52
ibid.processors[:] = []
55
def _add_processor(self, proc_func):
56
"Add a processor to the dispatch chain."
57
ibid.processors.append(TestProcessor(proc_func))
59
def _ev(self, source='fakesource', type='testmessage'):
60
"Create an event with some default values."
61
return event.Event(source, type)
63
def _defer_assertions(self, callback, result):
64
"Create a deferred to assert things that only happen later."
65
dfr = defer.Deferred()
66
dfr.addCallback(callback, self)
67
_defer_cb(dfr, result)
70
def _dispatch_and_assert(self, callback, ev):
72
Dispatch an event and add an assertion callback to the
75
dfr = self.dispatcher.dispatch(ev)
76
dfr.addCallback(callback, self)
79
def test_process_no_processors(self):
80
"With no processors, an event is unmodified."
82
pev = self.dispatcher._process(ev)
83
self.assertEqual(ev, pev)
84
self.assertEqual([], pev.responses)
86
def test_dispatch_no_processors(self):
87
"With no processors, an event is unmodified."
90
_self.assertEqual(ev, _ev)
91
_self.assertEqual([], _ev.responses)
92
return self._dispatch_and_assert(_cb, ev)
94
def test_process_noop_processor(self):
95
"A passive processor is called, but does not modify."
100
self._add_processor(prc)
101
pev = self.dispatcher._process(ev)
102
self.assertEqual(ev, pev)
103
self.assertEqual([], pev.responses)
104
self.assertEqual([1], procs)
105
self.assertEqual(False, pev.processed)
107
def test_dispatch_noop_processor(self):
108
"A passive processor is called, but does not modify the event."
113
self._add_processor(prc)
115
_self.assertEqual(ev, _ev)
116
_self.assertEqual([], _ev.responses)
117
_self.assertEqual([1], procs)
118
_self.assertEqual(False, _ev.processed)
119
return self._dispatch_and_assert(_cb, ev)
121
def test_process_simple_reply(self):
122
"A processor can add a reply."
125
e.addresponse(u'foo')
126
self._add_processor(prc)
127
pev = self.dispatcher._process(ev)
128
self.assertEqual(ev, pev)
129
self.assertTrue('complain' not in pev)
130
self.assertEqual([{'reply': u'foo',
132
'source': 'fakesource',
134
'conflate': True}], pev.responses)
135
self.assertEqual(True, pev.processed)
137
def test_dispatch_simple_reply(self):
138
"A processor can add a reply."
141
e.addresponse(u'foo')
142
self._add_processor(prc)
144
_self.assertEqual(ev, _ev)
145
_self.assertTrue('complain' not in _ev)
146
_self.assertEqual([{'reply': u'foo',
148
'source': 'fakesource',
150
'conflate': True}], _ev.responses)
151
_self.assertEqual(True, _ev.processed)
152
return self._dispatch_and_assert(_cb, ev)
154
def test_process_broken_processor(self):
155
"If a processor dies, we complain and carry on."
159
self._add_processor(prc)
160
pev = self.dispatcher._process(ev)
161
self.assertEqual(ev, pev)
162
self.assertEqual('exception', pev.complain)
163
self.assertEqual([], pev.responses)
164
self.assertEqual(True, pev.processed)
166
def test_dispatch_broken_processor(self):
167
"If a processor dies, we complain and carry on."
171
self._add_processor(prc)
173
_self.assertEqual(ev, _ev)
174
_self.assertEqual('exception', _ev.complain)
175
_self.assertEqual([], _ev.responses)
176
_self.assertEqual(True, _ev.processed)
177
return self._dispatch_and_assert(_cb, ev)
179
def test_process_double_reply(self):
180
"We can add multiple replies to an event."
183
e.addresponse(u'foo')
184
e.addresponse(u'bar')
185
self._add_processor(prc)
186
pev = self.dispatcher._process(ev)
187
self.assertEqual(ev, pev)
188
self.assertTrue('complain' not in pev)
189
self.assertEqual([{'reply': u'foo',
191
'source': 'fakesource',
196
'source': 'fakesource',
198
'conflate': True}], pev.responses)
199
self.assertEqual(True, pev.processed)
201
def test_dispatch_double_reply(self):
202
"We can add multiple replies to an event."
205
e.addresponse(u'foo')
206
e.addresponse(u'bar')
207
self._add_processor(prc)
209
_self.assertEqual(ev, _ev)
210
_self.assertTrue('complain' not in _ev)
211
_self.assertEqual([{'reply': u'foo',
213
'source': 'fakesource',
218
'source': 'fakesource',
220
'conflate': True}], _ev.responses)
221
_self.assertEqual(True, _ev.processed)
222
return self._dispatch_and_assert(_cb, ev)
224
def test_process_reply_send_invalid_source(self):
225
"Messages to invalid sources get silently swallowed."
228
e.addresponse(u'foo')
229
e.addresponse(u'bar', source='testsource')
230
self._add_processor(prc)
231
pev = self.dispatcher._process(ev)
232
self.assertEqual(ev, pev)
233
self.assertTrue('complain' not in pev)
234
self.assertEqual([{'reply': u'foo',
236
'source': 'fakesource',
238
'conflate': True}], pev.responses)
239
self.assertEqual(True, pev.processed)
241
def test_dispatch_reply_send_invalid_source(self):
242
"Messages to invalid sources get silently swallowed."
245
e.addresponse(u'foo')
246
e.addresponse(u'bar', source='testsource')
247
self._add_processor(prc)
249
_self.assertEqual(ev, _ev)
250
_self.assertTrue('complain' not in _ev)
251
_self.assertEqual([{'reply': u'foo',
253
'source': 'fakesource',
255
'conflate': True}], _ev.responses)
256
self.assertEqual(True, _ev.processed)
257
return self._dispatch_and_assert(_cb, ev)
259
def test_process_reply_send_valid_source(self):
260
"Messages to other sources get sent."
262
ibid.sources['testsource'] = src
265
e.addresponse(u'foo')
266
e.addresponse(u'bar', source='testsource')
267
self._add_processor(prc)
268
pev = self.dispatcher._process(ev)
269
self.assertEqual(ev, pev)
270
self.assertTrue('complain' not in pev)
271
self.assertEqual([{'reply': u'foo',
273
'source': 'fakesource',
275
'conflate': True}], pev.responses)
276
self.assertEqual(True, pev.processed)
277
def _cb(_src, _self):
278
_self.assertEqual([{'reply': u'bar',
280
'source': 'testsource',
282
'conflate': True}], _src._msgs)
283
return self._defer_assertions(_cb, src)
285
def test_dispatch_reply_send_valid_source(self):
286
"Messages to other sources get sent."
288
ibid.sources['testsource'] = src
291
e.addresponse(u'foo')
292
e.addresponse(u'bar', source='testsource')
293
self._add_processor(prc)
295
_self.assertEqual(ev, _ev)
296
_self.assertTrue('complain' not in _ev)
297
_self.assertEqual([{'reply': u'foo',
299
'source': 'fakesource',
301
'conflate': True}], _ev.responses)
302
_self.assertEqual(True, _ev.processed)
303
_self.assertEqual([{'reply': u'bar',
305
'source': 'testsource',
307
'conflate': True}], src._msgs)
308
return self._dispatch_and_assert(_cb, ev)
310
def test_call_later_no_args(self):
311
"Calling later calls stuff later."
315
dfr = defer.Deferred()
320
def _cb(_ev, _self, _oev):
321
_self.assertTrue(tm + timedelta(seconds=0.01) < datetime.now())
322
_self.assertTrue(_ev.did_stuff)
323
_self.assertFalse(hasattr(_oev, 'did_stuff'))
324
dfr.addCallback(_cb, self, ev)
325
self.dispatcher.call_later(0.01, _cl, ev)
328
def test_call_later_args(self):
329
"Calling later with args calls stuff later."
333
dfr = defer.Deferred()
338
def _cb(_ev, _self, _oev):
339
_self.assertTrue(tm + timedelta(seconds=0.01) < datetime.now())
340
_self.assertEqual('thingy', _ev.did_stuff)
341
_self.assertFalse(hasattr(_oev, 'did_stuff'))
342
dfr.addCallback(_cb, self, ev)
343
self.dispatcher.call_later(0.01, _cl, ev, 'thingy')
346
def test_call_later_kwargs(self):
347
"Calling later with kwargs calls stuff later."
351
dfr = defer.Deferred()
353
def _cl(_ev, val='default'):
356
def _cb(_ev, _self, _oev):
357
_self.assertTrue(tm + timedelta(seconds=0.01) < datetime.now())
358
_self.assertEqual('thingy', _ev.did_stuff)
359
_self.assertFalse(hasattr(_oev, 'did_stuff'))
360
dfr.addCallback(_cb, self, ev)
361
self.dispatcher.call_later(0.01, _cl, ev, val='thingy')
364
def test_call_later_reply(self):
365
"Calling later can send responses."
367
ibid.sources['testsource'] = src
371
dfr = defer.Deferred()
374
_ev.addresponse(u'This happens later.', source='testsource')
376
def _cb(_ev, _self, _src):
377
_self.assertTrue(tm + timedelta(seconds=0.01) < datetime.now())
378
_self.assertEqual([{'reply': u'This happens later.',
380
'source': 'testsource',
382
'conflate': True}], _src._msgs)
383
dfr.addCallback(_cb, self, src)
384
self.dispatcher.call_later(0.01, _cl, ev)
387
def test_call_later_multi_reply(self):
388
"Calling later can send many responses."
390
ibid.sources['fakesource'] = src
391
ibid.sources['testsource'] = src
395
dfr = defer.Deferred()
398
_ev.addresponse(u'This happens later.')
399
_ev.addresponse(u'So does this.', source='testsource')
401
def _cb(_ev, _self, _src):
402
_self.assertTrue(tm + timedelta(seconds=0.01) < datetime.now())
403
# Order is reversed because 'fakesource' waits until
404
# processing is finished.
405
_self.assertEqual([{'reply': u'So does this.',
407
'source': 'testsource',
410
{'reply': u'This happens later.',
412
'source': 'fakesource',
416
dfr.addCallback(_cb, self, src)
417
self.dispatcher.call_later(0.01, _cl, ev)
420
# vi: set et sta sw=4 ts=4: