Merge lp:~marcustomlinson/unity-scopes-api/online_accounts_tests into lp:unity-scopes-api

Proposed by Marcus Tomlinson
Status: Superseded
Proposed branch: lp:~marcustomlinson/unity-scopes-api/online_accounts_tests
Merge into: lp:unity-scopes-api
Prerequisite: lp:~marcustomlinson/unity-scopes-api/fix_ui_policy
Diff against target: 1558 lines (+1046/-146)
13 files modified
CMakeLists.txt (+1/-1)
HACKING (+22/-1)
RELEASE_NOTES.md (+5/-0)
debian/changelog (+7/-0)
debian/control (+3/-1)
include/unity/scopes/OnlineAccountClient.h (+15/-9)
include/unity/scopes/internal/OnlineAccountClientImpl.h (+24/-13)
src/scopes/internal/OnlineAccountClientImpl.cpp (+359/-113)
src/scopes/internal/RegistryObject.cpp (+11/-1)
test/gtest/scopes/OnlineAccountClient/CMakeLists.txt (+8/-0)
test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp (+544/-7)
test/gtest/scopes/OnlineAccountClient/data/TestService.service (+26/-0)
valgrind-suppress (+21/-0)
To merge this branch: bzr merge lp:~marcustomlinson/unity-scopes-api/online_accounts_tests
Reviewer Review Type Date Requested Status
Paweł Stołowski (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+237217@code.launchpad.net

This proposal has been superseded by a proposal from 2014-10-08.

Commit message

Added tests for OnlineAccountClient

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

LGTM.

review: Approve
277. By Marcus Tomlinson

Merged devel

278. By Marcus Tomlinson

Merged fix_ui_policy

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-09-11 08:35:01 +0000
3+++ CMakeLists.txt 2014-10-08 11:00:01 +0000
4@@ -195,7 +195,7 @@
5 # API version
6 set(UNITY_SCOPES_MAJOR 0)
7 set(UNITY_SCOPES_MINOR 6)
8-set(UNITY_SCOPES_MICRO 6)
9+set(UNITY_SCOPES_MICRO 7)
10 set(UNITY_SCOPES_SOVERSION 3)
11
12 # Version for testing, with all symbols visible
13
14=== modified file 'HACKING'
15--- HACKING 2014-09-08 22:15:40 +0000
16+++ HACKING 2014-10-08 11:00:01 +0000
17@@ -156,7 +156,28 @@
18 This creates a diff of the symbols in /tmp/symbols.diff.
19 (The demangled symbols from the debian build are in ./new_symbols.)
20
21-Review any changes in /tmp/symbols.diff. If they are OK:
22+NOTE: We currently have one architecture-specific symbol:
23+
24+(c++|arch=amd64)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0.6.6+14.10.20140916
25+
26+We need this because the copy-constructor is inlined on i386 and armhf.
27+
28+The above script can't cope with the arch tag at the moment. Until we get
29+around to fixing this, you need to delete the corresponding change from
30+the diff output before applying the diff as a patch. The offending diff
31+will look something like this:
32+
33+@@ -377,7 +378,7 @@
34+ (c++)"unity::scopes::internal::RegistryObject::remove_desktop_file(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.6.6+14.10.20140916
35+ (c++)"unity::scopes::internal::RegistryObject::remove_local_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
36+ (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::~ScopeExecData()@Base" 0.4.2+14.04.20140404.2
37+- (c++|arch=amd64)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0.6.6+14.10.20140916
38++ (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0replaceme
39+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::clear_handle_unlocked()@Base" 0.4.2+14.04.20140404.2
40+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::exec(core::posix::ChildProcess::DeathObserver&, std::shared_ptr<unity::scopes::internal::Executor>)@Base" 0.4.3+14.10.20140428
41+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::expand_custom_exec()@Base" 0.5.0+14.10.20140619
42+
43+Review any other changes in /tmp/symbols.diff. If they are OK:
44
45 $ cd -
46 $ patch -p0 < /tmp/symbols.diff
47
48=== modified file 'RELEASE_NOTES.md'
49--- RELEASE_NOTES.md 2014-09-11 08:42:38 +0000
50+++ RELEASE_NOTES.md 2014-10-08 11:00:01 +0000
51@@ -1,6 +1,11 @@
52 Release notes
53 =============
54
55+Changes in version 0.6.7
56+========================
57+ - Added new RunInExternalUiMainLoop option to OnlineAccountClient::MainLoopSelect.
58+ - OnlineAccountClient signon UI policy determined by main loop used (shell: show / scope: hide).
59+
60 Changes in version 0.6.6
61 ========================
62 - Added support for online accounts (via new OnlineAccountClient class).
63
64=== modified file 'debian/changelog'
65--- debian/changelog 2014-10-06 12:03:20 +0000
66+++ debian/changelog 2014-10-08 11:00:01 +0000
67@@ -1,3 +1,10 @@
68+unity-scopes-api (0.6.7-0ubuntu1) UNRELEASED; urgency=medium
69+
70+ * Added new RunInExternalUiMainLoop option to OnlineAccountClient::MainLoopSelect.
71+ * OnlineAccountClient signon UI policy determined by main loop used (shell: show / scope: hide).
72+
73+ -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Wed, 01 Oct 2014 15:46:30 +0200
74+
75 unity-scopes-api (0.6.6+14.10.20141006-0ubuntu1) utopic; urgency=medium
76
77 [ Pawel Stolowski ]
78
79=== modified file 'debian/control'
80--- debian/control 2014-09-23 08:33:51 +0000
81+++ debian/control 2014-10-08 11:00:01 +0000
82@@ -26,6 +26,8 @@
83 libjsoncpp-dev,
84 libaccounts-glib-dev,
85 libsignon-glib-dev,
86+ dbus-test-runner,
87+ libdbustest1-dev,
88 valgrind,
89 Standards-Version: 3.9.5
90 XS-Testsuite: autopkgtest
91@@ -81,4 +83,4 @@
92 Multi-Arch: foreign
93 Depends: ${misc:Depends},
94 Description: Documentation for Unity scopes API
95- Library to integrate scopes with the Unity shell (documentation)
96\ No newline at end of file
97+ Library to integrate scopes with the Unity shell (documentation)
98
99=== modified file 'include/unity/scopes/OnlineAccountClient.h'
100--- include/unity/scopes/OnlineAccountClient.h 2014-09-15 05:10:17 +0000
101+++ include/unity/scopes/OnlineAccountClient.h 2014-10-08 11:00:01 +0000
102@@ -37,6 +37,11 @@
103 class OnlineAccountClientImpl;
104 }
105
106+namespace testing
107+{
108+class OnlineAccountClientTest;
109+}
110+
111 /**
112 \brief A simple interface for integrating online accounts access and monitoring into scopes.
113
114@@ -55,7 +60,7 @@
115 */
116 struct ServiceStatus
117 {
118- uint account_id; ///< A unique ID of the online account parenting this service.
119+ int account_id; ///< A unique ID of the online account parenting this service.
120 bool service_enabled; ///< True if this service is enabled.
121 bool service_authenticated; ///< True if this service is authenticated.
122 std::string client_id; ///< "ConsumerKey" / "ClientId" OAuth (1 / 2) parameter.
123@@ -68,16 +73,15 @@
124 /**
125 \brief Indicates whether an external main loop already exists, or one should be created internally.
126
127- A running main loop is essential in order to receive service update callbacks. When in doubt, set
128- RunInExternalMainLoop and just use refresh_service_statuses() and get_service_statuses() to obtain
129- service statuses.
130+ A running main loop is essential in order to receive service updates from the online accounts
131+ backend. When in doubt, set to CreateInternalMainLoop.
132 */
133 enum MainLoopSelect
134 {
135- RunInExternalMainLoop, ///< An external main loop already exists or the service update
136- /// callback is not required.
137- CreateInternalMainLoop ///< An external main loop does not exist and the service update
138- /// callback is required.
139+ RunInExternalMainLoop, ///< An external main loop already exists and is running.
140+ CreateInternalMainLoop, ///< An external main loop does not exist.
141+ RunInExternalUiMainLoop, ///< An external UI main loop exists and is running (Intended for
142+ /// shell use only. A scope should not be running a UI main loop).
143 };
144
145 /**
146@@ -91,7 +95,7 @@
147 OnlineAccountClient(std::string const& service_name,
148 std::string const& service_type,
149 std::string const& provider_name,
150- MainLoopSelect main_loop_select = RunInExternalMainLoop);
151+ MainLoopSelect main_loop_select = CreateInternalMainLoop);
152
153 /// @cond
154 ~OnlineAccountClient();
155@@ -160,6 +164,8 @@
156
157 private:
158 std::unique_ptr<internal::OnlineAccountClientImpl> p;
159+
160+ friend class testing::OnlineAccountClientTest;
161 };
162
163 } // namespace scopes
164
165=== modified file 'include/unity/scopes/internal/OnlineAccountClientImpl.h'
166--- include/unity/scopes/internal/OnlineAccountClientImpl.h 2014-09-11 11:29:29 +0000
167+++ include/unity/scopes/internal/OnlineAccountClientImpl.h 2014-10-08 11:00:01 +0000
168@@ -19,6 +19,7 @@
169 #ifndef UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H
170 #define UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H
171
172+#include <unity/scopes/internal/MiddlewareBase.h>
173 #include <unity/scopes/OnlineAccountClient.h>
174
175 #include <libaccounts-glib/accounts-glib.h>
176@@ -78,42 +79,52 @@
177 OnlineAccountClient::PostLoginAction login_failed_action);
178
179 // Methods used only by impl
180- void flush_pending_sessions();
181+ void construct();
182+ void tear_down();
183+
184+ void flush_pending_session(AgAccountId const& account_id, std::unique_lock<std::mutex>& lock);
185
186 void main_loop_state_notify(bool is_running);
187
188+ std::shared_ptr<AgManager> manager();
189 std::string service_name();
190- void callback(OnlineAccountClient::ServiceStatus const& service_status);
191+ OnlineAccountClient::MainLoopSelect main_loop_select();
192+ std::shared_ptr<GMainContext> main_loop_context();
193+
194+ void callback(AccountInfo const* info, std::string const& error = "");
195
196 bool has_account(AgAccountId const& account_id);
197 void add_account(AgAccountId const& account_id, std::shared_ptr<AccountInfo> account_info);
198 void remove_account(AgAccountId const& account_id);
199
200- bool inc_logins();
201- void dec_logins();
202-
203 private:
204- std::string service_name_;
205- std::string service_type_;
206- std::string provider_name_;
207+ std::string const service_name_;
208+ std::string const service_type_;
209+ std::string const provider_name_;
210+ OnlineAccountClient::MainLoopSelect const main_loop_select_;
211+
212+ std::mutex callback_mutex_;
213 OnlineAccountClient::ServiceUpdateCallback callback_;
214- bool use_external_main_loop_;
215
216- std::thread main_loop_thread_;
217- std::mutex callback_mutex_;
218 std::mutex mutex_;
219 std::condition_variable cond_;
220+ std::thread main_loop_thread_;
221 bool main_loop_is_running_;
222- bool client_stopping_;
223- int logins_busy_;
224+ std::exception_ptr thread_exception_;
225 gulong account_enabled_signal_id_;
226 gulong account_deleted_signal_id_;
227
228 std::shared_ptr<GMainLoop> main_loop_;
229+ std::shared_ptr<GMainContext> main_loop_context_;
230 std::shared_ptr<AgManager> manager_;
231 std::map<AgAccountId, std::shared_ptr<AccountInfo>> accounts_;
232
233+ MiddlewareBase::SPtr mw_;
234+ MWPublisher::UPtr auth_publisher_;
235+ MWSubscriber::UPtr auth_subscriber_;
236+
237 void main_loop_thread();
238+ void auth_callback(std::string const& details_json);
239 };
240
241 } // namespace internal
242
243=== modified file 'src/scopes/internal/OnlineAccountClientImpl.cpp'
244--- src/scopes/internal/OnlineAccountClientImpl.cpp 2014-09-15 07:06:51 +0000
245+++ src/scopes/internal/OnlineAccountClientImpl.cpp 2014-10-08 11:00:01 +0000
246@@ -17,7 +17,12 @@
247 */
248
249 #include <unity/scopes/internal/OnlineAccountClientImpl.h>
250-#include <unity/util/ResourcePtr.h>
251+
252+#include <unity/scopes/internal/JsonCppNode.h>
253+#include <unity/scopes/internal/MiddlewareFactory.h>
254+#include <unity/scopes/internal/RuntimeConfig.h>
255+#include <unity/scopes/internal/RuntimeImpl.h>
256+#include <unity/UnityExceptions.h>
257
258 #include <iostream>
259
260@@ -88,7 +93,41 @@
261 return service_status;
262 }
263
264-static void clear_session_info(AccountInfo* info)
265+static std::string details_to_json(OnlineAccountClient::ServiceStatus const& details)
266+{
267+ VariantMap vm;
268+ vm["account_id"] = details.account_id;
269+ vm["service_enabled"] = details.service_enabled;
270+ vm["service_authenticated"] = details.service_authenticated;
271+ vm["client_id"] = details.client_id;
272+ vm["client_secret"] = details.client_secret;
273+ vm["access_token"] = details.access_token;
274+ vm["token_secret"] = details.token_secret;
275+ vm["error"] = details.error;
276+
277+ Variant var(vm);
278+ JsonCppNode node(var);
279+ return node.to_json_string();
280+}
281+
282+static OnlineAccountClient::ServiceStatus json_to_details(std::string const& json)
283+{
284+ OnlineAccountClient::ServiceStatus details;
285+ JsonCppNode node(json);
286+
287+ details.account_id = node.get_node("account_id")->as_int();
288+ details.service_enabled = node.get_node("service_enabled")->as_bool();
289+ details.service_authenticated = node.get_node("service_authenticated")->as_bool();
290+ details.client_id = node.get_node("client_id")->as_string();
291+ details.client_secret = node.get_node("client_secret")->as_string();
292+ details.access_token = node.get_node("access_token")->as_string();
293+ details.token_secret = node.get_node("token_secret")->as_string();
294+ details.error = node.get_node("error")->as_string();
295+
296+ return details;
297+}
298+
299+static void clear_session(AccountInfo* info)
300 {
301 if (info->session)
302 {
303@@ -96,8 +135,6 @@
304 signon_auth_session_cancel(info->session.get());
305 }
306 info->session = nullptr;
307- info->auth_params = nullptr;
308- info->session_data = nullptr;
309 }
310
311 static gboolean main_loop_is_running_cb(void* user_data)
312@@ -107,6 +144,13 @@
313 return G_SOURCE_REMOVE;
314 }
315
316+static gboolean main_loop_is_stopping_cb(void* user_data)
317+{
318+ OnlineAccountClientImpl* account_client = reinterpret_cast<OnlineAccountClientImpl*>(user_data);
319+ account_client->main_loop_state_notify(false);
320+ return G_SOURCE_REMOVE;
321+}
322+
323 static gboolean wake_up_event_loop_cb(void* user_data)
324 {
325 GMainLoop* event_loop = reinterpret_cast<GMainLoop*>(user_data);
326@@ -116,58 +160,75 @@
327
328 static void service_login_cb(GObject* source, GAsyncResult* result, void* user_data)
329 {
330- GError* error = nullptr;
331- util::ResourcePtr<GError*, decltype(&g_error_free)> error_cleanup(error, free_error);
332-
333 SignonAuthSession* session = reinterpret_cast<SignonAuthSession*>(source);
334 AccountInfo* info = reinterpret_cast<AccountInfo*>(user_data);
335
336 // Get session data then send a notification with the login result
337+ GError* error = nullptr;
338 info->session_data.reset(signon_auth_session_process_finish(session, result, &error), free_variant);
339- info->account_client->callback(info_to_details(info, error ? error->message : ""));
340- info->account_client->dec_logins();
341+ std::shared_ptr<GError> error_cleanup(error, free_error);
342+
343+ info->account_client->callback(info, error ? error->message : "");
344+
345+ // Clear session info
346+ clear_session(info);
347 }
348
349 static void service_update_cb(AgAccountService* account_service, gboolean enabled, AccountInfo* info)
350 {
351- GError* error = nullptr;
352- util::ResourcePtr<GError*, decltype(&g_error_free)> error_cleanup(error, free_error);
353-
354 // Service state has updated, clear the old session data
355 info->service_enabled = enabled;
356- clear_session_info(info);
357+ clear_session(info);
358
359 if (enabled)
360 {
361 // Get authorization data then create a new authorization session
362 std::shared_ptr<AgAuthData> auth_data(ag_account_service_get_auth_data(account_service), ag_auth_data_unref);
363+
364+ GError* error = nullptr;
365 info->session.reset(signon_auth_session_new(
366 ag_auth_data_get_credentials_id(auth_data.get()), ag_auth_data_get_method(auth_data.get()), &error), g_object_unref);
367+ std::shared_ptr<GError> error_cleanup(error, free_error);
368+
369 if (error)
370 {
371 // Send notification that the authorization session failed
372- info->account_client->callback(info_to_details(info, error->message));
373+ info->account_client->callback(info, error->message); // LCOV_EXCL_LINE
374 return;
375 }
376
377 // Get authorization parameters then attempt to signon
378+ GVariantBuilder builder;
379+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
380+
381+ if (info->account_client->main_loop_select() == OnlineAccountClient::RunInExternalUiMainLoop)
382+ {
383+ g_variant_builder_add(&builder, "{sv}",
384+ SIGNON_SESSION_DATA_UI_POLICY,
385+ g_variant_new_int32(SIGNON_POLICY_DEFAULT)); // LCOV_EXCL_LINE
386+ }
387+ else
388+ {
389+ g_variant_builder_add(&builder, "{sv}",
390+ SIGNON_SESSION_DATA_UI_POLICY,
391+ g_variant_new_int32(SIGNON_POLICY_NO_USER_INTERACTION));
392+ }
393+
394 info->auth_params.reset(
395- g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), nullptr)), free_variant);
396+ g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), g_variant_builder_end(&builder))), free_variant);
397
398- if (info->account_client->inc_logins())
399- {
400- signon_auth_session_process_async(info->session.get(),
401- info->auth_params.get(),
402- ag_auth_data_get_mechanism(auth_data.get()),
403- nullptr,
404- service_login_cb,
405- info);
406- }
407+ // Start signon process
408+ signon_auth_session_process_async(info->session.get(),
409+ info->auth_params.get(),
410+ ag_auth_data_get_mechanism(auth_data.get()),
411+ nullptr,
412+ service_login_cb,
413+ info);
414 }
415 else
416 {
417 // Send notification that account has been disabled
418- info->account_client->callback(info_to_details(info));
419+ info->account_client->callback(info);
420 }
421 }
422
423@@ -182,7 +243,7 @@
424 if (!account)
425 {
426 // The account was not found
427- return;
428+ return; // LCOV_EXCL_LINE
429 }
430 // Find the service we're concerned with
431 std::shared_ptr<GList> services(ag_account_list_services(account.get()), ag_service_list_free);
432@@ -193,7 +254,7 @@
433 if (account_client->service_name() == ag_service_get_name(service))
434 {
435 std::shared_ptr<AgAccountService> account_service(ag_account_service_new(account.get(), service), g_object_unref);
436- std::shared_ptr<AccountInfo> info(new AccountInfo, [](AccountInfo* info){ info->account_client = nullptr; delete info; });
437+ std::shared_ptr<AccountInfo> info(new AccountInfo);
438 info->service_enabled = false;
439 info->account_client = account_client;
440 info->account_service.reset(reinterpret_cast<AgAccountService*>(g_object_ref(account_service.get())), g_object_unref);
441@@ -226,51 +287,99 @@
442 : service_name_(service_name)
443 , service_type_(service_type)
444 , provider_name_(provider_name)
445- , use_external_main_loop_(main_loop_select == OnlineAccountClient::RunInExternalMainLoop)
446- , main_loop_is_running_(false)
447- , client_stopping_(false)
448- , logins_busy_(0)
449+ , main_loop_select_(main_loop_select)
450+ , main_loop_is_running_(main_loop_select != OnlineAccountClient::CreateInternalMainLoop)
451 {
452+ // Set up authentication pub/sub
453+ std::string oa_id = provider_name + ":" + service_type + ":" + service_name;
454+ RuntimeImpl::UPtr rt = RuntimeImpl::create(oa_id);
455+ MiddlewareFactory mw_factory(rt.get());
456+ RuntimeConfig rt_config("");
457+
458+ mw_ = mw_factory.create(oa_id, rt_config.default_middleware(), rt_config.default_middleware_configfile());
459+ if (main_loop_select == OnlineAccountClient::RunInExternalUiMainLoop)
460+ {
461+ // If this client was created by the shell, set up as the publisher
462+ auth_publisher_ = mw_->create_publisher(oa_id);
463+ }
464+ else
465+ {
466+ // If this client was created by a scope, set up as a subscriber
467+ auth_subscriber_ = mw_->create_subscriber(oa_id);
468+ auth_subscriber_->message_received().connect(std::bind(&OnlineAccountClientImpl::auth_callback, this, std::placeholders::_1));
469+ }
470+
471 // If we are responsible for the main loop
472- if (!use_external_main_loop_)
473+ if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
474 {
475 // Wait here until either the main loop begins running, or the thread exits
476- std::unique_lock<std::mutex> lock(mutex_);
477- main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);
478- cond_.wait(lock, [this] { return main_loop_is_running_; });
479- }
480-
481- manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);
482-
483- // Watch for changes to accounts
484- account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);
485- account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);
486-
487- // Now check initial state
488- refresh_service_statuses();
489+ {
490+ std::unique_lock<std::mutex> lock(mutex_);
491+ main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);
492+ cond_.wait_for(lock, std::chrono::seconds(5), [this] { return main_loop_is_running_; });
493+ }
494+ if (!main_loop_is_running_)
495+ {
496+ // LCOV_EXCL_START
497+ if (main_loop_)
498+ {
499+ // Quit the main loop, causing the thread to exit
500+ g_main_loop_quit(main_loop_.get());
501+ if (main_loop_thread_.joinable())
502+ {
503+ main_loop_thread_.join();
504+ }
505+ }
506+ if (thread_exception_)
507+ {
508+ std::rethrow_exception(thread_exception_);
509+ }
510+ else
511+ {
512+ throw unity::ResourceException("OnlineAccountClientImpl(): main_loop_thread failed to start.");
513+ }
514+ // LCOV_EXCL_STOP
515+ }
516+ }
517+ else
518+ {
519+ construct();
520+ }
521 }
522
523 OnlineAccountClientImpl::~OnlineAccountClientImpl()
524 {
525- // Disconnect all signal handlers
526- g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);
527- g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);
528-
529- for (auto info : accounts_)
530- {
531- g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);
532- }
533-
534- // Wait until all currently running login sessions are done
535- {
536- std::unique_lock<std::mutex> lock(mutex_);
537- client_stopping_ = true;
538- }
539- flush_pending_sessions();
540-
541- // If we are responsible for the main loop, quit it on destruction
542- if (main_loop_)
543- {
544+ {
545+ std::lock_guard<std::mutex> lock(mutex_);
546+ if (thread_exception_)
547+ {
548+ // LCOV_EXCL_START
549+ try
550+ {
551+ std::rethrow_exception(thread_exception_);
552+ }
553+ catch (std::exception const& e)
554+ {
555+ std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an exception: " << e.what() << std::endl;
556+ }
557+ catch (...)
558+ {
559+ std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an unknown exception" << std::endl;
560+ }
561+ // LCOV_EXCL_STOP
562+ }
563+ }
564+
565+ // If we are responsible for the main loop
566+ if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
567+ {
568+ // Invoke tear_down() from within the main loop
569+ {
570+ std::unique_lock<std::mutex> lock(mutex_);
571+ g_main_context_invoke(main_loop_context_.get(), main_loop_is_stopping_cb, this);
572+ cond_.wait(lock, [this] { return !main_loop_is_running_; });
573+ }
574+
575 // Quit the main loop, causing the thread to exit
576 g_main_loop_quit(main_loop_.get());
577 if (main_loop_thread_.joinable())
578@@ -278,16 +387,32 @@
579 main_loop_thread_.join();
580 }
581 }
582+ else
583+ {
584+ tear_down();
585+ }
586 }
587
588 void OnlineAccountClientImpl::set_service_update_callback(OnlineAccountClient::ServiceUpdateCallback callback)
589 {
590- std::lock_guard<std::mutex> lock(callback_mutex_);
591+ std::lock_guard<std::mutex> lock(mutex_);
592+ if (thread_exception_)
593+ {
594+ std::rethrow_exception(thread_exception_); // LCOV_EXCL_LINE
595+ }
596+
597+ std::lock_guard<std::mutex> callback_lock(callback_mutex_);
598 callback_ = callback;
599 }
600
601 void OnlineAccountClientImpl::refresh_service_statuses()
602 {
603+ std::unique_lock<std::mutex> lock(mutex_);
604+ if (thread_exception_)
605+ {
606+ std::rethrow_exception(thread_exception_); // LCOV_EXCL_LINE
607+ }
608+
609 std::shared_ptr<GList> enabled_accounts(ag_manager_list(manager_.get()), ag_manager_list_free);
610 GList* it;
611 for (it = enabled_accounts.get(); it; it = it->next)
612@@ -297,19 +422,25 @@
613 std::string provider_name = ag_account_get_provider_name(account.get());
614 if (provider_name == provider_name_)
615 {
616+ lock.unlock();
617 account_enabled_cb(manager_.get(), account_id, this);
618+ lock.lock();
619 }
620+ flush_pending_session(account_id, lock);
621 }
622- flush_pending_sessions();
623 }
624
625 std::vector<OnlineAccountClient::ServiceStatus> OnlineAccountClientImpl::get_service_statuses()
626 {
627 std::lock_guard<std::mutex> lock(mutex_);
628+ if (thread_exception_)
629+ {
630+ std::rethrow_exception(thread_exception_); // LCOV_EXCL_LINE
631+ }
632
633 // Return all service statuses
634 std::vector<OnlineAccountClient::ServiceStatus> service_statuses;
635- for (auto const info : accounts_)
636+ for (auto const& info : accounts_)
637 {
638 service_statuses.push_back(info_to_details(info.second.get()));
639 }
640@@ -352,42 +483,117 @@
641 widget.add_attribute_value("online_account_details", Variant(account_details_map));
642 }
643
644-void OnlineAccountClientImpl::flush_pending_sessions()
645-{
646+void OnlineAccountClientImpl::construct()
647+{
648+ manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);
649+
650+ // Watch for changes to accounts
651+ account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);
652+ account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);
653+
654+ // Now check initial state
655+ refresh_service_statuses();
656+}
657+
658+void OnlineAccountClientImpl::tear_down()
659+{
660+ // Disconnect signal handlers
661+ g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);
662+ g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);
663+
664+ // Remove all accounts
665+ {
666+ std::unique_lock<std::mutex> lock(mutex_);
667+ for (auto const& info : accounts_)
668+ {
669+ // Before we nuke the map, ensure that any pending sessions are done
670+ g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);
671+ clear_session(info.second.get());
672+ flush_pending_session(info.second->account_id, lock);
673+ }
674+ accounts_.clear();
675+ }
676+}
677+
678+void OnlineAccountClientImpl::flush_pending_session(AgAccountId const& account_id, std::unique_lock<std::mutex>& lock)
679+{
680+ // Get account info
681+ auto info_it = accounts_.find(account_id);
682+ if (info_it == accounts_.end())
683+ {
684+ return;
685+ }
686+ auto info = info_it->second;
687+
688 // Wait until all currently running login sessions are done
689 // (ensures that accounts_ is up to date)
690- std::unique_lock<std::mutex> lock(mutex_);
691+ std::shared_ptr<GMainLoop> event_loop;
692+ event_loop.reset(g_main_loop_new(g_main_context_get_thread_default(), true), g_main_loop_unref);
693+ while(info->session)
694 {
695- std::shared_ptr<GMainLoop> event_loop;
696- event_loop.reset(g_main_loop_new(nullptr, true), g_main_loop_unref);
697- while(logins_busy_ > 0)
698- {
699- // We need to wait inside an event loop to allow for the main application loop to
700- // process its pending events
701- g_timeout_add(100, wake_up_event_loop_cb, event_loop.get());
702- lock.unlock();
703- g_main_loop_run(event_loop.get());
704- lock.lock();
705- }
706+ // We need to wait inside an event loop to allow for the main application loop to
707+ // process its pending events
708+ std::shared_ptr<GSource> source;
709+ source.reset(g_timeout_source_new(10), g_source_unref);
710+ g_source_set_callback(source.get(), wake_up_event_loop_cb, event_loop.get(), NULL);
711+ g_source_attach(source.get(), g_main_context_get_thread_default());
712+
713+ lock.unlock();
714+ g_main_loop_run(event_loop.get());
715+ lock.lock();
716 }
717 }
718
719 void OnlineAccountClientImpl::main_loop_state_notify(bool is_running)
720 {
721+ bool was_running = false;
722+ {
723+ std::lock_guard<std::mutex> lock(mutex_);
724+ was_running = main_loop_is_running_;
725+ }
726+ if (!was_running && is_running)
727+ {
728+ construct();
729+ }
730+ else if (was_running && !is_running)
731+ {
732+ tear_down();
733+ }
734+
735 std::lock_guard<std::mutex> lock(mutex_);
736 main_loop_is_running_ = is_running;
737 cond_.notify_all();
738 }
739
740+std::shared_ptr<AgManager> OnlineAccountClientImpl::manager()
741+{
742+ return manager_;
743+}
744+
745 std::string OnlineAccountClientImpl::service_name()
746 {
747 std::lock_guard<std::mutex> lock(mutex_);
748 return service_name_;
749 }
750
751-void OnlineAccountClientImpl::callback(OnlineAccountClient::ServiceStatus const& service_status)
752+OnlineAccountClient::MainLoopSelect OnlineAccountClientImpl::main_loop_select()
753+{
754+ return main_loop_select_;
755+}
756+
757+std::shared_ptr<GMainContext> OnlineAccountClientImpl::main_loop_context()
758+{
759+ return main_loop_context_;
760+}
761+
762+void OnlineAccountClientImpl::callback(AccountInfo const* info, std::string const& error)
763 {
764 std::lock_guard<std::mutex> lock(callback_mutex_);
765+ auto service_status = info_to_details(info, error);
766+ if (service_status.service_authenticated && auth_publisher_)
767+ {
768+ auth_publisher_->send_message(details_to_json(service_status));
769+ }
770 if (callback_)
771 {
772 callback_(service_status);
773@@ -408,42 +614,82 @@
774
775 void OnlineAccountClientImpl::remove_account(AgAccountId const& account_id)
776 {
777- std::lock_guard<std::mutex> lock(mutex_);
778+ std::unique_lock<std::mutex> lock(mutex_);
779+
780+ // Get account info
781+ auto info_it = accounts_.find(account_id);
782+ if (info_it == accounts_.end())
783+ {
784+ return;
785+ }
786+ auto info = info_it->second;
787+
788+ // Before we nuke the pointer, ensure that any pending sessions are done
789+ g_signal_handler_disconnect(info->account_service.get(), info->service_update_signal_id_);
790+ clear_session(info.get());
791+ flush_pending_session(account_id, lock);
792+
793+ // Remove account info from accounts_ map
794 accounts_.erase(account_id);
795 }
796
797-bool OnlineAccountClientImpl::inc_logins()
798-{
799- std::lock_guard<std::mutex> lock(mutex_);
800- if (!client_stopping_)
801- {
802- // Increment number of logins busy
803- ++logins_busy_;
804- return true;
805- }
806- // Client is stopping so don't increment
807- return false;
808-}
809-
810-void OnlineAccountClientImpl::dec_logins()
811-{
812- // Decrement number of logins busy
813- std::lock_guard<std::mutex> lock(mutex_);
814- --logins_busy_;
815-}
816-
817 void OnlineAccountClientImpl::main_loop_thread()
818 {
819- // If something goes wrong causing the main loop not to run, the destruction of this pointer
820- // will break the constructor from its wait
821- std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(true); });
822-
823- // Stick a method call into the main loop to notify the constructor when the main loop begins running
824- g_idle_add(main_loop_is_running_cb, this);
825-
826- // Run the main loop
827- main_loop_.reset(g_main_loop_new(nullptr, true), g_main_loop_unref);
828- g_main_loop_run(main_loop_.get());
829+ // If something goes wrong causing the thread to abort, the destruction of this pointer update the
830+ // main_loop_is_running_ state accordingly.
831+ std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(false); });
832+
833+ try
834+ {
835+ main_loop_context_.reset(g_main_context_new(), g_main_context_unref);
836+ g_main_context_push_thread_default(main_loop_context_.get());
837+
838+ // Stick a method call into the main loop to notify the constructor when the main loop begins running
839+ g_main_context_invoke(main_loop_context_.get(), main_loop_is_running_cb, this);
840+
841+ // Run the main loop
842+ main_loop_.reset(g_main_loop_new(main_loop_context_.get(), true), g_main_loop_unref);
843+ g_main_loop_run(main_loop_.get());
844+ }
845+ // LCOV_EXCL_START
846+ catch (std::exception const& e)
847+ {
848+ std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: " << e.what() << std::endl;
849+ std::lock_guard<std::mutex> lock(mutex_);
850+ thread_exception_ = std::current_exception();
851+ }
852+ catch (...)
853+ {
854+ std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: unknown exception" << std::endl;
855+ std::lock_guard<std::mutex> lock(mutex_);
856+ thread_exception_ = std::current_exception();
857+ }
858+ // LCOV_EXCL_STOP
859+}
860+
861+void OnlineAccountClientImpl::auth_callback(std::string const& details_json)
862+{
863+ OnlineAccountClient::ServiceStatus details = json_to_details(details_json);
864+
865+ // Update account info
866+ {
867+ std::lock_guard<std::mutex> lock(mutex_);
868+ auto info_it = accounts_.find(details.account_id);
869+ if (info_it == accounts_.end())
870+ {
871+ return;
872+ }
873+ auto info = info_it->second;
874+
875+ GVariantDict dict;
876+ g_variant_dict_init(&dict, nullptr);
877+ g_variant_dict_insert(&dict, "AccessToken", "s", details.access_token.c_str());
878+ g_variant_dict_insert(&dict, "TokenSecret", "s", details.token_secret.c_str());
879+ info->session_data.reset(g_variant_ref_sink(g_variant_dict_end(&dict)), free_variant);
880+ }
881+
882+ std::lock_guard<std::mutex> lock(callback_mutex_);
883+ callback_(details);
884 }
885
886 } // namespace internal
887
888=== modified file 'src/scopes/internal/RegistryObject.cpp'
889--- src/scopes/internal/RegistryObject.cpp 2014-09-15 18:33:08 +0000
890+++ src/scopes/internal/RegistryObject.cpp 2014-10-08 11:00:01 +0000
891@@ -33,6 +33,7 @@
892 #include <cassert>
893 #include <fstream>
894 #include <wordexp.h>
895+#include <iostream>
896
897 using namespace std;
898
899@@ -385,7 +386,16 @@
900 desktop_file << "Type=Application" << std::endl;
901 desktop_file << "NoDisplay=true" << std::endl;
902 desktop_file << "Name=" << metadata.display_name() << std::endl;
903- desktop_file << "Icon=" << metadata.icon() << std::endl;
904+ desktop_file << "Icon=";
905+ try
906+ {
907+ desktop_file << metadata.icon();
908+ }
909+ catch (NotFoundException const&)
910+ {
911+ // Icon is optional.
912+ }
913+ desktop_file << endl;
914 desktop_file.close();
915 }
916
917
918=== modified file 'test/gtest/scopes/OnlineAccountClient/CMakeLists.txt'
919--- test/gtest/scopes/OnlineAccountClient/CMakeLists.txt 2014-09-10 08:45:38 +0000
920+++ test/gtest/scopes/OnlineAccountClient/CMakeLists.txt 2014-10-08 11:00:01 +0000
921@@ -1,4 +1,12 @@
922 add_executable(OnlineAccountClient_test OnlineAccountClient_test.cpp)
923+
924+pkg_check_modules(DBUSTEST REQUIRED dbustest-1)
925+include_directories(${DBUSTEST_INCLUDE_DIRS})
926+target_link_libraries(OnlineAccountClient_test ${DBUSTEST_LDFLAGS})
927+
928+add_definitions(-DTEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data")
929+add_definitions(-DTEST_DB_DIR="${CMAKE_CURRENT_BINARY_DIR}")
930+
931 target_link_libraries(OnlineAccountClient_test ${TESTLIBS})
932
933 add_test(OnlineAccountClient OnlineAccountClient_test)
934
935=== modified file 'test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp'
936--- test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2014-09-10 08:57:42 +0000
937+++ test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2014-10-08 11:00:01 +0000
938@@ -16,15 +16,552 @@
939 * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com>
940 */
941
942+#include <unity/scopes/OnlineAccountClient.h>
943+#include <unity/scopes/internal/OnlineAccountClientImpl.h>
944+
945+#include <unity/scopes/CategorisedResult.h>
946+#include <unity/scopes/CategoryRenderer.h>
947+#include <unity/scopes/internal/CategoryRegistry.h>
948+#include <unity/scopes/PreviewWidget.h>
949+#include <unity/scopes/Variant.h>
950+
951+#include <boost/filesystem/operations.hpp>
952+#include <condition_variable>
953+#include <functional>
954 #include <gtest/gtest.h>
955-#include <unity/scopes/OnlineAccountClient.h>
956-
957+#include <libaccounts-glib/accounts-glib.h>
958+#include <libdbustest/dbus-test.h>
959 #include <thread>
960
961+using namespace unity;
962 using namespace unity::scopes;
963-
964-TEST(OnlineAccountClient, basic)
965-{
966- OnlineAccountClient oa_client("com.ubuntu.scopes.youtube_youtube", "sharing", "google");
967- auto service_statuses = oa_client.get_service_statuses();
968+using namespace unity::scopes::testing;
969+using namespace unity::scopes::internal;
970+
971+namespace unity
972+{
973+namespace scopes
974+{
975+namespace testing
976+{
977+
978+class OnlineAccountClientTest : public ::testing::Test
979+{
980+public:
981+ OnlineAccountClientTest(OnlineAccountClient::MainLoopSelect main_loop_select = OnlineAccountClient::CreateInternalMainLoop)
982+ {
983+ boost::filesystem::remove(TEST_DB_DIR "/accounts.db");
984+
985+ setenv("XDG_RUNTIME_DIR", "/tmp", true);
986+ setenv("ACCOUNTS", TEST_DB_DIR, false);
987+ setenv("AG_SERVICES", TEST_DATA_DIR, false);
988+ setenv("AG_SERVICE_TYPES", TEST_DATA_DIR, false);
989+ setenv("AG_PROVIDERS", TEST_DATA_DIR, false);
990+
991+ oa_client_.reset(new OnlineAccountClient("TestService", "sharing", "TestProvider", main_loop_select));
992+
993+ manager_ = oa_client_->p->manager();
994+ main_loop_context_ = oa_client_->p->main_loop_context();
995+ }
996+
997+ ~OnlineAccountClientTest()
998+ {
999+ account_ = nullptr;
1000+ manager_ = nullptr;
1001+ oa_client_ = nullptr;
1002+ }
1003+
1004+ std::shared_ptr<OnlineAccountClient> oa_client()
1005+ {
1006+ return oa_client_;
1007+ }
1008+
1009+ void create_account()
1010+ {
1011+ run_in_main_loop_(std::bind(&OnlineAccountClientTest::create_account_, this));
1012+ }
1013+
1014+ void delete_account()
1015+ {
1016+ run_in_main_loop_(std::bind(&OnlineAccountClientTest::delete_account_, this));
1017+ }
1018+
1019+ void enable_service()
1020+ {
1021+ run_in_main_loop_(std::bind(&OnlineAccountClientTest::enable_service_, this));
1022+ }
1023+
1024+ void disable_service()
1025+ {
1026+ run_in_main_loop_(std::bind(&OnlineAccountClientTest::disable_service_, this));
1027+ }
1028+
1029+ void disable_account()
1030+ {
1031+ run_in_main_loop_(std::bind(&OnlineAccountClientTest::disable_account_, this));
1032+ }
1033+
1034+ void enable_account()
1035+ {
1036+ run_in_main_loop_(std::bind(&OnlineAccountClientTest::enable_account_, this));
1037+ }
1038+
1039+ void service_update_enabled(OnlineAccountClient::ServiceStatus const& status)
1040+ {
1041+ EXPECT_EQ(1, status.account_id);
1042+ EXPECT_EQ(true, status.service_enabled);
1043+ EXPECT_EQ(false, status.service_authenticated);
1044+ EXPECT_EQ("69842936499-sdflkbhslufhgrjamwlicefhb.apps.test.com", status.client_id);
1045+ EXPECT_EQ("lj3i8iorep0w03994jwjef0j", status.client_secret);
1046+ EXPECT_EQ("", status.access_token);
1047+ EXPECT_EQ("", status.token_secret);
1048+ EXPECT_NE("", status.error);
1049+
1050+ std::lock_guard<std::mutex> lock(mutex_);
1051+ got_update_ = true;
1052+ cond_.notify_all();
1053+ }
1054+
1055+ void service_update_disabled(OnlineAccountClient::ServiceStatus const& status)
1056+ {
1057+ EXPECT_EQ(1, status.account_id);
1058+ EXPECT_EQ(false, status.service_enabled);
1059+ EXPECT_EQ(false, status.service_authenticated);
1060+ EXPECT_EQ("69842936499-sdflkbhslufhgrjamwlicefhb.apps.test.com", status.client_id);
1061+ EXPECT_EQ("lj3i8iorep0w03994jwjef0j", status.client_secret);
1062+ EXPECT_EQ("", status.access_token);
1063+ EXPECT_EQ("", status.token_secret);
1064+ EXPECT_EQ("", status.error);
1065+
1066+ std::lock_guard<std::mutex> lock(mutex_);
1067+ got_update_ = true;
1068+ cond_.notify_all();
1069+ }
1070+
1071+ void service_update_none(OnlineAccountClient::ServiceStatus const& status)
1072+ {
1073+ EXPECT_EQ(1, status.account_id);
1074+ EXPECT_EQ(false, status.service_enabled);
1075+ EXPECT_EQ(false, status.service_authenticated);
1076+ EXPECT_EQ("", status.client_id);
1077+ EXPECT_EQ("", status.client_secret);
1078+ EXPECT_EQ("", status.access_token);
1079+ EXPECT_EQ("", status.token_secret);
1080+ EXPECT_EQ("", status.error);
1081+
1082+ std::lock_guard<std::mutex> lock(mutex_);
1083+ got_update_ = true;
1084+ cond_.notify_all();
1085+ }
1086+
1087+ void service_update_auth(OnlineAccountClient::ServiceStatus const& status)
1088+ {
1089+ EXPECT_EQ(1, status.account_id);
1090+ EXPECT_EQ(true, status.service_enabled);
1091+ EXPECT_EQ(true, status.service_authenticated);
1092+ EXPECT_EQ("isuertbiseruy87srkuthksvu", status.client_id);
1093+ EXPECT_EQ("rytwekfgiodng523dr4", status.client_secret);
1094+ EXPECT_EQ("sfhgbfgutgi9ugwirheg74", status.access_token);
1095+ EXPECT_EQ("qwpeurylsfdg83", status.token_secret);
1096+ EXPECT_EQ("not really an error, but just to test", status.error);
1097+
1098+ std::lock_guard<std::mutex> lock(mutex_);
1099+ got_update_ = true;
1100+ cond_.notify_all();
1101+ }
1102+
1103+ bool wait_for_service_update()
1104+ {
1105+ std::unique_lock<std::mutex> lock(mutex_);
1106+ EXPECT_TRUE(cond_.wait_for(lock, std::chrono::seconds(1), [this]{ return got_update_; })) << "service update callback not triggered";
1107+ bool result = got_update_;
1108+ got_update_ = false;
1109+ return result;
1110+ }
1111+
1112+ void invoke_callback(std::shared_ptr<OnlineAccountClient> oa_client, AccountInfo const* info, std::string const& error)
1113+ {
1114+ oa_client->p->callback(info, error);
1115+ }
1116+
1117+ static void safe_g_error_free_(GError* e)
1118+ {
1119+ if (e)
1120+ {
1121+ g_error_free(e);
1122+ }
1123+ }
1124+
1125+ static void safe_g_variant_free_(GVariant* v)
1126+ {
1127+ if (v)
1128+ {
1129+ g_variant_unref(v);
1130+ }
1131+ }
1132+
1133+private:
1134+ std::shared_ptr<OnlineAccountClient> oa_client_;
1135+ std::shared_ptr<AgManager> manager_;
1136+ std::shared_ptr<AgAccount> account_;
1137+ std::shared_ptr<GMainContext> main_loop_context_;
1138+
1139+ bool got_update_;
1140+ std::mutex mutex_;
1141+ std::condition_variable cond_;
1142+
1143+private:
1144+ void run_in_main_loop_(std::function<void()> func)
1145+ {
1146+ auto func_task = [](void* user_data)
1147+ {
1148+ auto cb_data = reinterpret_cast<std::pair<std::function<void()>*, GMainLoop*>*>(user_data);
1149+ auto func = cb_data->first;
1150+ auto event_loop = cb_data->second;
1151+
1152+ (*func)();
1153+
1154+ while (!g_main_loop_is_running(event_loop));
1155+ g_main_loop_quit(event_loop);
1156+
1157+ return G_SOURCE_REMOVE;
1158+ };
1159+
1160+ std::shared_ptr<GMainLoop> event_loop;
1161+ if (main_loop_context_)
1162+ {
1163+ event_loop.reset(g_main_loop_new(main_loop_context_.get(), false), g_main_loop_unref);
1164+ auto cb_data = std::make_pair(&func, event_loop.get());
1165+ g_main_context_invoke(main_loop_context_.get(), func_task, &cb_data);
1166+ }
1167+ else
1168+ {
1169+ event_loop.reset(g_main_loop_new(nullptr, false), g_main_loop_unref);
1170+ auto cb_data = std::make_pair(&func, event_loop.get());
1171+ g_idle_add(func_task, &cb_data);
1172+ }
1173+ g_main_loop_run(event_loop.get());
1174+ }
1175+
1176+ void create_account_()
1177+ {
1178+ account_.reset(ag_manager_create_account(manager_.get(), "TestProvider"), g_object_unref);
1179+ ag_account_select_service(account_.get(), nullptr);
1180+ ag_account_set_enabled(account_.get(), true);
1181+
1182+ GError* error = nullptr;
1183+ bool stored = ag_account_store_blocking(account_.get(), &error);
1184+ std::shared_ptr<GError> error_cleanup(error, safe_g_error_free_);
1185+
1186+ EXPECT_TRUE(stored);
1187+ EXPECT_EQ(nullptr, error);
1188+ }
1189+
1190+ void delete_account_()
1191+ {
1192+ ag_account_delete(account_.get());
1193+
1194+ GError* error = nullptr;
1195+ bool stored = ag_account_store_blocking(account_.get(), &error);
1196+ std::shared_ptr<GError> error_cleanup(error, safe_g_error_free_);
1197+
1198+ account_ = nullptr;
1199+
1200+ EXPECT_TRUE(stored);
1201+ EXPECT_EQ(nullptr, error);
1202+ }
1203+
1204+ void enable_service_()
1205+ {
1206+ auto service = ag_manager_get_service(manager_.get(), "TestService");
1207+ ag_account_select_service(account_.get(), service);
1208+ ag_account_set_enabled(account_.get(), true);
1209+
1210+ GError* error = nullptr;
1211+ bool stored = ag_account_store_blocking(account_.get(), &error);
1212+ std::shared_ptr<GError> error_cleanup(error, safe_g_error_free_);
1213+
1214+ EXPECT_TRUE(stored);
1215+ EXPECT_EQ(nullptr, error);
1216+ }
1217+
1218+ void disable_service_()
1219+ {
1220+ auto service = ag_manager_get_service(manager_.get(), "TestService");
1221+ ag_account_select_service(account_.get(), service);
1222+ ag_account_set_enabled(account_.get(), false);
1223+
1224+ GError* error = nullptr;
1225+ bool stored = ag_account_store_blocking(account_.get(), &error);
1226+ std::shared_ptr<GError> error_cleanup(error, safe_g_error_free_);
1227+
1228+ EXPECT_TRUE(stored);
1229+ EXPECT_EQ(nullptr, error);
1230+ }
1231+
1232+ void disable_account_()
1233+ {
1234+ ag_account_set_enabled(account_.get(), false);
1235+
1236+ GError* error = nullptr;
1237+ bool stored = ag_account_store_blocking(account_.get(), &error);
1238+ std::shared_ptr<GError> error_cleanup(error, safe_g_error_free_);
1239+
1240+ EXPECT_TRUE(stored);
1241+ EXPECT_EQ(nullptr, error);
1242+ }
1243+
1244+ void enable_account_()
1245+ {
1246+ ag_account_set_enabled(account_.get(), true);
1247+
1248+ GError* error = nullptr;
1249+ bool stored = ag_account_store_blocking(account_.get(), &error);
1250+ std::shared_ptr<GError> error_cleanup(error, safe_g_error_free_);
1251+
1252+ EXPECT_TRUE(stored);
1253+ EXPECT_EQ(nullptr, error);
1254+ }
1255+};
1256+
1257+class OnlineAccountClientTestNoMainLoop : public OnlineAccountClientTest
1258+{
1259+public:
1260+ OnlineAccountClientTestNoMainLoop()
1261+ : OnlineAccountClientTest(OnlineAccountClient::RunInExternalMainLoop) {}
1262+};
1263+
1264+} // namespace testing
1265+} // namespace scopes
1266+} // namespace unity
1267+
1268+TEST_F(OnlineAccountClientTest, register_account_login_result)
1269+{
1270+ OnlineAccountClient oa_client("test_service_name", "test_service_type", "test_provider");
1271+
1272+ CategoryRegistry reg;
1273+ auto cat = reg.register_category("1", "title", "icon", nullptr, CategoryRenderer());
1274+ CategorisedResult result(cat);
1275+
1276+ oa_client.register_account_login_item(result, CannedQuery("test"), OnlineAccountClient::InvalidateResults, OnlineAccountClient::DoNothing);
1277+
1278+ EXPECT_TRUE(result.contains("online_account_details"));
1279+
1280+ VariantMap details = result.value("online_account_details").get_dict();
1281+
1282+ EXPECT_NE(details.end(), details.find("service_name"));
1283+ EXPECT_NE(details.end(), details.find("service_type"));
1284+ EXPECT_NE(details.end(), details.find("provider_name"));
1285+ EXPECT_NE(details.end(), details.find("login_passed_action"));
1286+ EXPECT_NE(details.end(), details.find("login_failed_action"));
1287+
1288+ EXPECT_EQ("test_service_name", details.at("service_name").get_string());
1289+ EXPECT_EQ("test_service_type", details.at("service_type").get_string());
1290+ EXPECT_EQ("test_provider", details.at("provider_name").get_string());
1291+ EXPECT_EQ(OnlineAccountClient::InvalidateResults, static_cast<OnlineAccountClient::PostLoginAction>(details.at("login_passed_action").get_int()));
1292+ EXPECT_EQ(OnlineAccountClient::DoNothing, static_cast<OnlineAccountClient::PostLoginAction>(details.at("login_failed_action").get_int()));
1293+}
1294+
1295+TEST_F(OnlineAccountClientTest, register_account_login_widget)
1296+{
1297+ OnlineAccountClient oa_client("test_service_name", "test_service_type", "test_provider");
1298+
1299+ PreviewWidget widget("i1", "image");
1300+
1301+ oa_client.register_account_login_item(widget, OnlineAccountClient::ContinueActivation, OnlineAccountClient::DoNothing);
1302+
1303+ VariantMap values = widget.attribute_values();
1304+
1305+ EXPECT_NE(values.end(), values.find("online_account_details"));
1306+
1307+ VariantMap details = values.at("online_account_details").get_dict();
1308+
1309+ EXPECT_NE(details.end(), details.find("service_name"));
1310+ EXPECT_NE(details.end(), details.find("service_type"));
1311+ EXPECT_NE(details.end(), details.find("provider_name"));
1312+ EXPECT_NE(details.end(), details.find("login_passed_action"));
1313+ EXPECT_NE(details.end(), details.find("login_failed_action"));
1314+
1315+ EXPECT_EQ("test_service_name", details.at("service_name").get_string());
1316+ EXPECT_EQ("test_service_type", details.at("service_type").get_string());
1317+ EXPECT_EQ("test_provider", details.at("provider_name").get_string());
1318+ EXPECT_EQ(OnlineAccountClient::ContinueActivation, static_cast<OnlineAccountClient::PostLoginAction>(details.at("login_passed_action").get_int()));
1319+ EXPECT_EQ(OnlineAccountClient::DoNothing, static_cast<OnlineAccountClient::PostLoginAction>(details.at("login_failed_action").get_int()));
1320+}
1321+
1322+TEST_F(OnlineAccountClientTest, refresh_services_main_loop)
1323+{
1324+ auto statuses = oa_client()->get_service_statuses();
1325+ EXPECT_EQ(0, statuses.size());
1326+
1327+ create_account();
1328+ oa_client()->refresh_service_statuses();
1329+
1330+ statuses = oa_client()->get_service_statuses();
1331+ EXPECT_EQ(1, statuses.size());
1332+ EXPECT_FALSE(statuses[0].service_enabled);
1333+
1334+ enable_service();
1335+ oa_client()->refresh_service_statuses();
1336+
1337+ statuses = oa_client()->get_service_statuses();
1338+ EXPECT_TRUE(statuses[0].service_enabled);
1339+
1340+ disable_service();
1341+ oa_client()->refresh_service_statuses();
1342+
1343+ statuses = oa_client()->get_service_statuses();
1344+ EXPECT_FALSE(statuses[0].service_enabled);
1345+
1346+ enable_service();
1347+ oa_client()->refresh_service_statuses();
1348+
1349+ statuses = oa_client()->get_service_statuses();
1350+ EXPECT_TRUE(statuses[0].service_enabled);
1351+
1352+ disable_account();
1353+ oa_client()->refresh_service_statuses();
1354+
1355+ statuses = oa_client()->get_service_statuses();
1356+ EXPECT_FALSE(statuses[0].service_enabled);
1357+
1358+ enable_account();
1359+ oa_client()->refresh_service_statuses();
1360+
1361+ statuses = oa_client()->get_service_statuses();
1362+ EXPECT_TRUE(statuses[0].service_enabled);
1363+
1364+ delete_account();
1365+ oa_client()->refresh_service_statuses();
1366+
1367+ statuses = oa_client()->get_service_statuses();
1368+ EXPECT_EQ(0, statuses.size());
1369+}
1370+
1371+TEST_F(OnlineAccountClientTest, service_update_callback)
1372+{
1373+ auto statuses = oa_client()->get_service_statuses();
1374+ EXPECT_EQ(0, statuses.size());
1375+
1376+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_none, this, std::placeholders::_1));
1377+ create_account();
1378+ wait_for_service_update();
1379+
1380+ statuses = oa_client()->get_service_statuses();
1381+ EXPECT_EQ(1, statuses.size());
1382+
1383+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_enabled, this, std::placeholders::_1));
1384+ enable_service();
1385+ wait_for_service_update();
1386+
1387+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_disabled, this, std::placeholders::_1));
1388+ disable_service();
1389+ wait_for_service_update();
1390+
1391+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_enabled, this, std::placeholders::_1));
1392+ enable_service();
1393+ wait_for_service_update();
1394+
1395+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_disabled, this, std::placeholders::_1));
1396+ disable_account();
1397+ wait_for_service_update();
1398+
1399+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_enabled, this, std::placeholders::_1));
1400+ enable_account();
1401+ wait_for_service_update();
1402+
1403+ oa_client()->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_disabled, this, std::placeholders::_1));
1404+ delete_account();
1405+ wait_for_service_update();
1406+
1407+ statuses = oa_client()->get_service_statuses();
1408+ EXPECT_EQ(0, statuses.size());
1409+}
1410+
1411+TEST_F(OnlineAccountClientTestNoMainLoop, refresh_services_no_main_loop)
1412+{
1413+ auto statuses = oa_client()->get_service_statuses();
1414+ EXPECT_EQ(0, statuses.size());
1415+
1416+ create_account();
1417+ oa_client()->refresh_service_statuses();
1418+
1419+ statuses = oa_client()->get_service_statuses();
1420+ EXPECT_EQ(1, statuses.size());
1421+ EXPECT_FALSE(statuses[0].service_enabled);
1422+
1423+ enable_service();
1424+ oa_client()->refresh_service_statuses();
1425+
1426+ statuses = oa_client()->get_service_statuses();
1427+ EXPECT_TRUE(statuses[0].service_enabled);
1428+
1429+ disable_service();
1430+ oa_client()->refresh_service_statuses();
1431+
1432+ statuses = oa_client()->get_service_statuses();
1433+ EXPECT_FALSE(statuses[0].service_enabled);
1434+
1435+ enable_service();
1436+ oa_client()->refresh_service_statuses();
1437+
1438+ statuses = oa_client()->get_service_statuses();
1439+ EXPECT_TRUE(statuses[0].service_enabled);
1440+
1441+ disable_account();
1442+ oa_client()->refresh_service_statuses();
1443+
1444+ statuses = oa_client()->get_service_statuses();
1445+ EXPECT_FALSE(statuses[0].service_enabled);
1446+
1447+ enable_account();
1448+ oa_client()->refresh_service_statuses();
1449+
1450+ statuses = oa_client()->get_service_statuses();
1451+ EXPECT_TRUE(statuses[0].service_enabled);
1452+}
1453+
1454+TEST_F(OnlineAccountClientTestNoMainLoop, pub_sub_authentication)
1455+{
1456+ std::shared_ptr<OnlineAccountClient> shell_oa_client;
1457+ shell_oa_client.reset(new OnlineAccountClient("TestService", "sharing", "TestProvider", OnlineAccountClient::RunInExternalUiMainLoop));
1458+
1459+ std::shared_ptr<OnlineAccountClient> scope_oa_client;
1460+ scope_oa_client.reset(new OnlineAccountClient("TestService", "sharing", "TestProvider", OnlineAccountClient::RunInExternalMainLoop));
1461+
1462+ create_account();
1463+ scope_oa_client->refresh_service_statuses();
1464+
1465+ std::shared_ptr<AccountInfo> info(new AccountInfo);
1466+ info->account_id = 1;
1467+ info->service_enabled = true;
1468+ {
1469+ GVariantDict dict;
1470+ g_variant_dict_init(&dict, nullptr);
1471+ g_variant_dict_insert(&dict, "ConsumerKey", "s", "isuertbiseruy87srkuthksvu");
1472+ g_variant_dict_insert(&dict, "ConsumerSecret", "s", "rytwekfgiodng523dr4");
1473+ info->auth_params.reset(g_variant_ref_sink(g_variant_dict_end(&dict)), safe_g_variant_free_);
1474+ }
1475+ {
1476+ GVariantDict dict;
1477+ g_variant_dict_init(&dict, nullptr);
1478+ g_variant_dict_insert(&dict, "AccessToken", "s", "sfhgbfgutgi9ugwirheg74");
1479+ g_variant_dict_insert(&dict, "TokenSecret", "s", "qwpeurylsfdg83");
1480+ info->session_data.reset(g_variant_ref_sink(g_variant_dict_end(&dict)), safe_g_variant_free_);
1481+ }
1482+
1483+ // Invoke callback on shell_oa_client which should invoke a callback on scope_oa_client via pub/sub
1484+ scope_oa_client->set_service_update_callback(std::bind(&OnlineAccountClientTest::service_update_auth, this, std::placeholders::_1));
1485+ invoke_callback(shell_oa_client, info.get(), "not really an error, but just to test");
1486+ wait_for_service_update();
1487+}
1488+
1489+int main(int argc, char **argv)
1490+{
1491+ std::shared_ptr<DbusTestService> dbus_test_service;
1492+ dbus_test_service.reset(dbus_test_service_new(nullptr), g_object_unref);
1493+ dbus_test_service_run(dbus_test_service.get());
1494+
1495+ ::testing::InitGoogleTest(&argc, argv);
1496+ return RUN_ALL_TESTS();
1497 }
1498
1499=== added directory 'test/gtest/scopes/OnlineAccountClient/data'
1500=== added file 'test/gtest/scopes/OnlineAccountClient/data/TestService.service'
1501--- test/gtest/scopes/OnlineAccountClient/data/TestService.service 1970-01-01 00:00:00 +0000
1502+++ test/gtest/scopes/OnlineAccountClient/data/TestService.service 2014-10-08 11:00:01 +0000
1503@@ -0,0 +1,26 @@
1504+<?xml version="1.0" encoding="UTF-8" ?>
1505+<service id="TestService">
1506+ <type>sharing</type>
1507+ <name>Test Service</name>
1508+ <provider>TestProvider</provider>
1509+ <template>
1510+ <group name="auth">
1511+ <setting name="method">oauth2</setting>
1512+ <setting name="mechanism">web_server</setting>
1513+ <group name="oauth2">
1514+ <group name="web_server">
1515+ <setting name="Host">accounts.test.com</setting>
1516+
1517+ <setting name="AuthPath">o/oauth2/auth?access_type=offline</setting>
1518+ <setting name="TokenPath">o/oauth2/token</setting>
1519+ <setting name="RedirectUri">https://wiki.ubuntu.com/</setting>
1520+ <setting name="ResponseType">code</setting>
1521+ <setting type="as" name="Scope">['https://www.test.com/auth/test.readonly']</setting>
1522+ <setting name="ClientId">69842936499-sdflkbhslufhgrjamwlicefhb.apps.test.com</setting>
1523+ <setting name="ClientSecret">lj3i8iorep0w03994jwjef0j</setting>
1524+ <setting type="as" name="AllowedSchemes">['https','http']</setting>
1525+ </group>
1526+ </group>
1527+ </group>
1528+ </template>
1529+</service>
1530
1531=== modified file 'valgrind-suppress'
1532--- valgrind-suppress 2014-09-25 09:43:35 +0000
1533+++ valgrind-suppress 2014-10-08 11:00:01 +0000
1534@@ -212,3 +212,24 @@
1535 Memcheck:Cond
1536 fun:ag_auth_data_unref
1537 }
1538+
1539+{
1540+ malloc[Memcheck:Leak]
1541+ Memcheck:Leak
1542+ fun:malloc
1543+ fun:g_malloc
1544+ fun:g_slice_alloc
1545+ fun:g_slice_alloc0
1546+ ...
1547+ fun:ag_manager_get_service
1548+ ...
1549+ fun:ag_manager_list_services_by_type
1550+ fun:ag_account_list_services
1551+}
1552+
1553+{
1554+ operator new(unsigned long)[Memcheck:Leak]
1555+ Memcheck:Leak
1556+ fun:_Znwm
1557+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
1558+}

Subscribers

People subscribed via source and target branches

to all changes: