~didrocks/ubuntuone-client/dont-suffer-zg-crash

« back to all changes in this revision

Viewing changes to tests/syncdaemon/test_eventqueue.py

  • Committer: Bazaar Package Importer
  • Author(s): Rodney Dawes
  • Date: 2009-06-30 12:00:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090630120000-by806ovmw3193qe8
Tags: upstream-0.90.3
ImportĀ upstreamĀ versionĀ 0.90.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Author: Facundo Batista <facundo@canonical.com>
 
3
#
 
4
# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
 
5
#
 
6
# Copyright 2009 Canonical Ltd.
 
7
#
 
8
# This program is free software: you can redistribute it and/or modify it
 
9
# under the terms of the GNU General Public License version 3, as published
 
10
# by the Free Software Foundation.
 
11
#
 
12
# This program is distributed in the hope that it will be useful, but
 
13
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
14
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
15
# PURPOSE.  See the GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License along
 
18
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
 
 
20
'''Tests for the Event Queue.'''
 
21
 
 
22
import logging
 
23
import unittest
 
24
 
 
25
from ubuntuone.syncdaemon import (
 
26
    event_queue,
 
27
    filesystem_manager,
 
28
)
 
29
from contrib.testing import testcase
 
30
from twisted.internet import defer
 
31
 
 
32
 
 
33
class BaseEQTestCase(testcase.BaseTwistedTestCase):
 
34
    ''' Setup an EQ for test. '''
 
35
 
 
36
    def setUp(self):
 
37
        '''Setup the test.'''
 
38
        testcase.BaseTwistedTestCase.setUp(self)
 
39
        self.fsmdir = self.mktemp('fsmdir')
 
40
        self.root_dir = self.mktemp('root_dir')
 
41
        self.fs = filesystem_manager.FileSystemManager(self.fsmdir,
 
42
                                   testcase.FakeVolumeManager(self.root_dir))
 
43
        mdobj = self.fs.create(path=self.root_dir,
 
44
                                 share_id='', is_dir=True)
 
45
        self.fs.set_by_path(path=self.root_dir,
 
46
                              local_hash=None, server_hash=None)
 
47
        self.eq = event_queue.EventQueue(self.fs)
 
48
 
 
49
    def tearDown(self):
 
50
        '''Clean up the tests.'''
 
51
        testcase.BaseTwistedTestCase.tearDown(self)
 
52
        self.eq.shutdown()
 
53
 
 
54
 
 
55
class SubscriptionTests(BaseEQTestCase):
 
56
    '''Test to subscribe and unsubscribe to the EQ.'''
 
57
 
 
58
    def test_subscription(self):
 
59
        '''Test that subscription works.'''
 
60
        # simple one
 
61
        o = object()
 
62
        self.eq.subscribe(o)
 
63
        self.assertTrue(o in self.eq._listeners)
 
64
 
 
65
        # not duplicates
 
66
        o = object()
 
67
        self.eq.subscribe(o)
 
68
        self.eq.subscribe(o)
 
69
        self.assertTrue(self.eq._listeners.count(o) == 1)
 
70
 
 
71
    def test_unsubscription(self):
 
72
        '''Test that unsubscription works.'''
 
73
        # simple one
 
74
        o = object()
 
75
        self.eq.subscribe(o)
 
76
        self.eq.unsubscribe(o)
 
77
        self.assertFalse(o in self.eq._listeners)
 
78
 
 
79
        # duplicate ok (two suscs)
 
80
        o = object()
 
81
        self.eq.subscribe(o)
 
82
        self.eq.subscribe(o)
 
83
        self.eq.unsubscribe(o)
 
84
        self.assertFalse(o in self.eq._listeners)
 
85
 
 
86
        # duplicate error (two unsuscs)
 
87
        o = object()
 
88
        self.eq.subscribe(o)
 
89
        self.eq.subscribe(o)
 
90
        self.eq.unsubscribe(o)
 
91
        self.assertRaises(ValueError, self.eq.unsubscribe, o)
 
92
 
 
93
        # indecisive
 
94
        o = object()
 
95
        self.eq.subscribe(o)
 
96
        self.eq.unsubscribe(o)
 
97
        self.eq.subscribe(o)
 
98
        self.eq.unsubscribe(o)
 
99
        self.assertFalse(o in self.eq._listeners)
 
100
 
 
101
 
 
102
class PushTests(BaseEQTestCase):
 
103
    '''Test the event distribution machinery.'''
 
104
 
 
105
    def test_push_simple(self):
 
106
        '''Test that events can be pushed (not listening yet).'''
 
107
        # not even an event
 
108
        self.assertRaises(TypeError, self.eq.push)
 
109
 
 
110
        # bad event
 
111
        self.assertRaises(event_queue.InvalidEventError,
 
112
                          self.eq.push, "no-such-event")
 
113
 
 
114
        # not enough or incorrect args for that event
 
115
        self.assertRaises(TypeError, self.eq.push, "FS_FILE_MOVE")
 
116
        self.assertRaises(TypeError, self.eq.push, "FS_FILE_MOVE", 1, 2, 3)
 
117
        self.assertRaises(TypeError,
 
118
                      self.eq.push, "FS_FILE_MOVE", 1, 2, path_to=3)
 
119
        self.assertRaises(TypeError,
 
120
                      self.eq.push, "FS_FILE_MOVE", 1, path_from=2, path_to=3)
 
121
 
 
122
        # all ok... args, kwargs, and mixed
 
123
        self.eq.push("FS_FILE_MOVE", 1, 2)
 
124
        self.eq.push("FS_FILE_MOVE", path_from=1, path_to=2)
 
125
        self.eq.push("FS_FILE_MOVE", 1, path_to=2)
 
126
 
 
127
    def test_listened_pushs(self):
 
128
        '''Push events and listem them.'''
 
129
 
 
130
        # helper class, pylint: disable-msg=C0111
 
131
        class C(object):
 
132
            def __init__(self):
 
133
                self.a = None
 
134
            def handle_FS_FILE_CREATE(self, a):
 
135
                self.a = a
 
136
 
 
137
        # it get passed!
 
138
        c = C()
 
139
        self.eq.subscribe(c)
 
140
        self.eq.push("FS_FILE_CREATE", 1)
 
141
        self.assertEqual(c.a, 1)
 
142
        self.eq.unsubscribe(c)
 
143
 
 
144
        # don't get what don't listen
 
145
        c = C()
 
146
        self.eq.subscribe(c)
 
147
        self.eq.push("FS_FILE_DELETE", 1)
 
148
        self.assertEqual(c.a, None)
 
149
        self.eq.unsubscribe(c)
 
150
 
 
151
    def test_signatures(self):
 
152
        '''Check that the handle signatures are forced when passing.'''
 
153
 
 
154
        # helper class, pylint: disable-msg=C0111
 
155
        class C(object):
 
156
            def handle_FS_FILE_CREATE(self, a, b): # it should be only 'a' here
 
157
                pass
 
158
 
 
159
        # it get passed!
 
160
        c = C()
 
161
        self.eq.subscribe(c)
 
162
 
 
163
        # caught by EQ
 
164
        self.assertRaises(TypeError, self.eq.push, "FS_FILE_CREATE")
 
165
        self.assertRaises(TypeError, self.eq.push, "FS_FILE_CREATE", 1, 2)
 
166
 
 
167
        # approved by EQ, but the listener has a wrong signature
 
168
        # this is logged as an error/exception, so we hook to the logger
 
169
        handler = testcase.MementoHandler()
 
170
        handler.setLevel(logging.ERROR)
 
171
        self.eq.log.addHandler(handler)
 
172
 
 
173
        self.eq.push("FS_FILE_CREATE", 1)
 
174
        self.assertEquals(1, len(handler.records))
 
175
        self.assertEquals('FS_FILE_CREATE', handler.records[0].args[0])
 
176
        self.assertEquals(C, type(handler.records[0].args[1]))
 
177
 
 
178
        self.eq.log.removeHandler(handler)
 
179
        self.eq.unsubscribe(c)
 
180
 
 
181
 
 
182
class PushTestsWithCallback(BaseEQTestCase):
 
183
    '''Test the error handling in the event distribution machinery.'''
 
184
 
 
185
    def test_keep_going(self):
 
186
        ''' Check that if a listener raises an Exception or have a
 
187
        wrong signature, the next listeners are called.
 
188
        '''
 
189
        d = defer.Deferred()
 
190
        # helper class, pylint: disable-msg=C0111
 
191
        class BadListener(object):
 
192
            def handle_FS_FILE_CREATE(self, a, b): # it should be only 'a' here
 
193
                d.callback(False)
 
194
 
 
195
        class GoodListener(object):
 
196
            def handle_FS_FILE_CREATE(self, a):
 
197
                d.callback(a)
 
198
 
 
199
        bl = BadListener()
 
200
        gl = GoodListener()
 
201
        self.eq.subscribe(bl)
 
202
        self.eq.subscribe(gl)
 
203
 
 
204
        def cleanup():
 
205
            """ unsubscribe the listeners """
 
206
            self.eq.unsubscribe(bl)
 
207
            self.eq.unsubscribe(gl)
 
208
        self.addCleanup(cleanup)
 
209
 
 
210
        # caught by EQ
 
211
        self.assertRaises(TypeError, self.eq.push, "FS_FILE_CREATE")
 
212
        self.assertRaises(TypeError, self.eq.push, "FS_FILE_CREATE", 1, 2)
 
213
 
 
214
        # approved by EQ, but one listener has a wrong signature
 
215
        self.eq.push("FS_FILE_CREATE", 1)
 
216
        def callback(result):
 
217
            """ asserts that GoodListener was called. """
 
218
            self.assertTrue(result)
 
219
            self.assertEquals(1, result)
 
220
 
 
221
        d.addCallback(callback)
 
222
        return d
 
223
 
 
224
    def test_default_handler(self):
 
225
        ''' Check that handler_default is called. '''
 
226
        d = defer.Deferred()
 
227
        # helper class, pylint: disable-msg=C0111
 
228
        class Listener(object):
 
229
            def handle_default(self, *args, **kwargs):
 
230
                d.callback(args)
 
231
 
 
232
        l = Listener()
 
233
        self.eq.subscribe(l)
 
234
 
 
235
        def cleanup():
 
236
            """ unsubscribe the listeners """
 
237
            self.eq.unsubscribe(l)
 
238
        self.addCleanup(cleanup)
 
239
 
 
240
        # push some event and expect it'll be handled by handle_default
 
241
        self.eq.push("FS_FILE_CREATE", 1)
 
242
        def callback(result):
 
243
            """ asserts that GoodListener was called. """
 
244
            self.assertEquals(2, len(result))
 
245
            self.assertEquals('FS_FILE_CREATE', result[0])
 
246
            self.assertEquals(1, result[1])
 
247
 
 
248
        d.addCallback(callback)
 
249
        return d
 
250
 
 
251
    def test_ordered_dispatch(self):
 
252
        """ Check that the events are pushed to all listeners in order. """
 
253
        d = defer.Deferred()
 
254
        # helper class, pylint: disable-msg=C0111
 
255
        class Listener(object):
 
256
            def __init__(self, eq):
 
257
                self.eq = eq
 
258
                self.events = []
 
259
 
 
260
            def handle_FS_FILE_CREATE(self, a):
 
261
                self.events.append('FS_FILE_CREATE')
 
262
                self.eq.push('FS_FILE_MOVE', a, 2)
 
263
 
 
264
            def handle_FS_FILE_DELETE(self, a):
 
265
                self.events.append('FS_FILE_DELETE')
 
266
 
 
267
            def handle_FS_FILE_MOVE(self, *args):
 
268
                self.events.append('FS_FILE_MOVE')
 
269
                d.callback(True)
 
270
 
 
271
        # create 10 listeners in order to create an event madness
 
272
        listeners = []
 
273
        for i in xrange(0, 10):
 
274
            l = Listener(self.eq)
 
275
            listeners.append(l)
 
276
            self.eq.subscribe(l)
 
277
 
 
278
        def cleanup():
 
279
            """ unsubscribe the listeners """
 
280
            for l in listeners:
 
281
                self.eq.unsubscribe(l)
 
282
        self.addCleanup(cleanup)
 
283
 
 
284
        # push some events to unleash the event madness
 
285
        self.eq.push("FS_FILE_CREATE", 1)
 
286
        self.eq.push('FS_FILE_DELETE', 2)
 
287
 
 
288
        def callback(result):
 
289
            """ asserts that Listener was called in the right order"""
 
290
            listeners_events = [listener.events for listener in listeners]
 
291
            for l_events in listeners_events:
 
292
                for other_l_events in listeners_events:
 
293
                    self.assertEqual(l_events, other_l_events)
 
294
 
 
295
        d.addCallback(callback)
 
296
        return d
 
297
 
 
298
 
 
299
def test_suite():
 
300
    # pylint: disable-msg=C0111
 
301
    return unittest.TestLoader().loadTestsFromName(__name__)
 
302
 
 
303
if __name__ == "__main__":
 
304
    unittest.main()