Merge lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-publishapi into lp:ubuntuone-client

Proposed by Diego Sarmentero
Status: Merged
Approved by: Diego Sarmentero
Approved revision: 1340
Merged at revision: 1336
Proposed branch: lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-publishapi
Merge into: lp:ubuntuone-client
Diff against target: 180 lines (+113/-11)
2 files modified
tests/platform/tools/test_tools.py (+98/-6)
ubuntuone/platform/tools/perspective_broker.py (+15/-5)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntuone-client/ubuntuone-client-publishapi
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Mike McCracken (community) Approve
Review via email: mp+128312@code.launchpad.net

Commit message

- Making the handler connection to be a list of handlers (LP: #1061880).

To post a comment you must log in.
Revision history for this message
Mike McCracken (mikemc) wrote :

This is related to this control-panel branch:
https://code.launchpad.net/~diegosarmentero/ubuntuone-control-panel/u1-cp-publishapi/+merge/128316

but in light of that branch, it's not totally clear why we need to change this here.

share_links is the only part of control panel that ever sets signal handlers for the public_files handler and the public_access_changed handler. Is it possible to have multiple invocations of this remote call from control panel? ie, does this happen if we click fast to share two files in the table or something? or is that impossible?

Also, just a note that on IRC I requested a test that shows the problem and breaks with the old code… Diego was working on that at EOD friday…

review: Needs Information
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

We needed to store a list of handlers to be called, because the setattr is overriding the behaviour of that part of the object every time a connect_signal is requested, and it was happening that several functions were being connected to the same signals, the ones with the handler that we actually want, and some other for clean up at syncdaemon core, and only the last handler connected was the one that was being called.

1335. By Diego Sarmentero

merge

1336. By Diego Sarmentero

improving handler calls

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

73 + self.addCleanup(self.sdtool.shutdown)

Why not adding it after the creation of the sdtool?

review: Needs Fixing
1337. By Diego Sarmentero

improve test

1338. By Diego Sarmentero

fix test

1339. By Diego Sarmentero

adding new test

Revision history for this message
Mike McCracken (mikemc) wrote :

minor - test_connect_avoid_repeated_connection has the same docstring as test_connect_several_handlers_to_same_signal but they test different things

review: Needs Fixing
1340. By Diego Sarmentero

fixed docstring

Revision history for this message
Mike McCracken (mikemc) wrote :

Approve. These new tests pass on OSX*, and testing the share links panel shows no further hangs.

* the whole suite still has a ton of dirty reactor errors though

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tests/platform/tools/test_tools.py'
--- tests/platform/tools/test_tools.py 2012-08-27 15:22:13 +0000
+++ tests/platform/tools/test_tools.py 2012-10-09 15:37:25 +0000
@@ -139,11 +139,11 @@
139 def method_call(self, *args, **kwargs):139 def method_call(self, *args, **kwargs):
140 """Fake a remote method call."""140 """Fake a remote method call."""
141 if self.number_calls == 0:141 if self.number_calls == 0:
142 self.number_calls += 1142 self.number_calls += 1
143 raise pb.DeadReferenceError()143 raise pb.DeadReferenceError()
144 else:144 else:
145 self.called.append((args, kwargs))145 self.called.append((args, kwargs))
146 return defer.succeed(self.number_calls)146 return defer.succeed(self.number_calls)
147147
148148
149class PerspectiveBrokerReconnect(TestCase):149class PerspectiveBrokerReconnect(TestCase):
@@ -184,8 +184,100 @@
184 @defer.inlineCallbacks184 @defer.inlineCallbacks
185 def test_reconnect_signals(self):185 def test_reconnect_signals(self):
186 """Test reconnection with signals."""186 """Test reconnection with signals."""
187 self.sdtool.connected_signals = dict(create_signal=lambda:None,187 self.sdtool.connected_signals = dict(create_signal=[lambda: None],
188 delete_signal=lambda:None)188 delete_signal=[lambda: None])
189 yield self.sdtool.call_method('fake_remote', 'method_call')189 yield self.sdtool.call_method('fake_remote', 'method_call')
190 self.assertTrue(self.reconnected)190 self.assertTrue(self.reconnected)
191 self.assertEqual(2, len(self.connected_signals))191 self.assertEqual(2, len(self.connected_signals))
192
193
194class FakeU1Objects(object):
195 """Fake PublicFiles for UbuntuOneClient."""
196
197 def on_public_files_list_cb(self):
198 """Do nothing."""
199
200 def on_share_changed_cb(self):
201 """Do nothing."""
202
203
204class FakeUbuntuOneClient(object):
205 """Fake UbuntuOneClient."""
206
207 def __init__(self):
208 self.public_files = FakeU1Objects()
209 self.shares = FakeU1Objects()
210 self.connected = True
211
212 def is_connected(self):
213 """Fake is_connected."""
214 return self.connected
215
216 def connect(self):
217 """Fake connect."""
218 self.connected = True
219 yield self.connected
220
221 def disconnect(self):
222 """Fake disconnect."""
223 self.connected = False
224
225
226class PerspectiveBrokerConnectSignal(TestCase):
227 """Test when the ipc is reconnected."""
228
229 @defer.inlineCallbacks
230 def setUp(self):
231 """Set the tests."""
232 yield super(PerspectiveBrokerConnectSignal, self).setUp()
233 self.patch(perspective_broker, 'UbuntuOneClient', FakeUbuntuOneClient)
234 self.sdtool = perspective_broker.SyncDaemonToolProxy()
235 self.addCleanup(self.sdtool.shutdown)
236
237 def test_connect_several_handlers_to_same_signal(self):
238 """Connect more than one handler to the same signal name."""
239 data = []
240
241 signal_name = "PublicFilesList"
242 self.sdtool.connect_signal(signal_name, lambda *a: data.append(a))
243 self.sdtool.connect_signal(signal_name, lambda *a: data.append(a))
244
245 self.assertEqual(len(self.sdtool.connected_signals[signal_name]), 2)
246 self.sdtool.client.public_files.on_public_files_list_cb()
247 expected = [(), ()]
248 self.assertEqual(data, expected)
249
250 def test_connect_avoid_repeated_connection(self):
251 """Ensure that we don't have the same handler called twice."""
252 data = []
253
254 signal_name = "PublicFilesList"
255 func = lambda *a: data.append(a)
256 self.sdtool.connect_signal(signal_name, func)
257 self.sdtool.connect_signal(signal_name, func)
258
259 self.assertEqual(len(self.sdtool.connected_signals[signal_name]), 1)
260 self.sdtool.client.public_files.on_public_files_list_cb()
261 expected = [()]
262 self.assertEqual(data, expected)
263
264 def test_proper_connections(self):
265 """Check that the proper handlers are called."""
266 data = []
267 data2 = []
268
269 signal_name = "PublicFilesList"
270 signal_name2 = "ShareChanges"
271 self.sdtool.connect_signal(signal_name, lambda *a: data.append(a))
272 self.sdtool.connect_signal(signal_name2, lambda *a: data2.append(a))
273
274 self.assertEqual(len(self.sdtool.connected_signals[signal_name]), 1)
275 self.assertEqual(len(self.sdtool.connected_signals[signal_name2]), 1)
276 self.sdtool.client.public_files.on_public_files_list_cb()
277 expected = [()]
278 self.assertEqual(data, expected)
279 self.assertEqual(data2, [])
280 data = []
281 self.sdtool.client.shares.on_share_changed_cb()
282 self.assertEqual(data, [])
283 self.assertEqual(data2, expected)
192\ No newline at end of file284\ No newline at end of file
193285
=== modified file 'ubuntuone/platform/tools/perspective_broker.py'
--- ubuntuone/platform/tools/perspective_broker.py 2012-08-27 15:49:13 +0000
+++ ubuntuone/platform/tools/perspective_broker.py 2012-10-09 15:37:25 +0000
@@ -30,6 +30,7 @@
3030
31import logging31import logging
32import subprocess32import subprocess
33from collections import defaultdict
3334
34from twisted.internet import defer35from twisted.internet import defer
35from twisted.spread.pb import DeadReferenceError36from twisted.spread.pb import DeadReferenceError
@@ -122,7 +123,7 @@
122 'perspective_broker')123 'perspective_broker')
123 self.client = UbuntuOneClient()124 self.client = UbuntuOneClient()
124 self.connected = None125 self.connected = None
125 self.connected_signals = {}126 self.connected_signals = defaultdict(set)
126127
127 def _call_after_connection(self, method):128 def _call_after_connection(self, method):
128 """Make sure Perspective Broker is connected before calling."""129 """Make sure Perspective Broker is connected before calling."""
@@ -150,8 +151,9 @@
150 self.connected = False151 self.connected = False
151 yield self.client.reconnect()152 yield self.client.reconnect()
152 # do connect all the signals again153 # do connect all the signals again
153 for signal_name, handler in self.connected_signals.items():154 for signal_name, handlers in self.connected_signals.items():
154 self.connect_signal(signal_name, handler)155 for handler in handlers:
156 self.connect_signal(signal_name, handler)
155157
156 @defer.inlineCallbacks158 @defer.inlineCallbacks
157 def call_method(self, client_kind, method_name, *args, **kwargs):159 def call_method(self, client_kind, method_name, *args, **kwargs):
@@ -173,13 +175,21 @@
173 """Close connections."""175 """Close connections."""
174 return self.client.disconnect()176 return self.client.disconnect()
175177
178 def _handler(self, signal_name, *args, **kwargs):
179 """Call all the handlers connected to signal_name."""
180 for cb_handler in self.connected_signals[signal_name]:
181 cb_handler(*args, **kwargs)
182
176 def connect_signal(self, signal_name, handler):183 def connect_signal(self, signal_name, handler):
177 """Connect 'handler' with 'signal_name'."""184 """Connect 'handler' with 'signal_name'."""
178 client_kind, callback = self._SIGNAL_MAPPING[signal_name]185 client_kind, callback = self._SIGNAL_MAPPING[signal_name]
179 client = getattr(self.client, client_kind)186 client = getattr(self.client, client_kind)
180 setattr(client, callback, handler)187 if len(self.connected_signals[signal_name]) == 0:
188 setattr(client, callback,
189 lambda *args, **kwargs:
190 self._handler(signal_name, *args, **kwargs))
181 # do remember the connected signal in case we need to reconnect191 # do remember the connected signal in case we need to reconnect
182 self.connected_signals[signal_name] = handler192 self.connected_signals[signal_name].add(handler)
183 return handler193 return handler
184194
185 def disconnect_signal(self, signal_name, handler_or_match):195 def disconnect_signal(self, signal_name, handler_or_match):

Subscribers

People subscribed via source and target branches