Merge lp:~canonical-platform-qa/uoa-integration-tests/python-testability into lp:uoa-integration-tests
- python-testability
- Merge into trunk
Proposed by
Leo Arias
Status: | Merged |
---|---|
Merged at revision: | 15 |
Proposed branch: | lp:~canonical-platform-qa/uoa-integration-tests/python-testability |
Merge into: | lp:uoa-integration-tests |
Diff against target: |
365 lines (+346/-0) 3 files modified
python/uoa_integration_tests/evernote.py (+41/-0) python/uoa_integration_tests/onlineaccounts.py (+233/-0) python/uoa_integration_tests/tests/test_evernote_online_accounts.py (+72/-0) |
To merge this branch: | bzr merge lp:~canonical-platform-qa/uoa-integration-tests/python-testability |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Online Accounts | Pending | ||
Review via email: mp+241815@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'python' | |||
2 | === added directory 'python/uoa_integration_tests' | |||
3 | === added file 'python/uoa_integration_tests/__init__.py' | |||
4 | === added file 'python/uoa_integration_tests/evernote.py' | |||
5 | --- python/uoa_integration_tests/evernote.py 1970-01-01 00:00:00 +0000 | |||
6 | +++ python/uoa_integration_tests/evernote.py 2014-11-14 14:59:44 +0000 | |||
7 | @@ -0,0 +1,41 @@ | |||
8 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
9 | 2 | # | ||
10 | 3 | # Copyright (C) 2014 Canonical Ltd. | ||
11 | 4 | # | ||
12 | 5 | # This program is free software; you can redistribute it and/or modify | ||
13 | 6 | # it under the terms of the GNU General Public License version 3, as published | ||
14 | 7 | # by the Free Software Foundation. | ||
15 | 8 | # | ||
16 | 9 | # This program is distributed in the hope that it will be useful, | ||
17 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | 12 | # GNU General Public License for more details. | ||
20 | 13 | # | ||
21 | 14 | # You should have received a copy of the GNU General Public License | ||
22 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
23 | 16 | |||
24 | 17 | import logging | ||
25 | 18 | |||
26 | 19 | from uoa_integration_tests import onlineaccounts | ||
27 | 20 | |||
28 | 21 | |||
29 | 22 | logger = logging.getLogger(__name__) | ||
30 | 23 | |||
31 | 24 | |||
32 | 25 | class EvernoteOnlineAccounts(): | ||
33 | 26 | |||
34 | 27 | @classmethod | ||
35 | 28 | def add_evernote_sandbox_account_with_oauth_token( | ||
36 | 29 | cls, user_name, password, oauth_token): | ||
37 | 30 | """Add an Evernote Sandbox account . | ||
38 | 31 | |||
39 | 32 | :param user_name: The user name of the account. | ||
40 | 33 | :param password: The password of the account. | ||
41 | 34 | :param oauth_token: The oauth token of the account. | ||
42 | 35 | |||
43 | 36 | """ | ||
44 | 37 | accounts_manager = onlineaccounts.AccountsManager() | ||
45 | 38 | provider_id = 'com.ubuntu.reminders_evernote-account-plugin-sandbox' | ||
46 | 39 | service_id = 'com.ubuntu.reminders_reminders-sandbox' | ||
47 | 40 | return accounts_manager.add_account_with_oauth_token( | ||
48 | 41 | provider_id, service_id, user_name, password, oauth_token) | ||
49 | 0 | 42 | ||
50 | === added file 'python/uoa_integration_tests/onlineaccounts.py' | |||
51 | --- python/uoa_integration_tests/onlineaccounts.py 1970-01-01 00:00:00 +0000 | |||
52 | +++ python/uoa_integration_tests/onlineaccounts.py 2014-11-14 14:59:44 +0000 | |||
53 | @@ -0,0 +1,233 @@ | |||
54 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
55 | 2 | # | ||
56 | 3 | # Copyright (C) 2014 Canonical Ltd. | ||
57 | 4 | # | ||
58 | 5 | # This program is free software; you can redistribute it and/or modify | ||
59 | 6 | # it under the terms of the GNU General Public License version 3, as published | ||
60 | 7 | # by the Free Software Foundation. | ||
61 | 8 | # | ||
62 | 9 | # This program is distributed in the hope that it will be useful, | ||
63 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
64 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
65 | 12 | # GNU General Public License for more details. | ||
66 | 13 | # | ||
67 | 14 | # You should have received a copy of the GNU General Public License | ||
68 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
69 | 16 | |||
70 | 17 | import logging | ||
71 | 18 | import time | ||
72 | 19 | import threading | ||
73 | 20 | |||
74 | 21 | from gi.repository import Accounts, GLib, Signon | ||
75 | 22 | |||
76 | 23 | |||
77 | 24 | logger = logging.getLogger(__name__) | ||
78 | 25 | |||
79 | 26 | |||
80 | 27 | class CredentialsException(Exception): | ||
81 | 28 | |||
82 | 29 | """Exception for credentials problems.""" | ||
83 | 30 | |||
84 | 31 | |||
85 | 32 | class AccountsManager(object): | ||
86 | 33 | |||
87 | 34 | """Manager for online accounts.""" | ||
88 | 35 | |||
89 | 36 | def __init__(self): | ||
90 | 37 | self._manager = Accounts.Manager.new() | ||
91 | 38 | |||
92 | 39 | def _start_main_loop(self): | ||
93 | 40 | self.error = None | ||
94 | 41 | self._main_loop = GLib.MainLoop() | ||
95 | 42 | self._main_loop_thread = threading.Thread( | ||
96 | 43 | target=self._main_loop.run) | ||
97 | 44 | self._main_loop_thread.start() | ||
98 | 45 | |||
99 | 46 | def _join_main_loop(self): | ||
100 | 47 | self._main_loop_thread.join() | ||
101 | 48 | if self.error is not None: | ||
102 | 49 | raise CredentialsException(self.error) | ||
103 | 50 | |||
104 | 51 | def add_account_with_oauth_token( | ||
105 | 52 | self, provider_id, service_id, user_name, password, oauth_token): | ||
106 | 53 | """Add an account with an oauth token. | ||
107 | 54 | |||
108 | 55 | :param provider_id: The identifier of the account provider. | ||
109 | 56 | :param servide_id: The identifier of the service that will use the | ||
110 | 57 | account. | ||
111 | 58 | :param user_name: The user name of the account. | ||
112 | 59 | :param password: The password of the account. | ||
113 | 60 | :param oauth_token: The oauth token of the account. | ||
114 | 61 | |||
115 | 62 | """ | ||
116 | 63 | logger.info('Add an account for provider {} and service {}.'.format( | ||
117 | 64 | provider_id, service_id)) | ||
118 | 65 | logger.info('user name: {}, password: {}, oauth_token: {}.'.format( | ||
119 | 66 | user_name, password, oauth_token)) | ||
120 | 67 | self._start_main_loop() | ||
121 | 68 | |||
122 | 69 | account = self._create_account(provider_id) | ||
123 | 70 | logger.debug('account %s' % account.get_settings_dict()) | ||
124 | 71 | |||
125 | 72 | if not account.get_settings_dict(): | ||
126 | 73 | self._delete_account_on_error(account) | ||
127 | 74 | self._quit_main_loop_on_error('account is blank', 'add account') | ||
128 | 75 | |||
129 | 76 | info = self._get_identity_info(user_name, password) | ||
130 | 77 | |||
131 | 78 | identity = Signon.Identity.new() | ||
132 | 79 | identity.store_credentials_with_info( | ||
133 | 80 | info, self._set_credentials_id_to_account, | ||
134 | 81 | {'account': account, 'oauth_token': oauth_token}) | ||
135 | 82 | logger.debug('identity %s' % identity.list_properties()) | ||
136 | 83 | |||
137 | 84 | logger.debug('Joining loop') | ||
138 | 85 | |||
139 | 86 | self._join_main_loop() | ||
140 | 87 | |||
141 | 88 | # XXX Sometimes, the account fails to be enabled. This sleep seems to | ||
142 | 89 | # fix it but we haven't yet found the reason. --elopio - 2014-06-25 | ||
143 | 90 | time.sleep(10) | ||
144 | 91 | self._enable_service(account, service_id) | ||
145 | 92 | |||
146 | 93 | logger.info('Created the account with id: {}.'.format(account.id)) | ||
147 | 94 | self._log_accounts_info() | ||
148 | 95 | return account | ||
149 | 96 | |||
150 | 97 | def _create_account(self, provider_id): | ||
151 | 98 | logger.debug('Creating the Evernote account.') | ||
152 | 99 | account = self._manager.create_account(provider_id) | ||
153 | 100 | account.set_enabled(True) | ||
154 | 101 | account.store(self._on_account_created, None) | ||
155 | 102 | return account | ||
156 | 103 | |||
157 | 104 | def _on_account_created(self, account, error, _): | ||
158 | 105 | if error: | ||
159 | 106 | self._quit_main_loop_on_error(error, 'storing account') | ||
160 | 107 | |||
161 | 108 | def _delete_account_on_error(self, account): | ||
162 | 109 | # attempt to clean up after ourselves, since normal | ||
163 | 110 | # addCleanups will not run in this case | ||
164 | 111 | try: | ||
165 | 112 | logger.debug('Cleaning up account %s' % account.id) | ||
166 | 113 | account.delete() | ||
167 | 114 | account.store(self._on_account_deleted, None) | ||
168 | 115 | except: | ||
169 | 116 | logger.warn('Failed to cleanup account') | ||
170 | 117 | |||
171 | 118 | def _quit_main_loop_on_error(self, error, step): | ||
172 | 119 | logger.error('Error {}.'.format(step)) | ||
173 | 120 | self.error = error | ||
174 | 121 | self._main_loop.quit() | ||
175 | 122 | raise CredentialsException(error) | ||
176 | 123 | |||
177 | 124 | def _get_identity_info(self, user_name, password): | ||
178 | 125 | info = Signon.IdentityInfo.new() | ||
179 | 126 | info.set_username(user_name) | ||
180 | 127 | info.set_caption(user_name) | ||
181 | 128 | info.set_secret(password, True) | ||
182 | 129 | return info | ||
183 | 130 | |||
184 | 131 | def _set_credentials_id_to_account( | ||
185 | 132 | self, identity, id_, error, account_dict): | ||
186 | 133 | if error: | ||
187 | 134 | self._quit_main_loop_on_error( | ||
188 | 135 | error, 'storing credentials with info') | ||
189 | 136 | |||
190 | 137 | logger.debug('Setting credentials to account.') | ||
191 | 138 | account = account_dict.get('account') | ||
192 | 139 | oauth_token = account_dict.get('oauth_token') | ||
193 | 140 | account.set_variant('CredentialsId', GLib.Variant('u', id_)) | ||
194 | 141 | account.store(self._process_session, oauth_token) | ||
195 | 142 | logger.debug('Account stored') | ||
196 | 143 | |||
197 | 144 | def _process_session(self, account, error, oauth_token): | ||
198 | 145 | if error: | ||
199 | 146 | self._quit_main_loop_on_error( | ||
200 | 147 | error, 'setting credentials id to account') | ||
201 | 148 | |||
202 | 149 | logger.debug('Processing session.') | ||
203 | 150 | account_service = Accounts.AccountService.new(account, None) | ||
204 | 151 | logger.debug('account_service %s' % account_service.list_properties()) | ||
205 | 152 | auth_data = account_service.get_auth_data() | ||
206 | 153 | logger.debug('auth_data %s' % auth_data.get_parameters()) | ||
207 | 154 | identity = auth_data.get_credentials_id() | ||
208 | 155 | method = auth_data.get_method() | ||
209 | 156 | if method is None: | ||
210 | 157 | self._delete_account_on_error(account) | ||
211 | 158 | self._quit_main_loop_on_error('Method is none', | ||
212 | 159 | 'processing auth data') | ||
213 | 160 | mechanism = auth_data.get_mechanism() | ||
214 | 161 | session_data = auth_data.get_parameters() | ||
215 | 162 | session_data['ProvidedTokens'] = GLib.Variant('a{sv}', { | ||
216 | 163 | 'TokenSecret': GLib.Variant('s', 'dummy'), | ||
217 | 164 | 'AccessToken': GLib.Variant('s', oauth_token), | ||
218 | 165 | }) | ||
219 | 166 | logger.debug('session_data %s' % session_data) | ||
220 | 167 | logger.debug('Authenticating session %s, %s' % (identity, method)) | ||
221 | 168 | session = Signon.AuthSession.new(identity, method) | ||
222 | 169 | logger.debug('Send session %s' % session) | ||
223 | 170 | session.process( | ||
224 | 171 | session_data, mechanism, self._on_login_processed, None) | ||
225 | 172 | logger.debug('Session processed') | ||
226 | 173 | |||
227 | 174 | def _on_login_processed(self, session, reply, error, userdata): | ||
228 | 175 | if error: | ||
229 | 176 | self._quit_main_loop_on_error(error, 'processing session') | ||
230 | 177 | |||
231 | 178 | else: | ||
232 | 179 | self._main_loop.quit() | ||
233 | 180 | |||
234 | 181 | def _enable_service(self, account, service_id): | ||
235 | 182 | logger.debug('Enabling evernote service.') | ||
236 | 183 | service = self._manager.get_service(service_id) | ||
237 | 184 | account.select_service(service) | ||
238 | 185 | account.set_enabled(True) | ||
239 | 186 | account.store(self._on_service_enabled, None) | ||
240 | 187 | |||
241 | 188 | def _on_service_enabled(self, account, error, _): | ||
242 | 189 | if error: | ||
243 | 190 | self._quit_main_loop_on_error(error, 'enabling service') | ||
244 | 191 | |||
245 | 192 | def _log_accounts_info(self): | ||
246 | 193 | account_ids = self._manager.list() | ||
247 | 194 | logger.debug('Existing accounts: {}.'.format(account_ids)) | ||
248 | 195 | for id_ in account_ids: | ||
249 | 196 | account = self._manager.get_account(id_) | ||
250 | 197 | self._log_account_info(account) | ||
251 | 198 | |||
252 | 199 | def _log_account_info(self, account): | ||
253 | 200 | logger.debug('Account info:') | ||
254 | 201 | logger.debug('id: {}'.format(account.id)) | ||
255 | 202 | logger.debug('provider: {}'.format(account.get_provider_name())) | ||
256 | 203 | logger.debug('enabled: {}'.format(account.get_enabled())) | ||
257 | 204 | logger.debug('Account services:') | ||
258 | 205 | for service in account.list_services(): | ||
259 | 206 | logger.debug('name: {}'.format(service.get_name())) | ||
260 | 207 | account_service = Accounts.AccountService.new(account, service) | ||
261 | 208 | logger.debug('enabled: {}'.format(account_service.get_enabled())) | ||
262 | 209 | |||
263 | 210 | def get_accounts_list(self): | ||
264 | 211 | return self._manager.list() | ||
265 | 212 | |||
266 | 213 | def delete_account(self, account): | ||
267 | 214 | """Delete an account. | ||
268 | 215 | |||
269 | 216 | :param account: The account to delete. | ||
270 | 217 | |||
271 | 218 | """ | ||
272 | 219 | logger.info('Deleting the account with id {}.'.format(account.id)) | ||
273 | 220 | self._start_main_loop() | ||
274 | 221 | # XXX There seems to be a problem when we try to delete the account too | ||
275 | 222 | # soon after starting the main loop. | ||
276 | 223 | # Reported as bug http://pad.lv/1363604. --elopio - 2014-09-01 | ||
277 | 224 | time.sleep(10) | ||
278 | 225 | account.delete() | ||
279 | 226 | account.store(self._on_account_deleted, None) | ||
280 | 227 | self._join_main_loop() | ||
281 | 228 | |||
282 | 229 | def _on_account_deleted(self, account, error, _): | ||
283 | 230 | if error: | ||
284 | 231 | self._quit_main_loop_on_error(error, 'deleting account') | ||
285 | 232 | else: | ||
286 | 233 | self._main_loop.quit() | ||
287 | 0 | 234 | ||
288 | === added directory 'python/uoa_integration_tests/tests' | |||
289 | === added file 'python/uoa_integration_tests/tests/__init__.py' | |||
290 | === added file 'python/uoa_integration_tests/tests/test_evernote_online_accounts.py' | |||
291 | --- python/uoa_integration_tests/tests/test_evernote_online_accounts.py 1970-01-01 00:00:00 +0000 | |||
292 | +++ python/uoa_integration_tests/tests/test_evernote_online_accounts.py 2014-11-14 14:59:44 +0000 | |||
293 | @@ -0,0 +1,72 @@ | |||
294 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
295 | 2 | # | ||
296 | 3 | # Copyright (C) 2014 Canonical Ltd | ||
297 | 4 | # | ||
298 | 5 | # This program is free software: you can redistribute it and/or modify | ||
299 | 6 | # it under the terms of the GNU General Public License version 3 as | ||
300 | 7 | # published by the Free Software Foundation. | ||
301 | 8 | # | ||
302 | 9 | # This program is distributed in the hope that it will be useful, | ||
303 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
304 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
305 | 12 | # GNU General Public License for more details. | ||
306 | 13 | # | ||
307 | 14 | # You should have received a copy of the GNU General Public License | ||
308 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
309 | 16 | |||
310 | 17 | import logging | ||
311 | 18 | |||
312 | 19 | import testtools | ||
313 | 20 | from gi.repository import Accounts | ||
314 | 21 | from testtools.matchers import HasLength | ||
315 | 22 | |||
316 | 23 | from uoa_integration_tests import onlineaccounts, evernote | ||
317 | 24 | |||
318 | 25 | |||
319 | 26 | logger = logging.getLogger(__name__) | ||
320 | 27 | |||
321 | 28 | |||
322 | 29 | class EvernoteCredentialsTestCase(testtools.TestCase): | ||
323 | 30 | |||
324 | 31 | def setUp(self): | ||
325 | 32 | super(EvernoteCredentialsTestCase, self).setUp() | ||
326 | 33 | self.accounts_manager = onlineaccounts.AccountsManager() | ||
327 | 34 | |||
328 | 35 | def add_evernote_sandbox_account(self): | ||
329 | 36 | accounts = evernote.EvernoteOnlineAccounts | ||
330 | 37 | add_account = accounts.add_evernote_sandbox_account_with_oauth_token | ||
331 | 38 | account = add_account('dummy', 'dummy', 'dummy') | ||
332 | 39 | self.addCleanup(self.delete_account, account) | ||
333 | 40 | return account | ||
334 | 41 | |||
335 | 42 | def delete_account(self, account): | ||
336 | 43 | if account.id in self.account_manager.get_accounts_list(): | ||
337 | 44 | self.accounts_manager.delete_account(account) | ||
338 | 45 | |||
339 | 46 | def test_add_evernote_sandbox_account_must_enable_it(self): | ||
340 | 47 | account = self.add_evernote_sandbox_account() | ||
341 | 48 | |||
342 | 49 | self.assertTrue(account.get_enabled()) | ||
343 | 50 | |||
344 | 51 | def test_add_evernote_sandbox_account_must_set_provider(self): | ||
345 | 52 | account = self.add_evernote_sandbox_account() | ||
346 | 53 | |||
347 | 54 | provider_id = 'com.ubuntu.reminders_evernote-account-plugin-sandbox' | ||
348 | 55 | self.assertEqual(account.get_provider_name(), provider_id) | ||
349 | 56 | |||
350 | 57 | def test_add_evernote_sandbox_account_must_enable_evernote_service(self): | ||
351 | 58 | account = self.add_evernote_sandbox_account() | ||
352 | 59 | services = account.list_services() | ||
353 | 60 | |||
354 | 61 | self.assertThat(services, HasLength(1)) | ||
355 | 62 | service_id = 'com.ubuntu.reminders_reminders-sandbox' | ||
356 | 63 | self.assertEqual(services[0].get_name(), service_id) | ||
357 | 64 | service = Accounts.AccountService.new(account, services[0]) | ||
358 | 65 | self.assertTrue(service.get_enabled()) | ||
359 | 66 | |||
360 | 67 | def test_delete_evernote_sandbox_account_must_remove_it(self): | ||
361 | 68 | account = self.add_evernote_sandbox_account() | ||
362 | 69 | self.assertThat(self.account_manager._manager.list(), HasLength(1)) | ||
363 | 70 | |||
364 | 71 | self.account_manager.delete_account(account) | ||
365 | 72 | self.assertThat(self.account_manager._manager.list(), HasLength(0)) |