summaryrefslogtreecommitdiff
path: root/src/leap/common/tests/test_events.py
blob: 0779b2eb104b0787732b5717d7de7e369ec604c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
## -*- coding: utf-8 -*-
# test_events.py
# Copyright (C) 2013 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import unittest
import sets
import time
import socket
import threading
import random


from mock import Mock
from protobuf.socketrpc import RpcService
from leap.common import events
from leap.common.events import (
    server,
    client,
    mac_auth,
)
from leap.common.events.events_pb2 import (
    EventsServerService,
    EventsServerService_Stub,
    EventsClientService_Stub,
    EventResponse,
    SignalRequest,
    RegisterRequest,
    PingRequest,
    SOLEDAD_CREATING_KEYS,
    CLIENT_UID,
)


port = 8090

received = False


class EventsTestCase(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        server.EventsServerDaemon.ensure(8090)
        cls.callbacks = events.client.registered_callbacks

    @classmethod
    def tearDownClass(cls):
        # give some time for requests to be processed.
        time.sleep(1)

    def setUp(self):
        super(EventsTestCase, self).setUp()

    def tearDown(self):
        #events.client.registered_callbacks = {}
        server.registered_clients = {}
        super(EventsTestCase, self).tearDown()

    def test_service_singleton(self):
        """
        Ensure that there's always just one instance of the server daemon
        running.
        """
        service1 = server.EventsServerDaemon.ensure(8090)
        service2 = server.EventsServerDaemon.ensure(8090)
        self.assertEqual(service1, service2,
                         "Can't get singleton class for service.")

    def test_client_register(self):
        """
        Ensure clients can register callbacks.
        """
        self.assertTrue(1 not in self.callbacks,
                        'There should should be no callback for this signal.')
        events.register(1, lambda x: True)
        self.assertTrue(1 in self.callbacks,
                        'Could not register signal in local client.')
        events.register(2, lambda x: True)
        self.assertTrue(1 in self.callbacks,
                        'Could not register signal in local client.')
        self.assertTrue(2 in self.callbacks,
                        'Could not register signal in local client.')

    def test_register_signal_replace(self):
        """
        Make sure clients can replace already registered callbacks.
        """
        sig = 3
        cbk = lambda x: True
        events.register(sig, cbk, uid=1)
        self.assertRaises(Exception, events.register, sig, lambda x: True,
                          uid=1)
        events.register(sig, lambda x: True, uid=1, replace=True)
        self.assertTrue(sig in self.callbacks, 'Could not register signal.')
        self.assertEqual(1, len(self.callbacks[sig]),
                         'Wrong number of registered callbacks.')

    def test_signal_response_status(self):
        """
        Ensure there's an appropriate response from server when signaling.
        """
        sig = 4
        request = SignalRequest()
        request.event = sig
        request.content = 'my signal contents'
        request.mac_method = mac_auth.MacMethod.MAC_NONE
        request.mac = ""
        service = RpcService(EventsServerService_Stub, port, 'localhost')
        # test synch
        response = service.signal(request, timeout=1000)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_signal_executes_callback(self):
        """
        Ensure callback is executed upon receiving signal.
        """
        sig = CLIENT_UID
        request = SignalRequest()
        request.event = sig
        request.content = 'my signal contents'
        request.mac_method = mac_auth.MacMethod.MAC_NONE
        request.mac = ""
        service = RpcService(EventsServerService_Stub, port, 'localhost')

        # register a callback
        flag = Mock()
        events.register(sig, lambda req: flag(req.event))
        # signal
        response = service.signal(request)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')
        time.sleep(1)  # wait for signal to arrive
        flag.assert_called_once_with(sig)

    def test_events_server_service_register(self):
        """
        Ensure the server can register clients to be signaled.
        """
        sig = 5
        request = RegisterRequest()
        request.event = sig
        request.port = 8091
        request.mac_method = mac_auth.MacMethod.MAC_NONE
        request.mac = ""
        service = RpcService(EventsServerService_Stub, port, 'localhost')
        complist = server.registered_clients
        self.assertEqual({}, complist,
                         'There should be no registered_ports when '
                         'server has just been created.')
        response = service.register(request, timeout=1000)
        self.assertTrue(sig in complist, "Signal not registered succesfully.")
        self.assertTrue(8091 in complist[sig],
                        'Failed registering client port.')

    def test_client_request_register(self):
        """
        Ensure clients can register themselves with server.
        """
        sig = 6
        complist = server.registered_clients
        self.assertTrue(sig not in complist,
                        'There should be no registered clients for this '
                        'signal.')
        events.register(sig, lambda x: True)
        time.sleep(0.1)
        port = client.EventsClientDaemon.get_instance().get_port()
        self.assertTrue(sig in complist, 'Failed registering client.')
        self.assertTrue(port in complist[sig],
                        'Failed registering client port.')

    def test_client_receives_signal(self):
        """
        Ensure clients can receive signals.
        """
        sig = 7
        flag = Mock()

        events.register(sig, lambda req: flag(req.event))
        request = SignalRequest()
        request.event = sig
        request.content = ""
        request.mac_method = mac_auth.MacMethod.MAC_NONE
        request.mac = ""
        service = RpcService(EventsServerService_Stub, port, 'localhost')
        response = service.signal(request, timeout=1000)
        self.assertTrue(response is not None, 'Did not receive response.')
        time.sleep(0.5)
        flag.assert_called_once_with(sig)

    def test_client_send_signal(self):
        """
        Ensure clients can send signals.
        """
        sig = 8
        response = events.signal(sig)
        self.assertTrue(response.status == response.OK,
                        'Received wrong response status when signaling.')

    def test_client_unregister_all(self):
        """
        Test that the client can unregister all events for one signal.
        """
        sig = CLIENT_UID
        complist = server.registered_clients
        events.register(sig, lambda x: True)
        events.register(sig, lambda x: True)
        time.sleep(0.1)
        events.unregister(sig)
        time.sleep(0.1)
        port = client.EventsClientDaemon.get_instance().get_port()
        self.assertFalse(bool(complist[sig]))
        self.assertTrue(port not in complist[sig])

    def test_client_unregister_by_uid(self):
        """
        Test that the client can unregister an event by uid.
        """
        sig = CLIENT_UID
        complist = server.registered_clients
        events.register(sig, lambda x: True, uid='cbkuid')
        events.register(sig, lambda x: True, uid='cbkuid2')
        time.sleep(0.1)
        events.unregister(sig, uid='cbkuid')
        time.sleep(0.1)
        port = client.EventsClientDaemon.get_instance().get_port()
        self.assertTrue(sig in complist)
        self.assertTrue(len(complist[sig]) == 1)
        self.assertTrue(
            client.registered_callbacks[sig].pop()[0] == 'cbkuid2')
        self.assertTrue(port in complist[sig])

    def test_server_replies_ping(self):
        """
        Ensure server replies to a ping.
        """
        request = PingRequest()
        service = RpcService(EventsServerService_Stub, port, 'localhost')
        response = service.ping(request, timeout=1000)
        self.assertIsNotNone(response)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_client_replies_ping(self):
        """
        Ensure clients reply to a ping.
        """
        daemon = client.ensure_client_daemon()
        port = daemon.get_port()
        request = PingRequest()
        service = RpcService(EventsClientService_Stub, port, 'localhost')
        response = service.ping(request, timeout=1000)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_server_ping(self):
        """
        Ensure the function from server module pings correctly.
        """
        response = server.ping()
        self.assertIsNotNone(response)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_client_ping(self):
        """
        Ensure the function from client module pings correctly.
        """
        daemon = client.ensure_client_daemon()
        response = client.ping(daemon.get_port())
        self.assertIsNotNone(response)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_module_ping_server(self):
        """
        Ensure the function from main module pings server correctly.
        """
        response = events.ping_server()
        self.assertIsNotNone(response)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_module_ping_client(self):
        """
        Ensure the function from main module pings clients correctly.
        """
        daemon = client.ensure_client_daemon()
        response = events.ping_client(daemon.get_port())
        self.assertIsNotNone(response)
        self.assertEqual(EventResponse.OK, response.status,
                         'Wrong response status.')

    def test_ensure_server_raises_if_port_taken(self):
        """
        Verify that server raises an exception if port is already taken.
        """
        # get a random free port
        while True:
            port = random.randint(1024, 65535)
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.connect(('localhost', port))
                s.close()
            except:
                break

        class PortBlocker(threading.Thread):

            def run(self):
                conns = 0
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.bind(('localhost', port))
                s.setblocking(1)
                s.listen(1)
                while conns < 2:  # blocks until rece
                    conns += 1
                    s.accept()
                s.close()

        # block the port
        taker = PortBlocker()
        taker.start()
        time.sleep(1)  # wait for thread to start.
        self.assertRaises(
            server.PortAlreadyTaken, server.ensure_server, port)

    def test_async_register(self):
        """
        Test asynchronous registering of callbacks.
        """
        flag = Mock()

        # executed after async register, when response is received from server
        def reqcbk(request, response):
            flag(request.event, response.status)

        # callback registered by application
        def callback(request):
            pass

        # passing a callback as reqcbk param makes the call asynchronous
        result = events.register(CLIENT_UID, callback, reqcbk=reqcbk)
        self.assertIsNone(result)
        events.signal(CLIENT_UID)
        time.sleep(1)  # wait for signal to arrive from server
        flag.assert_called_once_with(CLIENT_UID, EventResponse.OK)

    def test_async_signal(self):
        """
        Test asynchronous registering of callbacks.
        """
        flag = Mock()

        # executed after async signal, when response is received from server
        def reqcbk(request, response):
            flag(request.event, response.status)

        # passing a callback as reqcbk param makes the call asynchronous
        result = events.signal(CLIENT_UID, reqcbk=reqcbk)
        self.assertIsNone(result)
        time.sleep(1)  # wait for signal to arrive from server
        flag.assert_called_once_with(CLIENT_UID, EventResponse.OK)

    def test_async_unregister(self):
        """
        Test asynchronous unregistering of callbacks.
        """
        flag = Mock()

        # executed after async signal, when response is received from server
        def reqcbk(request, response):
            flag(request.event, response.status)

        # callback registered by application
        def callback(request):
            pass

        # passing a callback as reqcbk param makes the call asynchronous
        events.register(CLIENT_UID, callback)
        result = events.unregister(CLIENT_UID, reqcbk=reqcbk)
        self.assertIsNone(result)
        time.sleep(1)  # wait for signal to arrive from server
        flag.assert_called_once_with(CLIENT_UID, EventResponse.OK)

    def test_async_ping_server(self):
        """
        Test asynchronous pinging of server.
        """
        flag = Mock()

        # executed after async signal, when response is received from server
        def reqcbk(request, response):
            flag(response.status)

        result = events.ping_server(reqcbk=reqcbk)
        self.assertIsNone(result)
        time.sleep(1)  # wait for response to arrive from server.
        flag.assert_called_once_with(EventResponse.OK)

    def test_async_ping_client(self):
        """
        Test asynchronous pinging of client.
        """
        flag = Mock()

        # executed after async signal, when response is received from server
        def reqcbk(request, response):
            flag(response.status)

        daemon = client.ensure_client_daemon()
        result = events.ping_client(daemon.get_port(), reqcbk=reqcbk)
        self.assertIsNone(result)
        time.sleep(1)  # wait for response to arrive from server.
        flag.assert_called_once_with(EventResponse.OK)