Merge lp:~nataliabidart/ubuntu-sso-client/shutdown into lp:ubuntu-sso-client

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 668
Merged at revision: 668
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/shutdown
Merge into: lp:ubuntu-sso-client
Diff against target: 413 lines (+235/-5)
5 files modified
bin/ubuntu-sso-login (+3/-1)
ubuntu_sso/logger.py (+1/-0)
ubuntu_sso/main.py (+43/-0)
ubuntu_sso/tests/__init__.py (+14/-0)
ubuntu_sso/tests/test_main.py (+174/-4)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/shutdown
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+45926@code.launchpad.net

Commit message

The service should shutdown when unused (LP: #701606).

Description of the change

To test, run from this branch:

killall ubuntu-sso-login; DEBUG=True PYTHONPATH=. ./bin/ubuntu-sso-login

And open d-feet and play with the com.ubuntuone.Credentials interface. You should expect to see the service quitting once there is no more requests to process.

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Code looks good, and works as well!

review: Approve
Revision history for this message
Roberto Alsina (ralsina) wrote :

I like it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/ubuntu-sso-login'
2--- bin/ubuntu-sso-login 2010-12-20 14:29:18 +0000
3+++ bin/ubuntu-sso-login 2011-01-11 22:44:07 +0000
4@@ -94,6 +94,8 @@
5 bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus())
6 SSOLogin(bus_name, object_path=DBUS_ACCOUNT_PATH)
7 SSOCredentials(bus_name, object_path=DBUS_CRED_PATH)
8- CredentialsManagement(bus_name, object_path=DBUS_CREDENTIALS_PATH)
9+ CredentialsManagement(timeout_func=gtk.timeout_add,
10+ shutdown_func=gtk.main_quit,
11+ bus_name=bus_name, object_path=DBUS_CREDENTIALS_PATH)
12
13 gtk.main()
14
15=== modified file 'ubuntu_sso/logger.py'
16--- ubuntu_sso/logger.py 2010-10-01 15:21:25 +0000
17+++ ubuntu_sso/logger.py 2011-01-11 22:44:07 +0000
18@@ -57,6 +57,7 @@
19 logger.addHandler(MAIN_HANDLER)
20 if os.environ.get('DEBUG'):
21 debug_handler = logging.StreamHandler(sys.stderr)
22+ debug_handler.setFormatter(logging.Formatter(fmt=FMT))
23 logger.addHandler(debug_handler)
24
25 return logger
26
27=== modified file 'ubuntu_sso/main.py'
28--- ubuntu_sso/main.py 2011-01-02 03:34:23 +0000
29+++ ubuntu_sso/main.py 2011-01-11 22:44:07 +0000
30@@ -50,6 +50,7 @@
31
32 logger = setup_logging("ubuntu_sso.main")
33 U1_PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
34+TIMEOUT_INTERVAL = 500
35
36
37 class SSOLoginProcessor(Account):
38@@ -421,6 +422,12 @@
39
40 """
41
42+ def __init__(self, timeout_func, shutdown_func, *args, **kwargs):
43+ super(CredentialsManagement, self).__init__(*args, **kwargs)
44+ self._ref_count = 0
45+ self.timeout_func = timeout_func
46+ self.shutdown_func = shutdown_func
47+
48 # Operator not preceded by a space (fails with dbus decorators)
49 # pylint: disable=C0322
50
51@@ -440,39 +447,66 @@
52 """Process the 'failure' and emit CredentialsError."""
53 self.CredentialsError(app_name, except_to_errdict(failure.value))
54
55+ def _get_ref_count(self):
56+ """Get value of ref_count."""
57+ logger.debug('ref_count is %r.', self._ref_count)
58+ return self._ref_count
59+
60+ def _set_ref_count(self, new_value):
61+ """Set a new value to ref_count."""
62+ logger.debug('ref_count is %r, changing value to %r.',
63+ self._ref_count, new_value)
64+ if new_value < 0:
65+ self._ref_count = 0
66+ msg = 'Attempting to decrease ref_count to a negative value (%r).'
67+ logger.warning(msg, new_value)
68+ else:
69+ self._ref_count = new_value
70+
71+ if self._ref_count == 0:
72+ self.timeout_func(TIMEOUT_INTERVAL, self.shutdown_func)
73+
74+ ref_count = property(fget=_get_ref_count, fset=_set_ref_count)
75+
76 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
77 def AuthorizationDenied(self, app_name):
78 """Signal thrown when the user denies the authorization."""
79+ self.ref_count -= 1
80 logger.info('%s: emitting AuthorizationDenied with app_name "%s".',
81 self.__class__.__name__, app_name)
82
83 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}')
84 def CredentialsFound(self, app_name, credentials):
85 """Signal thrown when the credentials are found."""
86+ self.ref_count -= 1
87 logger.info('%s: emitting CredentialsFound with app_name "%s".',
88 self.__class__.__name__, app_name)
89
90 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
91 def CredentialsNotFound(self, app_name):
92 """Signal thrown when the credentials are not found."""
93+ self.ref_count -= 1
94 logger.info('%s: emitting CredentialsNotFound with app_name "%s".',
95 self.__class__.__name__, app_name)
96
97 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
98 def CredentialsCleared(self, app_name):
99 """Signal thrown when the credentials were cleared."""
100+ self.ref_count -= 1
101 logger.info('%s: emitting CredentialsCleared with app_name "%s".',
102 self.__class__.__name__, app_name)
103
104 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s')
105 def CredentialsStored(self, app_name):
106 """Signal thrown when the credentials were cleared."""
107+ self.ref_count -= 1
108 logger.info('%s: emitting CredentialsStored with app_name "%s".',
109 self.__class__.__name__, app_name)
110
111 @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}')
112 def CredentialsError(self, app_name, error_dict):
113 """Signal thrown when there is a problem getting the credentials."""
114+ self.ref_count -= 1
115 logger.error('%s: emitting CredentialsError with app_name "%s" and '
116 'error_dict %r.', self.__class__.__name__, app_name,
117 error_dict)
118@@ -488,6 +522,7 @@
119 - 'args' is a dictionary, currently not used.
120
121 """
122+ self.ref_count += 1
123
124 def success_cb(credentials):
125 """Find credentials and notify using signals."""
126@@ -513,6 +548,8 @@
127 - 'args' is a dictionary, currently not used.
128
129 """
130+ self.ref_count += 1
131+
132 obj = Credentials(app_name)
133 d = obj.clear_credentials()
134 # pylint: disable=E1101
135@@ -532,6 +569,8 @@
136 'consumer_secret'.
137
138 """
139+ self.ref_count += 1
140+
141 obj = Credentials(app_name)
142 d = obj.store_credentials(args)
143 # pylint: disable=E1101
144@@ -542,6 +581,8 @@
145 in_signature='sa{ss}', out_signature='')
146 def register(self, app_name, args):
147 """Get credentials if found else prompt GUI to register."""
148+ self.ref_count += 1
149+
150 obj = Credentials(app_name, **self._parse_args(args))
151 obj.register()
152
153@@ -549,5 +590,7 @@
154 in_signature='sa{ss}', out_signature='')
155 def login(self, app_name, args):
156 """Get credentials if found else prompt GUI to login."""
157+ self.ref_count += 1
158+
159 obj = Credentials(app_name, **self._parse_args(args))
160 obj.login()
161
162=== modified file 'ubuntu_sso/tests/__init__.py'
163--- ubuntu_sso/tests/__init__.py 2010-11-19 19:53:22 +0000
164+++ ubuntu_sso/tests/__init__.py 2011-01-11 22:44:07 +0000
165@@ -18,6 +18,8 @@
166
167 import os
168
169+from twisted.trial import unittest
170+
171 from ubuntu_sso.keyring import get_token_name
172
173 APP_NAME = 'The Super App!'
174@@ -45,3 +47,15 @@
175 TOKEN_NAME = get_token_name(APP_NAME)
176 TC_URL = 'http://localhost/'
177 WINDOW_ID = 5
178+
179+
180+class TestCase(unittest.TestCase):
181+ """Customized test case that keeps tracks of method calls."""
182+
183+ def setUp(self):
184+ super(TestCase, self).setUp()
185+ self._called = False
186+
187+ def _set_called(self, *args, **kwargs):
188+ """Keep track of a method call."""
189+ self._called = (args, kwargs)
190
191=== modified file 'ubuntu_sso/tests/test_main.py'
192--- ubuntu_sso/tests/test_main.py 2011-01-02 03:34:23 +0000
193+++ ubuntu_sso/tests/test_main.py 2011-01-11 22:44:07 +0000
194@@ -34,14 +34,15 @@
195
196 from ubuntu_sso import DBUS_CREDENTIALS_IFACE
197 from ubuntu_sso.keyring import U1_APP_NAME
198-from ubuntu_sso.main import (U1_PING_URL, blocking, except_to_errdict,
199+from ubuntu_sso.main import (U1_PING_URL, TIMEOUT_INTERVAL,
200+ blocking, except_to_errdict,
201 CredentialsManagement, SSOCredentials, SSOLogin)
202 from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY,
203 TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY,
204 SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY)
205 from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID,
206 CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, PASSWORD, PING_URL, TOKEN,
207- TOKEN_NAME, WINDOW_ID)
208+ TOKEN_NAME, WINDOW_ID, TestCase)
209
210
211 # Access to a protected member 'yyy' of a client class
212@@ -709,15 +710,23 @@
213 }
214
215 def setUp(self):
216+ super(CredentialsManagementTestCase, self).setUp()
217+
218 self.mocker = Mocker()
219- self.client = CredentialsManagement()
220+ self.client = CredentialsManagement(timeout_func=lambda *a: None,
221+ shutdown_func=lambda *a: None)
222 self.args = {}
223 self.cred_args = {}
224
225+ self.memento = MementoHandler()
226+ self.memento.setLevel(logging.DEBUG)
227+ ubuntu_sso.main.logger.addHandler(self.memento)
228+
229 def tearDown(self):
230 """Verify the mocking stuff and shut it down."""
231 self.mocker.verify()
232 self.mocker.restore()
233+ super(CredentialsManagementTestCase, self).tearDown()
234
235 def assert_dbus_method_correct(self, method):
236 """Check that 'method' is a dbus method with proper signatures."""
237@@ -740,6 +749,166 @@
238 self.assertIsInstance(self.client, ubuntu_sso.main.dbus.service.Object)
239
240
241+class CredentialsManagementRefCountingTestCase(CredentialsManagementTestCase):
242+ """Tests for the CredentialsManagement ref counting."""
243+
244+ def test_ref_counting(self):
245+ """Ref counting is in place."""
246+ self.assertEqual(self.client.ref_count, 0)
247+
248+ def test_find_credentials(self):
249+ """Keep proper track of on going requests."""
250+ self.client.find_credentials(APP_NAME, self.args)
251+
252+ self.assertEqual(self.client.ref_count, 1)
253+
254+ def test_clear_credentials(self):
255+ """Keep proper track of on going requests."""
256+ self.client.clear_credentials(APP_NAME, self.args)
257+
258+ self.assertEqual(self.client.ref_count, 1)
259+
260+ def test_store_credentials(self):
261+ """Keep proper track of on going requests."""
262+ self.client.store_credentials(APP_NAME, self.args)
263+
264+ self.assertEqual(self.client.ref_count, 1)
265+
266+ def test_register(self):
267+ """Keep proper track of on going requests."""
268+ self.client.register(APP_NAME, self.args)
269+
270+ self.assertEqual(self.client.ref_count, 1)
271+
272+ def test_login(self):
273+ """Keep proper track of on going requests."""
274+ self.client.login(APP_NAME, self.args)
275+
276+ self.assertEqual(self.client.ref_count, 1)
277+
278+ def test_several_requests(self):
279+ """Requests can be nested."""
280+ self.client.login(APP_NAME, self.args)
281+ self.client.clear_credentials(APP_NAME, self.args)
282+ self.client.find_credentials(APP_NAME, self.args)
283+ self.client.register(APP_NAME, self.args)
284+ self.client.store_credentials(APP_NAME, self.args)
285+
286+ self.assertEqual(self.client.ref_count, 5)
287+
288+ def test_credentials_found(self):
289+ """Ref counter is decreased when a signal is sent."""
290+ self.client.ref_count = 3
291+ self.client.CredentialsFound(APP_NAME, TOKEN)
292+
293+ self.assertEqual(self.client.ref_count, 2)
294+
295+ def test_credentials_not_found(self):
296+ """Ref counter is decreased when a signal is sent."""
297+ self.client.ref_count = 3
298+ self.client.CredentialsNotFound(APP_NAME)
299+
300+ self.assertEqual(self.client.ref_count, 2)
301+
302+ def test_credentials_cleared(self):
303+ """Ref counter is decreased when a signal is sent."""
304+ self.client.ref_count = 3
305+ self.client.CredentialsCleared(APP_NAME)
306+
307+ self.assertEqual(self.client.ref_count, 2)
308+
309+ def test_credentials_stored(self):
310+ """Ref counter is decreased when a signal is sent."""
311+ self.client.ref_count = 3
312+ self.client.CredentialsStored(APP_NAME)
313+
314+ self.assertEqual(self.client.ref_count, 2)
315+
316+ def test_credentials_error(self):
317+ """Ref counter is decreased when a signal is sent."""
318+ self.client.ref_count = 3
319+ self.client.CredentialsError(APP_NAME, {'error_type': 'test'})
320+
321+ self.assertEqual(self.client.ref_count, 2)
322+
323+ def test_authorization_denied(self):
324+ """Ref counter is decreased when a signal is sent."""
325+ self.client.ref_count = 3
326+ self.client.AuthorizationDenied(APP_NAME)
327+
328+ self.assertEqual(self.client.ref_count, 2)
329+
330+ def test_credentials_found_when_ref_count_is_not_positive(self):
331+ """Ref counter is decreased when a signal is sent."""
332+ self.client._ref_count = -3
333+ self.client.CredentialsFound(APP_NAME, TOKEN)
334+
335+ self.assertEqual(self.client.ref_count, 0)
336+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
337+ self.assertTrue(self.memento.check_warning(msg))
338+
339+ def test_credentials_not_found_when_ref_count_is_not_positive(self):
340+ """Ref counter is decreased when a signal is sent."""
341+ self.client._ref_count = -3
342+ self.client.CredentialsNotFound(APP_NAME)
343+
344+ self.assertEqual(self.client.ref_count, 0)
345+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
346+ self.assertTrue(self.memento.check_warning(msg))
347+
348+ def test_credentials_cleared_when_ref_count_is_not_positive(self):
349+ """Ref counter is decreased when a signal is sent."""
350+ self.client._ref_count = -3
351+ self.client.CredentialsCleared(APP_NAME)
352+
353+ self.assertEqual(self.client.ref_count, 0)
354+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
355+ self.assertTrue(self.memento.check_warning(msg))
356+
357+ def test_credentials_stored_when_ref_count_is_not_positive(self):
358+ """Ref counter is decreased when a signal is sent."""
359+ self.client._ref_count = -3
360+ self.client.CredentialsStored(APP_NAME)
361+
362+ self.assertEqual(self.client.ref_count, 0)
363+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
364+ self.assertTrue(self.memento.check_warning(msg))
365+
366+ def test_credentials_error_when_ref_count_is_not_positive(self):
367+ """Ref counter is decreased when a signal is sent."""
368+ self.client._ref_count = -3
369+ self.client.CredentialsError(APP_NAME, {'error_type': 'test'})
370+
371+ self.assertEqual(self.client.ref_count, 0)
372+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
373+ self.assertTrue(self.memento.check_warning(msg))
374+
375+ def test_autorization_denied_when_ref_count_is_not_positive(self):
376+ """Ref counter is decreased when a signal is sent."""
377+ self.client._ref_count = -3
378+ self.client.AuthorizationDenied(APP_NAME)
379+
380+ self.assertEqual(self.client.ref_count, 0)
381+ msg = 'Attempting to decrease ref_count to a negative value (-4).'
382+ self.assertTrue(self.memento.check_warning(msg))
383+
384+ def test_on_zero_ref_count_shutdown(self):
385+ """When ref count reaches 0, queue shutdown op."""
386+ self.client.timeout_func = self._set_called
387+ self.client.find_credentials(APP_NAME, self.args)
388+ self.client.CredentialsFound(APP_NAME, TOKEN)
389+
390+ self.assertEqual(self._called,
391+ ((TIMEOUT_INTERVAL, self.client.shutdown_func), {}))
392+
393+ def test_on_non_zero_ref_count_do_not_shutdown(self):
394+ """If ref count is not 0, do not queue shutdown op."""
395+ self.client.timeout_func = self._set_called
396+ self.client.find_credentials(APP_NAME, self.args)
397+
398+ self.assertEqual(self._called, False)
399+
400+
401 class CredentialsManagementFindTestCase(CredentialsManagementTestCase):
402 """Tests for the CredentialsManagement find method."""
403
404@@ -989,7 +1158,8 @@
405 """Tests for the CredentialsManagement DBus signals."""
406
407 def setUp(self):
408- self.client = CredentialsManagement()
409+ self.client = CredentialsManagement(timeout_func=lambda *a: None,
410+ shutdown_func=lambda *a: None)
411
412 self.memento = MementoHandler()
413 self.memento.setLevel(logging.DEBUG)

Subscribers

People subscribed via source and target branches