Trying to init events server raises when given port is not free.
[leap_pycommon.git] / src / leap / common / tests / test_events.py
1 ## -*- coding: utf-8 -*-
2 # test_events.py
3 # Copyright (C) 2013 LEAP
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 import unittest
19 import sets
20 import time
21 import socket
22 import threading
23 import random
24
25
26 from mock import Mock
27 from protobuf.socketrpc import RpcService
28 from leap.common import events
29 from leap.common.events import (
30     server,
31     client,
32     mac_auth,
33 )
34 from leap.common.events.events_pb2 import (
35     EventsServerService,
36     EventsServerService_Stub,
37     EventsClientService_Stub,
38     EventResponse,
39     SignalRequest,
40     RegisterRequest,
41     PingRequest,
42     SOLEDAD_CREATING_KEYS,
43     CLIENT_UID,
44 )
45
46
47 port = 8090
48
49 received = False
50
51
52 class EventsTestCase(unittest.TestCase):
53
54     @classmethod
55     def setUpClass(cls):
56         server.EventsServerDaemon.ensure(8090)
57         cls.callbacks = events.client.registered_callbacks
58
59     @classmethod
60     def tearDownClass(cls):
61         # give some time for requests to be processed.
62         time.sleep(1)
63
64     def setUp(self):
65         super(EventsTestCase, self).setUp()
66
67     def tearDown(self):
68         #events.client.registered_callbacks = {}
69         server.registered_clients = {}
70         super(EventsTestCase, self).tearDown()
71
72     def test_service_singleton(self):
73         """
74         Ensure that there's always just one instance of the server daemon
75         running.
76         """
77         service1 = server.EventsServerDaemon.ensure(8090)
78         service2 = server.EventsServerDaemon.ensure(8090)
79         self.assertEqual(service1, service2,
80                          "Can't get singleton class for service.")
81
82     def test_client_register(self):
83         """
84         Ensure clients can register callbacks.
85         """
86         self.assertTrue(1 not in self.callbacks,
87                         'There should should be no callback for this signal.')
88         events.register(1, lambda x: True)
89         self.assertTrue(1 in self.callbacks,
90                         'Could not register signal in local client.')
91         events.register(2, lambda x: True)
92         self.assertTrue(1 in self.callbacks,
93                         'Could not register signal in local client.')
94         self.assertTrue(2 in self.callbacks,
95                         'Could not register signal in local client.')
96
97     def test_register_signal_replace(self):
98         """
99         Make sure clients can replace already registered callbacks.
100         """
101         sig = 3
102         cbk = lambda x: True
103         events.register(sig, cbk, uid=1)
104         self.assertRaises(Exception, events.register, sig, lambda x: True,
105                           uid=1)
106         events.register(sig, lambda x: True, uid=1, replace=True)
107         self.assertTrue(sig in self.callbacks, 'Could not register signal.')
108         self.assertEqual(1, len(self.callbacks[sig]),
109                          'Wrong number of registered callbacks.')
110
111     def test_signal_response_status(self):
112         """
113         Ensure there's an appropriate response from server when signaling.
114         """
115         sig = 4
116         request = SignalRequest()
117         request.event = sig
118         request.content = 'my signal contents'
119         request.mac_method = mac_auth.MacMethod.MAC_NONE
120         request.mac = ""
121         service = RpcService(EventsServerService_Stub, port, 'localhost')
122         # test synch
123         response = service.signal(request, timeout=1000)
124         self.assertEqual(EventResponse.OK, response.status,
125                          'Wrong response status.')
126
127     def test_signal_executes_callback(self):
128         """
129         Ensure callback is executed upon receiving signal.
130         """
131         sig = CLIENT_UID
132         request = SignalRequest()
133         request.event = sig
134         request.content = 'my signal contents'
135         request.mac_method = mac_auth.MacMethod.MAC_NONE
136         request.mac = ""
137         service = RpcService(EventsServerService_Stub, port, 'localhost')
138
139         # register a callback
140         flag = Mock()
141         events.register(sig, lambda req: flag(req.event))
142         # signal
143         response = service.signal(request)
144         self.assertEqual(EventResponse.OK, response.status,
145                          'Wrong response status.')
146         time.sleep(1)  # wait for signal to arrive
147         flag.assert_called_once_with(sig)
148
149     def test_events_server_service_register(self):
150         """
151         Ensure the server can register clients to be signaled.
152         """
153         sig = 5
154         request = RegisterRequest()
155         request.event = sig
156         request.port = 8091
157         request.mac_method = mac_auth.MacMethod.MAC_NONE
158         request.mac = ""
159         service = RpcService(EventsServerService_Stub, port, 'localhost')
160         complist = server.registered_clients
161         self.assertEqual({}, complist,
162                          'There should be no registered_ports when '
163                          'server has just been created.')
164         response = service.register(request, timeout=1000)
165         self.assertTrue(sig in complist, "Signal not registered succesfully.")
166         self.assertTrue(8091 in complist[sig],
167                         'Failed registering client port.')
168
169     def test_client_request_register(self):
170         """
171         Ensure clients can register themselves with server.
172         """
173         sig = 6
174         complist = server.registered_clients
175         self.assertTrue(sig not in complist,
176                         'There should be no registered clients for this '
177                         'signal.')
178         events.register(sig, lambda x: True)
179         time.sleep(0.1)
180         port = client.EventsClientDaemon.get_instance().get_port()
181         self.assertTrue(sig in complist, 'Failed registering client.')
182         self.assertTrue(port in complist[sig],
183                         'Failed registering client port.')
184
185     def test_client_receives_signal(self):
186         """
187         Ensure clients can receive signals.
188         """
189         sig = 7
190         flag = Mock()
191
192         events.register(sig, lambda req: flag(req.event))
193         request = SignalRequest()
194         request.event = sig
195         request.content = ""
196         request.mac_method = mac_auth.MacMethod.MAC_NONE
197         request.mac = ""
198         service = RpcService(EventsServerService_Stub, port, 'localhost')
199         response = service.signal(request, timeout=1000)
200         self.assertTrue(response is not None, 'Did not receive response.')
201         time.sleep(0.5)
202         flag.assert_called_once_with(sig)
203
204     def test_client_send_signal(self):
205         """
206         Ensure clients can send signals.
207         """
208         sig = 8
209         response = events.signal(sig)
210         self.assertTrue(response.status == response.OK,
211                         'Received wrong response status when signaling.')
212
213     def test_client_unregister_all(self):
214         """
215         Test that the client can unregister all events for one signal.
216         """
217         sig = CLIENT_UID
218         complist = server.registered_clients
219         events.register(sig, lambda x: True)
220         events.register(sig, lambda x: True)
221         time.sleep(0.1)
222         events.unregister(sig)
223         time.sleep(0.1)
224         port = client.EventsClientDaemon.get_instance().get_port()
225         self.assertFalse(bool(complist[sig]))
226         self.assertTrue(port not in complist[sig])
227
228     def test_client_unregister_by_uid(self):
229         """
230         Test that the client can unregister an event by uid.
231         """
232         sig = CLIENT_UID
233         complist = server.registered_clients
234         events.register(sig, lambda x: True, uid='cbkuid')
235         events.register(sig, lambda x: True, uid='cbkuid2')
236         time.sleep(0.1)
237         events.unregister(sig, uid='cbkuid')
238         time.sleep(0.1)
239         port = client.EventsClientDaemon.get_instance().get_port()
240         self.assertTrue(sig in complist)
241         self.assertTrue(len(complist[sig]) == 1)
242         self.assertTrue(
243             client.registered_callbacks[sig].pop()[0] == 'cbkuid2')
244         self.assertTrue(port in complist[sig])
245
246     def test_server_replies_ping(self):
247         """
248         Ensure server replies to a ping.
249         """
250         request = PingRequest()
251         service = RpcService(EventsServerService_Stub, port, 'localhost')
252         response = service.ping(request, timeout=1000)
253         self.assertIsNotNone(response)
254         self.assertEqual(EventResponse.OK, response.status,
255                          'Wrong response status.')
256
257     def test_client_replies_ping(self):
258         """
259         Ensure clients reply to a ping.
260         """
261         daemon = client.ensure_client_daemon()
262         port = daemon.get_port()
263         request = PingRequest()
264         service = RpcService(EventsClientService_Stub, port, 'localhost')
265         response = service.ping(request, timeout=1000)
266         self.assertEqual(EventResponse.OK, response.status,
267                          'Wrong response status.')
268
269     def test_server_ping(self):
270         """
271         Ensure the function from server module pings correctly.
272         """
273         response = server.ping()
274         self.assertIsNotNone(response)
275         self.assertEqual(EventResponse.OK, response.status,
276                          'Wrong response status.')
277
278     def test_client_ping(self):
279         """
280         Ensure the function from client module pings correctly.
281         """
282         daemon = client.ensure_client_daemon()
283         response = client.ping(daemon.get_port())
284         self.assertIsNotNone(response)
285         self.assertEqual(EventResponse.OK, response.status,
286                          'Wrong response status.')
287
288     def test_module_ping_server(self):
289         """
290         Ensure the function from main module pings server correctly.
291         """
292         response = events.ping_server()
293         self.assertIsNotNone(response)
294         self.assertEqual(EventResponse.OK, response.status,
295                          'Wrong response status.')
296
297     def test_module_ping_client(self):
298         """
299         Ensure the function from main module pings clients correctly.
300         """
301         daemon = client.ensure_client_daemon()
302         response = events.ping_client(daemon.get_port())
303         self.assertIsNotNone(response)
304         self.assertEqual(EventResponse.OK, response.status,
305                          'Wrong response status.')
306
307     def test_ensure_server_raises_if_port_taken(self):
308         """
309         Verify that server raises an exception if port is already taken.
310         """
311         # get a random free port
312         while True:
313             port = random.randint(1024, 65535)
314             try:
315                 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
316                 s.connect(('localhost', port))
317                 s.close()
318             except:
319                 break
320
321         class PortBlocker(threading.Thread):
322
323             def run(self):
324                 conns = 0
325                 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
326                 s.bind(('localhost', port))
327                 s.setblocking(1)
328                 s.listen(1)
329                 while conns < 2:  # blocks until rece
330                     conns += 1
331                     s.accept()
332                 s.close()
333
334         # block the port
335         taker = PortBlocker()
336         taker.start()
337         time.sleep(1)  # wait for thread to start.
338         self.assertRaises(
339             server.PortAlreadyTaken, server.ensure_server, port)