Merge lp:~alecu/ubuntuone-client/cleanup-emit-signals into lp:ubuntuone-client

Proposed by Alejandro J. Cura
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 1125
Merged at revision: 1126
Proposed branch: lp:~alecu/ubuntuone-client/cleanup-emit-signals
Merge into: lp:ubuntuone-client
Diff against target: 471 lines (+168/-88)
3 files modified
tests/platform/windows/test_ipc.py (+99/-28)
ubuntuone/platform/windows/ipc.py (+18/-20)
ubuntuone/platform/windows/ipc_client.py (+51/-40)
To merge this branch: bzr merge lp:~alecu/ubuntuone-client/cleanup-emit-signals
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Natalia Bidart (community) Approve
Review via email: mp+73733@code.launchpad.net

Commit message

Connect each IPC client to the signals it should listen to. (LP: #839060)

Description of the change

Connect each IPC client to the signals it should listen to. (LP: #839060)

To post a comment you must log in.
Revision history for this message
Manuel de la Peña (mandel) wrote :

A small comment, sending the entire collection of remote_calls to be signals seems wrong to me. Because remove_calls can be all the calls that can be remotly called from the client. I know that at this point we just have signals.. but who knows, what about using the @signal decorator to create a signals collection? Or at least a subset of all the remote_calls.

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good to me!

review: Approve
1124. By Alejandro J. Cura

separate the list of signal handlers from the list of remote calls.

1125. By Alejandro J. Cura

fix some tests

Revision history for this message
Manuel de la Peña (mandel) wrote :

Nice!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/windows/test_ipc.py'
2--- tests/platform/windows/test_ipc.py 2011-08-26 00:03:30 +0000
3+++ tests/platform/windows/test_ipc.py 2011-09-02 15:03:18 +0000
4@@ -43,6 +43,7 @@
5 IPCInterface,
6 IPCRoot,
7 PublicFiles,
8+ RemoteMeta,
9 Shares,
10 SignalBroadcaster,
11 Status,
12@@ -58,9 +59,10 @@
13 FoldersClient,
14 FileSystemClient,
15 PublicFilesClient,
16+ RemoteClient,
17 StatusClient,
18 SyncDaemonClient,
19- SharesClient
20+ SharesClient,
21 )
22
23 TEST_PORT = 40404
24@@ -272,6 +274,7 @@
25 """Return the public files remote object."""
26 return self._public_files
27
28+
29 class TestSignalBroadcaster(MockerTestCase):
30 """Test the signal broadcaster code."""
31
32@@ -279,13 +282,15 @@
33 super(TestSignalBroadcaster, self).setUp()
34 self.client = self.mocker.mock()
35 self.broad_caster = SignalBroadcaster()
36- self.broad_caster.clients.append(self.client)
37
38 def test_remote_register_to_signals(self):
39 """Assert that the client was added."""
40 self.mocker.replay()
41- self.broad_caster.remote_register_to_signals(self.client)
42- self.assertTrue(self.client in self.broad_caster.clients)
43+ signals = ["demo_signal1", "demo_signal2"]
44+ self.broad_caster.remote_register_to_signals(self.client, signals)
45+ for signal in signals:
46+ clients = self.broad_caster.clients_per_signal[signal]
47+ self.assertTrue(self.client in clients)
48
49 def test_emit_signal(self):
50 """Assert that the client method was called."""
51@@ -299,6 +304,8 @@
52 deferred.addErrback(ANY, ANY, ANY)
53 deferred.addErrback(ANY, ANY, ANY)
54 self.mocker.replay()
55+ signals = [signal_name]
56+ self.broad_caster.remote_register_to_signals(self.client, signals)
57 self.broad_caster.emit_signal(signal_name, first, second, word=word)
58
59 def test_emit_signal_dead_reference(self):
60@@ -310,10 +317,11 @@
61 self.mocker.replay()
62
63 sb = SignalBroadcaster()
64- sb.remote_register_to_signals(fake_remote_client)
65- self.assertIn(fake_remote_client, sb.clients)
66+ sb.remote_register_to_signals(fake_remote_client, [sample_signal])
67+ self.assertIn(fake_remote_client, sb.clients_per_signal[sample_signal])
68 sb.emit_signal(sample_signal)
69- self.assertNotIn(fake_remote_client, sb.clients)
70+ self.assertNotIn(fake_remote_client,
71+ sb.clients_per_signal[sample_signal])
72
73 def test_emit_signal_some_dead_some_not(self):
74 """Test a clean reference after a dead one."""
75@@ -328,8 +336,8 @@
76 self.mocker.replay()
77
78 sb = SignalBroadcaster()
79- sb.remote_register_to_signals(fake_dead_remote)
80- sb.remote_register_to_signals(fake_alive_remote)
81+ sb.remote_register_to_signals(fake_dead_remote, [sample_signal])
82+ sb.remote_register_to_signals(fake_alive_remote, [sample_signal])
83 sb.emit_signal(sample_signal)
84
85
86@@ -360,8 +368,10 @@
87 fake_remote_client = FakeRemoteClient()
88
89 sb = SignalBroadcaster()
90- sb.remote_register_to_signals(fake_remote_client)
91- self.assertIn(fake_remote_client, sb.clients)
92+ signals = [fake_remote_client.missing_signal]
93+ sb.remote_register_to_signals(fake_remote_client, signals)
94+ sb_clients = sb.clients_per_signal[fake_remote_client.missing_signal]
95+ self.assertIn(fake_remote_client, sb_clients)
96 sb.emit_signal(fake_remote_client.missing_signal)
97
98 expected = (
99@@ -379,8 +389,10 @@
100 fake_remote_client = FakeRemoteClient()
101
102 sb = SignalBroadcaster()
103- sb.remote_register_to_signals(fake_remote_client)
104- self.assertIn(fake_remote_client, sb.clients)
105+ signals = [fake_remote_client.failing_signal]
106+ sb.remote_register_to_signals(fake_remote_client, signals)
107+ sb_clients = sb.clients_per_signal[fake_remote_client.failing_signal]
108+ self.assertIn(fake_remote_client, sb_clients)
109 sb.emit_signal(fake_remote_client.failing_signal)
110
111 expected = (
112@@ -392,11 +404,38 @@
113 self.assertIn(expected, warnings)
114
115
116+class FakeRemoteObject(object):
117+ """A test helper."""
118+
119+ def __init__(self):
120+ """Initialize this test helper."""
121+ self.called = []
122+
123+ def callRemote(self, *args):
124+ """A remote call to this object."""
125+ self.called.append(args)
126+
127+
128+class RemoteClientTestCase(TestCase):
129+ """Tests for the RemoteClient class."""
130+
131+ def test_register_to_signals(self):
132+ """Test the register_to_signals method."""
133+ fake_remote_object = FakeRemoteObject()
134+ client = RemoteClient(fake_remote_object)
135+ client.signal_handlers = ["on_abc"]
136+ client.register_to_signals()
137+ expected = [
138+ ("register_to_signals", client, client.signal_handlers)
139+ ]
140+ self.assertEqual(fake_remote_object.called, expected)
141+
142+
143 class TestStatusEmitSignals(PerspectiveBrokerTestCase, MockerTestCase):
144 """Tests that the emit methods are correct."""
145
146 def setUp(self):
147- """Setup tests."""
148+ """Setup tests."""
149 super(TestStatusEmitSignals, self).setUp()
150 self.signal_method = self.mocker.mock()
151 self.status.syncdaemon_status = self.mocker.mock()
152@@ -885,21 +924,11 @@
153 root = yield self.client_factory.getRootObject()
154 remote = yield root.callRemote('get_status')
155 client = StatusClient(remote)
156+ # set the cb
157+ callback_names = [signal + "_cb" for signal in client.signal_handlers]
158 yield client.register_to_signals()
159- # set the cb
160- for signal in ['on_content_queue_changed_cb',
161- 'on_invalid_name_cb',
162- 'on_broken_node_cb',
163- 'on_status_changed_cb',
164- 'on_download_started_cb',
165- 'on_download_file_progress_cb',
166- 'on_download_finished_cb',
167- 'on_upload_started_cb',
168- 'on_upload_file_progress_cb',
169- 'on_upload_finished_cb',
170- 'on_account_changed_cb',
171- 'on_metaqueue_changed_cb']:
172- setattr(client, signal, self.mocker.mock())
173+ for callback_name in callback_names:
174+ setattr(client, callback_name, self.mocker.mock())
175 defer.returnValue(client)
176
177 def test_current_status(self):
178@@ -3119,6 +3148,48 @@
179 self.sender.handle_default(event_name, **kwargs)
180
181
182+class RemoteMetaTestCase(TestCase):
183+ """Tests for the RemoteMeta metaclass."""
184+
185+ def test_remote_calls_renamed(self):
186+ """The remote_calls are renamed."""
187+ test_token = object()
188+
189+ class TestClass(object):
190+ """A class for testing."""
191+
192+ __metaclass__ = RemoteMeta
193+
194+ remote_calls = ['test_method']
195+
196+ def test_method(self):
197+ """Fake call."""
198+ return test_token
199+
200+ tc = TestClass()
201+ self.assertEquals(tc.test_method(), test_token)
202+ self.assertEquals(tc.remote_test_method(), test_token)
203+
204+ def test_signal_handlers_renamed(self):
205+ """The signal_handlers are renamed."""
206+ test_token = object()
207+
208+ class TestClass(object):
209+ """A class for testing."""
210+
211+ __metaclass__ = RemoteMeta
212+
213+ signal_handlers = ['test_signal_handler']
214+
215+ def test_signal_handler(self):
216+ """Fake call."""
217+ return test_token
218+
219+ tc = TestClass()
220+ self.assertEquals(tc.test_signal_handler(), test_token)
221+ self.assertEquals(tc.remote_test_signal_handler(), test_token)
222+
223+
224 class TestIPCRoot(BaseIPCTestCase):
225 """Ensure that the IPCRoot works as expected."""
226
227
228=== modified file 'ubuntuone/platform/windows/ipc.py'
229--- ubuntuone/platform/windows/ipc.py 2011-08-26 00:03:30 +0000
230+++ ubuntuone/platform/windows/ipc.py 2011-09-02 15:03:18 +0000
231@@ -20,6 +20,7 @@
232
233 import logging
234
235+from collections import defaultdict
236 from threading import Thread
237
238 from twisted.internet import defer, reactor
239@@ -139,10 +140,10 @@
240 """
241
242 def __new__(cls, name, bases, attrs):
243- remote_calls = attrs.get('remote_calls', None)
244- if remote_calls:
245- for current in remote_calls:
246- attrs['remote_' + current] = attrs[current]
247+ remote_calls = attrs.get('remote_calls', [])
248+ signal_handlers = attrs.get('signal_handlers', [])
249+ for current in remote_calls + signal_handlers:
250+ attrs['remote_' + current] = attrs[current]
251 return super(RemoteMeta, cls).__new__(cls, name, bases, attrs)
252
253
254@@ -154,7 +155,7 @@
255
256 def __init__(self):
257 """Create a new instance."""
258- self.clients = []
259+ self.clients_per_signal = defaultdict(set)
260
261 def _ignore_no_such_method(self, failure, signal_name, current_client):
262 """NoSuchMethod is not an error, ignore it."""
263@@ -167,33 +168,29 @@
264 current_client, failure.value)
265 logger.warning('Traceback is:\n%s', failure.printDetailedTraceback())
266
267- def remote_register_to_signals(self, client):
268- """Allow a client to register to a signal."""
269- if client not in self.clients:
270- self.clients.append(client)
271- else:
272- logger.warning('Client %s tried to register twice.', client)
273+ def remote_register_to_signals(self, client, signals):
274+ """Allow a client to register to some signals."""
275+ for signal in signals:
276+ self.clients_per_signal[signal].add(client)
277
278 def remote_unregister_to_signals(self, client):
279- """Allow a client to register to a signal."""
280- if client in self.clients:
281- self.clients.remove(client)
282- else:
283- logger.warning('Tried to remove %s when was not registered.',
284- client)
285+ """Allow a client to unregister from the signal."""
286+ for connected_clients in self.clients_per_signal.values():
287+ if client in connected_clients:
288+ connected_clients.remove(client)
289
290 def emit_signal(self, signal_name, *args, **kwargs):
291 """Emit the given signal to the clients."""
292 logger.debug("emitting %r to all connected clients.", signal_name)
293- dead_clients = []
294- for current_client in self.clients:
295+ dead_clients = set()
296+ for current_client in self.clients_per_signal[signal_name]:
297 try:
298 d = current_client.callRemote(signal_name, *args, **kwargs)
299 d.addErrback(self._ignore_no_such_method, signal_name,
300 current_client)
301 d.addErrback(self._other_failure, signal_name, current_client)
302 except DeadReferenceError:
303- dead_clients.append(current_client)
304+ dead_clients.add(current_client)
305 for client in dead_clients:
306 self.remote_unregister_to_signals(client)
307
308@@ -379,6 +376,7 @@
309
310 def __init__(self, root, main, volume_manager, action_queue):
311 """ Creates the instance."""
312+ super(SyncDaemon, self).__init__()
313 self.service = SyncdaemonService(root, main, volume_manager,
314 action_queue)
315 self.clients = []
316
317=== modified file 'ubuntuone/platform/windows/ipc_client.py'
318--- ubuntuone/platform/windows/ipc_client.py 2011-07-15 17:03:13 +0000
319+++ ubuntuone/platform/windows/ipc_client.py 2011-09-02 15:03:18 +0000
320@@ -1,6 +1,7 @@
321 # -*- coding: utf-8 -*-
322 #
323-# Author: Manuel de la Pena<manuel@canonical.com>
324+# Authors: Manuel de la Pena <manuel@canonical.com>
325+# Alejandro J. Cura <alecu@canonical.com>
326 #
327 # Copyright 2011 Canonical Ltd.
328 #
329@@ -65,13 +66,16 @@
330 class RemoteClient(object):
331 """Represent a client for remote calls."""
332
333+ signal_handlers = []
334+
335 def __init__(self, remote_object):
336 """Create instance."""
337 self.remote = remote_object
338
339 def register_to_signals(self):
340 """Register to the signals."""
341- return self.remote.callRemote('register_to_signals', self)
342+ return self.remote.callRemote('register_to_signals', self,
343+ self.signal_handlers)
344
345 def unregister_to_signals(self):
346 """Register to the signals."""
347@@ -124,18 +128,20 @@
348 __metaclass__ = RemoteMeta
349
350 # calls that will be accessible remotely
351- remote_calls = ['on_content_queue_changed',
352- 'on_invalid_name',
353- 'on_broken_node',
354- 'on_status_changed',
355- 'on_download_started',
356- 'on_download_file_progress',
357- 'on_download_finished',
358- 'on_upload_started',
359- 'on_upload_file_progress',
360- 'on_upload_finished',
361- 'on_account_changed',
362- 'on_metaqueue_changed']
363+ signal_handlers = [
364+ 'on_content_queue_changed',
365+ 'on_invalid_name',
366+ 'on_broken_node',
367+ 'on_status_changed',
368+ 'on_download_started',
369+ 'on_download_file_progress',
370+ 'on_download_finished',
371+ 'on_upload_started',
372+ 'on_upload_file_progress',
373+ 'on_upload_finished',
374+ 'on_account_changed',
375+ 'on_metaqueue_changed',
376+ ]
377
378 def __init__(self, remote_status):
379 """Creates the instance."""
380@@ -228,7 +234,7 @@
381 __metaclass__ = RemoteMeta
382
383 # calls that will be accessible remotely
384- remote_calls = ['on_event',]
385+ signal_handlers = ['on_event',]
386
387 def __init__(self, remote_events):
388 """Creates the instance."""
389@@ -249,8 +255,7 @@
390 __metaclass__ = RemoteMeta
391
392 # calls that will be accessible remotely
393- remote_calls = ['on_root_mismatch',
394- 'on_quota_exceeded']
395+ signal_handlers = ['on_root_mismatch', 'on_quota_exceeded']
396
397 def __init__(self, remote_daemon):
398 """Creates the instance."""
399@@ -332,17 +337,19 @@
400 __metaclass__ = RemoteMeta
401
402 # calls that will be accessible remotely
403- remote_calls = ['on_share_deleted',
404- 'on_share_changed',
405- 'on_share_delete_error',
406- 'on_share_created',
407- 'on_share_create_error',
408- 'on_share_answer_response',
409- 'on_new_share',
410- 'on_share_subscribed',
411- 'on_share_subscribe_error',
412- 'on_share_unsubscribed',
413- 'on_share_unsubscribe_error']
414+ signal_handlers = [
415+ 'on_share_deleted',
416+ 'on_share_changed',
417+ 'on_share_delete_error',
418+ 'on_share_created',
419+ 'on_share_create_error',
420+ 'on_share_answer_response',
421+ 'on_new_share',
422+ 'on_share_subscribed',
423+ 'on_share_subscribe_error',
424+ 'on_share_unsubscribed',
425+ 'on_share_unsubscribe_error',
426+ ]
427
428 def __init__(self, remote_shares):
429 """Create the instance."""
430@@ -556,14 +563,16 @@
431 __metaclass__ = RemoteMeta
432
433 # calls that will be accessible remotely
434- remote_calls = ['on_folder_created',
435- 'on_folder_create_error',
436- 'on_folder_deleted',
437- 'on_folder_delete_error',
438- 'on_folder_subscribed',
439- 'on_folder_subscribe_error',
440- 'on_folder_unsubscribed',
441- 'on_folder_unsubscribe_error']
442+ signal_handlers = [
443+ 'on_folder_created',
444+ 'on_folder_create_error',
445+ 'on_folder_deleted',
446+ 'on_folder_delete_error',
447+ 'on_folder_subscribed',
448+ 'on_folder_subscribe_error',
449+ 'on_folder_unsubscribed',
450+ 'on_folder_unsubscribe_error',
451+ ]
452
453 def __init__(self, remote_folders):
454 """Creates the instance."""
455@@ -636,10 +645,12 @@
456 __metaclass__ = RemoteMeta
457
458 # calls that will be accessible remotely
459- remote_calls = ['on_public_access_changed',
460- 'on_public_access_change_error',
461- 'on_public_files_list',
462- 'on_public_files_list_error']
463+ signal_handlers = [
464+ 'on_public_access_changed',
465+ 'on_public_access_change_error',
466+ 'on_public_files_list',
467+ 'on_public_files_list_error',
468+ ]
469
470 def __init__(self, remote_public_files):
471 super(PublicFilesClient, self).__init__(remote_public_files)

Subscribers

People subscribed via source and target branches