Merge lp:~ted/url-dispatcher/url-overlay into lp:url-dispatcher/15.10

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/url-dispatcher/url-overlay
Merge into: lp:url-dispatcher/15.10
Diff against target: 1985 lines (+1497/-31)
32 files modified
CMakeLists.txt (+4/-1)
data/CMakeLists.txt (+7/-2)
data/url-dispatcher.url-overlay.click-hook.in (+3/-0)
debian/control (+3/-1)
debian/rules (+5/-0)
debian/url-dispatcher.install (+1/-0)
service/CMakeLists.txt (+26/-2)
service/dispatcher.c (+94/-11)
service/dispatcher.h (+5/-2)
service/glib-thread.cpp (+179/-0)
service/glib-thread.h (+81/-0)
service/overlay-tracker-iface.h (+26/-0)
service/overlay-tracker-mir.cpp (+153/-0)
service/overlay-tracker-mir.h (+48/-0)
service/overlay-tracker.cpp (+54/-0)
service/overlay-tracker.h (+28/-0)
service/recoverable-problem.c (+6/-0)
service/service.c (+3/-1)
service/url-overlay.c (+183/-0)
tests/CMakeLists.txt (+47/-1)
tests/app-id-test.cc (+3/-1)
tests/dispatcher-test.cc (+25/-1)
tests/exec-tool-test.cc (+111/-0)
tests/mir-mock.cpp (+124/-0)
tests/mir-mock.h (+36/-0)
tests/overlay-dir/mock-overlay.desktop (+2/-0)
tests/overlay-tracker-mock.h (+30/-0)
tests/overlay-tracker-test.cpp (+127/-0)
tests/service-test.cc (+3/-8)
tests/test-config.h.in (+3/-0)
tests/ubuntu-app-launch-mock.c (+65/-0)
tests/ubuntu-app-launch-mock.h (+12/-0)
To merge this branch: bzr merge lp:~ted/url-dispatcher/url-overlay
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Indicator Applet Developers Pending
Review via email: mp+259322@code.launchpad.net

This proposal has been superseded by a proposal from 2015-06-08.

Commit message

Support URL Overlays of content in a trusted session without a desktop file

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~ted/url-dispatcher/url-overlay updated
127. By Ted Gould

Grab the exec tool too

128. By Ted Gould

Ensure Mir has pthread

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~ted/url-dispatcher/url-overlay updated
129. By Ted Gould

Adding in directory support for the new UAL

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~ted/url-dispatcher/url-overlay updated
130. By Ted Gould

Add the libclick dev package

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~ted/url-dispatcher/url-overlay updated
131. By Ted Gould

Grab Upstart dep

132. By Ted Gould

Make the exec-tool test work with the directory feature (and test it)

133. By Ted Gould

Fixing the dispatcher test to match the fixes in the exec tool test

134. By Ted Gould

Allow dir to be NULL

135. By Ted Gould

Switch linking option to not include the pthread library, but instead ask for it directly

136. By Ted Gould

Remove (void) as a function prototype

137. By Ted Gould

Making internal functions private

138. By Ted Gould

Clean up a couple of warnings

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-10-28 12:46:32 +0000
3+++ CMakeLists.txt 2015-06-08 15:28:16 +0000
4@@ -52,7 +52,7 @@
5 set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
6 endif()
7
8-pkg_check_modules(UBUNTU_APP_LAUNCH REQUIRED ubuntu-app-launch-2)
9+pkg_check_modules(UBUNTU_APP_LAUNCH REQUIRED ubuntu-app-launch-2>=0.5)
10 include_directories(${UBUNTU_APP_LAUNCH_INCLUDE_DIRS})
11
12 pkg_check_modules(GLIB2 REQUIRED glib-2.0)
13@@ -76,6 +76,9 @@
14 pkg_check_modules(SQLITE REQUIRED sqlite3)
15 include_directories(${SQLITE_INCLUDE_DIRS})
16
17+pkg_check_modules(CLICK REQUIRED click-0.4)
18+include_directories(${CLICK_INCLUDE_DIRS})
19+
20 if(${LOCAL_INSTALL})
21 set(DBUSSERVICEDIR "${CMAKE_INSTALL_DATADIR}/dbus-1/services/")
22 else()
23
24=== modified file 'data/CMakeLists.txt'
25--- data/CMakeLists.txt 2014-08-20 18:58:49 +0000
26+++ data/CMakeLists.txt 2015-06-08 15:28:16 +0000
27@@ -92,8 +92,13 @@
28 # Click Hook
29 ###########################
30
31-configure_file("url-dispatcher.click-hook.in"
32- "${CMAKE_SOURCE_DIR}/debian/url-dispatcher.click-hook"
33+configure_file("url-dispatcher.urls.click-hook.in"
34+ "${CMAKE_SOURCE_DIR}/debian/url-dispatcher.urls.click-hook"
35+ @ONLY
36+)
37+
38+configure_file("url-dispatcher.url-overlay.click-hook.in"
39+ "${CMAKE_SOURCE_DIR}/debian/url-dispatcher.url-overlay.click-hook"
40 @ONLY
41 )
42
43
44=== added file 'data/url-dispatcher.url-overlay.click-hook.in'
45--- data/url-dispatcher.url-overlay.click-hook.in 1970-01-01 00:00:00 +0000
46+++ data/url-dispatcher.url-overlay.click-hook.in 2015-06-08 15:28:16 +0000
47@@ -0,0 +1,3 @@
48+Pattern: ${home}/.cache/url-dispatcher/url-overlays/${id}.desktop
49+User-Level: yes
50+Hook-Name: url-overlay
51
52=== renamed file 'data/url-dispatcher.click-hook.in' => 'data/url-dispatcher.urls.click-hook.in'
53=== modified file 'debian/control'
54--- debian/control 2014-08-20 18:41:41 +0000
55+++ debian/control 2015-06-08 15:28:16 +0000
56@@ -9,19 +9,21 @@
57 dh-autoreconf,
58 gtester2xunit,
59 intltool,
60+ libclick-0.4-dev,
61 libdbus-1-dev,
62 libdbustest1-dev (>= 14.04.0),
63 libglib2.0-dev,
64 libjson-glib-dev,
65 libgtest-dev,
66 libsqlite3-dev,
67- libubuntu-app-launch2-dev (>= 0.3),
68+ libubuntu-app-launch2-dev (>= 0.5),
69 python3,
70 python3-dbusmock,
71 python3-fixtures,
72 python3-nose,
73 python3-testtools,
74 sqlite3,
75+ upstart,
76 Standards-Version: 3.9.4
77 Homepage: http://launchpad.net/url-dispatcher
78 # If you aren't a member of ~indicator-applet-developers but need to upload
79
80=== modified file 'debian/rules'
81--- debian/rules 2014-07-20 11:45:00 +0000
82+++ debian/rules 2015-06-08 15:28:16 +0000
83@@ -2,6 +2,7 @@
84
85 export DPKG_GENSYMBOLS_CHECK_LEVEL = 4
86 export G_MESSAGES_DEBUG=all
87+export URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR=1
88
89 %:
90 dh $@ --fail-missing --with click
91@@ -12,3 +13,7 @@
92 mkdir -p debian/url-dispatcher/etc/apport/crashdb.conf.d/
93 install -m 644 debian/url-dispatcher-crashdb.conf debian/url-dispatcher/etc/apport/crashdb.conf.d/
94 dh_install --fail-missing
95+
96+override_dh_click:
97+ dh_click --name urls
98+ dh_click --name url-overlay
99
100=== modified file 'debian/url-dispatcher.install'
101--- debian/url-dispatcher.install 2013-06-28 16:35:12 +0000
102+++ debian/url-dispatcher.install 2015-06-08 15:28:16 +0000
103@@ -1,3 +1,4 @@
104 usr/lib/*/url-dispatcher
105+usr/lib/*/ubuntu-app-launch/url-overlay/exec-tool
106 usr/share/dbus-1
107 usr/share/upstart/sessions
108
109=== modified file 'service/CMakeLists.txt'
110--- service/CMakeLists.txt 2014-05-26 12:56:47 +0000
111+++ service/CMakeLists.txt 2015-06-08 15:28:16 +0000
112@@ -2,6 +2,8 @@
113 include(UseConstantBuilder)
114 include_directories(${CMAKE_CURRENT_SOURCE_DIR})
115
116+add_definitions( -DOVERLAY_SYSTEM_DIRECTORY="${CMAKE_INSTALL_FULL_DATADIR}/url-dispatcher/url-overlays" )
117+
118 ###########################
119 # Generated Lib
120 ###########################
121@@ -38,15 +40,24 @@
122
123 add_library(dispatcher-lib STATIC
124 dispatcher.h
125- dispatcher.c)
126+ dispatcher.c
127+ glib-thread.h
128+ glib-thread.cpp
129+ overlay-tracker.h
130+ overlay-tracker.cpp
131+ overlay-tracker-iface.h
132+ overlay-tracker-mir.h
133+ overlay-tracker-mir.cpp)
134
135 target_link_libraries(dispatcher-lib
136 url-db-lib
137 service-generated
138+ -lpthread
139 ${GLIB2_LIBRARIES}
140 ${GOBJECT2_LIBRARIES}
141 ${GIO2_LIBRARIES}
142 ${SQLITE_LIBRARIES}
143+ ${UBUNTU_APP_LAUNCH_LIBRARIES}
144 )
145
146 ###########################
147@@ -84,7 +95,7 @@
148
149 set_target_properties(service-exec PROPERTIES OUTPUT_NAME "url-dispatcher")
150
151-target_link_libraries(service-exec dispatcher-lib ${UBUNTU_APP_LAUNCH_LIBRARIES})
152+target_link_libraries(service-exec dispatcher-lib)
153
154 ###########################
155 # Update Directory
156@@ -95,6 +106,14 @@
157 target_link_libraries(update-directory ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} url-db-lib)
158
159 ###########################
160+# URL Overlay Exec Tool
161+###########################
162+
163+add_executable(url-overlay-exec-tool url-overlay.c recoverable-problem.c)
164+set_target_properties(url-overlay-exec-tool PROPERTIES OUTPUT_NAME "exec-tool")
165+target_link_libraries(url-overlay-exec-tool ${GIO2_LIBRARIES} ${UBUNTU_APP_LAUNCH_LIBRARIES} ${CLICK_LIBRARIES})
166+
167+###########################
168 # Installation
169 ###########################
170
171@@ -103,3 +122,8 @@
172 RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/url-dispatcher"
173 )
174
175+install(
176+ TARGETS url-overlay-exec-tool
177+ RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/ubuntu-app-launch/url-overlay"
178+)
179+
180
181=== modified file 'service/dispatcher.c'
182--- service/dispatcher.c 2015-01-23 14:34:30 +0000
183+++ service/dispatcher.c 2015-06-08 15:28:16 +0000
184@@ -26,6 +26,7 @@
185 #include "url-db.h"
186
187 /* Globals */
188+static OverlayTracker * tracker = NULL;
189 static GCancellable * cancellable = NULL;
190 static ServiceIfaceComCanonicalURLDispatcher * skel = NULL;
191 static GRegex * applicationre = NULL;
192@@ -78,11 +79,7 @@
193 NULL
194 };
195
196- /* Allow disabling for testing, we don't want to report bugs on
197- our tests ;-) */
198- if (g_getenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR") == NULL) {
199- report_recoverable_problem("url-dispatcher-bad-url", pid, FALSE, additional);
200- }
201+ report_recoverable_problem("url-dispatcher-bad-url", pid, FALSE, additional);
202
203 g_free(badurl);
204
205@@ -111,7 +108,7 @@
206 bad_url (GDBusMethodInvocation * invocation, const gchar * url)
207 {
208 const gchar * sender = g_dbus_method_invocation_get_sender(invocation);
209- GDBusConnection * conn = g_dbus_method_invocation_get_connection(invocation);
210+ GDBusConnection * conn = g_dbus_method_invocation_get_connection(invocation); /* transfer: none */
211
212 g_dbus_connection_call(conn,
213 "org.freedesktop.DBus",
214@@ -213,6 +210,73 @@
215 return TRUE;
216 }
217
218+/* Handles setting up the overlay with the URL */
219+gboolean
220+dispatcher_send_to_overlay (const gchar * app_id, const gchar * url, GDBusConnection * conn, const gchar * sender)
221+{
222+ GError * error = NULL;
223+
224+ /* TODO: Detect if a scope is what we need to overlay on */
225+ GVariant * callret = g_dbus_connection_call_sync(conn,
226+ "org.freedesktop.DBus",
227+ "/",
228+ "org.freedesktop.DBus",
229+ "GetConnectionUnixProcessID",
230+ g_variant_new("(s)", sender),
231+ G_VARIANT_TYPE("(u)"),
232+ G_DBUS_CALL_FLAGS_NONE,
233+ -1, /* timeout */
234+ NULL, /* cancellable */
235+ &error);
236+
237+ if (error != NULL) {
238+ g_warning("Unable to get PID for '%s' when processing URL '%s': %s", sender, url, error->message);
239+ g_error_free(error);
240+ return FALSE;
241+ }
242+
243+ unsigned int pid = 0;
244+ g_variant_get_child(callret, 0, "u", &pid);
245+ g_variant_unref(callret);
246+
247+ return overlay_tracker_add(tracker, app_id, pid, url);
248+}
249+
250+/* Check to see if this is an overlay AppID */
251+gboolean
252+dispatcher_is_overlay (const gchar * appid)
253+{
254+ const gchar * systemdir = NULL;
255+ gboolean found = FALSE;
256+ gchar * desktopname = g_strdup_printf("%s.desktop", appid);
257+
258+ /* First time, check the environment */
259+ if (G_UNLIKELY(systemdir == NULL)) {
260+ systemdir = g_getenv("URL_DISPATCHER_OVERLAY_DIR");
261+ if (systemdir == NULL) {
262+ systemdir = OVERLAY_SYSTEM_DIRECTORY;
263+ }
264+ }
265+
266+ /* Check system dir */
267+ if (!found) {
268+ gchar * sysdir = g_build_filename(systemdir, desktopname, NULL);
269+ found = g_file_test(sysdir, G_FILE_TEST_EXISTS);
270+ g_free(sysdir);
271+ }
272+
273+ /* Check user dir (clicks) */
274+ if (!found) {
275+ gchar * usrdir = g_build_filename(g_get_user_cache_dir(), "url-dispatcher", "url-overlays", desktopname, NULL);
276+ found = g_file_test(usrdir, G_FILE_TEST_EXISTS);
277+ g_free(usrdir);
278+ }
279+
280+ g_free(desktopname);
281+
282+ return found;
283+}
284+
285 /* Whether we should restrict this appid based on the package name */
286 gboolean
287 dispatcher_appid_restrict (const gchar * appid, const gchar * package)
288@@ -277,12 +341,25 @@
289 }
290
291 /* We're cleared to continue */
292- dispatcher_send_to_app(appid, outurl);
293+ gboolean sent = FALSE;
294+ if (!dispatcher_is_overlay(appid)) {
295+ sent = dispatcher_send_to_app(appid, outurl);
296+ } else {
297+ sent = dispatcher_send_to_overlay(
298+ appid,
299+ outurl,
300+ g_dbus_method_invocation_get_connection(invocation),
301+ g_dbus_method_invocation_get_sender(invocation));
302+ }
303 g_free(appid);
304
305- g_dbus_method_invocation_return_value(invocation, NULL);
306+ if (sent) {
307+ g_dbus_method_invocation_return_value(invocation, NULL);
308+ } else {
309+ bad_url(invocation, url);
310+ }
311
312- return TRUE;
313+ return sent;
314 }
315
316 /* Test a URL to find it's AppID */
317@@ -347,6 +424,9 @@
318 gboolean
319 dispatcher_url_to_appid (const gchar * url, gchar ** out_appid, const gchar ** out_url)
320 {
321+ g_return_val_if_fail(url != NULL, FALSE);
322+ g_return_val_if_fail(out_appid != NULL, FALSE);
323+
324 /* Special case the app id */
325 GMatchInfo * appidmatch = NULL;
326 if (g_regex_match(appidre, url, 0, &appidmatch)) {
327@@ -398,7 +478,9 @@
328
329 if (*out_appid != NULL) {
330 found = TRUE;
331- *out_url = url;
332+ if (out_url != NULL) {
333+ *out_url = url;
334+ }
335 }
336
337 g_free(protocol);
338@@ -465,8 +547,9 @@
339
340 /* Initialize all the globals */
341 gboolean
342-dispatcher_init (GMainLoop * mainloop)
343+dispatcher_init (GMainLoop * mainloop, OverlayTracker * intracker)
344 {
345+ tracker = intracker;
346 cancellable = g_cancellable_new();
347
348 urldb = url_db_create_database();
349
350=== modified file 'service/dispatcher.h'
351--- service/dispatcher.h 2014-10-24 18:55:11 +0000
352+++ service/dispatcher.h 2015-06-08 15:28:16 +0000
353@@ -20,15 +20,18 @@
354 #ifndef DISPATCHER_H
355 #define DISPATCHER_H 1
356
357-#include <glib.h>
358+#include <gio/gio.h>
359+#include "overlay-tracker.h"
360
361 G_BEGIN_DECLS
362
363-gboolean dispatcher_init (GMainLoop * mainloop);
364+gboolean dispatcher_init (GMainLoop * mainloop, OverlayTracker * tracker);
365 gboolean dispatcher_shutdown (void);
366 gboolean dispatcher_url_to_appid (const gchar * url, gchar ** out_appid, const gchar ** out_url);
367 gboolean dispatcher_appid_restrict (const gchar * appid, const gchar * package);
368+gboolean dispatcher_is_overlay (const gchar * appid);
369 gboolean dispatcher_send_to_app (const gchar * appid, const gchar * url);
370+gboolean dispatcher_send_to_overlay (const gchar * app_id, const gchar * url, GDBusConnection * conn, const gchar * sender);
371
372 G_END_DECLS
373
374
375=== added file 'service/glib-thread.cpp'
376--- service/glib-thread.cpp 1970-01-01 00:00:00 +0000
377+++ service/glib-thread.cpp 2015-06-08 15:28:16 +0000
378@@ -0,0 +1,179 @@
379+/*
380+ * Copyright © 2015 Canonical Ltd.
381+ *
382+ * This program is free software: you can redistribute it and/or modify it
383+ * under the terms of the GNU General Public License version 3, as published
384+ * by the Free Software Foundation.
385+ *
386+ * This program is distributed in the hope that it will be useful, but
387+ * WITHOUT ANY WARRANTY; without even the implied warranties of
388+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
389+ * PURPOSE. See the GNU General Public License for more details.
390+ *
391+ * You should have received a copy of the GNU General Public License along
392+ * with this program. If not, see <http://www.gnu.org/licenses/>.
393+ *
394+ * Authors:
395+ * Ted Gould <ted.gould@canonical.com>
396+ */
397+
398+#include "glib-thread.h"
399+
400+namespace GLib
401+{
402+
403+
404+ContextThread::ContextThread (std::function<void(void)> beforeLoop, std::function<void(void)> afterLoop)
405+ : _context(nullptr)
406+ , _loop(nullptr)
407+{
408+ _cancel = std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable * cancel)
409+ {
410+ if (cancel != nullptr)
411+ {
412+ g_cancellable_cancel(cancel);
413+ g_object_unref(cancel);
414+ }
415+ });
416+ std::promise<std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>> context_promise;
417+
418+ /* NOTE: We copy afterLoop but reference beforeLoop. We're blocking so we
419+ know that beforeLoop will stay valid long enough, but we can't say the
420+ same for afterLoop */
421+ _thread = std::thread([&context_promise, &beforeLoop, afterLoop, this](void) -> void
422+ {
423+ /* Build up the context and loop for the async events and a place
424+ for GDBus to send its events back to */
425+ auto context = std::shared_ptr<GMainContext>(g_main_context_new(), [](GMainContext * context)
426+ {
427+ g_clear_pointer(&context, g_main_context_unref);
428+ });
429+ auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(context.get(), FALSE), [](GMainLoop * loop)
430+ {
431+ g_clear_pointer(&loop, g_main_loop_unref);
432+ });
433+
434+ g_main_context_push_thread_default(context.get());
435+
436+ beforeLoop();
437+
438+ /* Free's the constructor to continue */
439+ auto pair = std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>(context, loop);
440+ context_promise.set_value(pair);
441+
442+ if (!g_cancellable_is_cancelled(_cancel.get()))
443+ {
444+ g_main_loop_run(loop.get());
445+ }
446+
447+ afterLoop();
448+ });
449+
450+ /* We need to have the context and the mainloop ready before
451+ other functions on this object can work properly. So we wait
452+ for them and set them on this thread. */
453+ auto context_future = context_promise.get_future();
454+ context_future.wait();
455+ auto context_value = context_future.get();
456+
457+ _context = context_value.first;
458+ _loop = context_value.second;
459+
460+ if (_context == nullptr || _loop == nullptr)
461+ {
462+ throw std::runtime_error("Unable to create GLib Thread");
463+ }
464+}
465+
466+ContextThread::~ContextThread (void)
467+{
468+ quit();
469+}
470+
471+void ContextThread::quit (void)
472+{
473+ g_cancellable_cancel(_cancel.get()); /* Force the cancellation on ongoing tasks */
474+ if (_loop != nullptr)
475+ {
476+ g_main_loop_quit(_loop.get()); /* Quit the loop */
477+ }
478+
479+ /* Joining here because we want to ensure that the final afterLoop()
480+ function is run before returning */
481+ if (std::this_thread::get_id() != _thread.get_id())
482+ {
483+ if (_thread.joinable())
484+ {
485+ _thread.join();
486+ }
487+ }
488+}
489+
490+bool ContextThread::isCancelled (void)
491+{
492+ return g_cancellable_is_cancelled(_cancel.get()) == TRUE;
493+}
494+
495+std::shared_ptr<GCancellable> ContextThread::getCancellable (void)
496+{
497+ return _cancel;
498+}
499+
500+void ContextThread::simpleSource (std::function<GSource * (void)> srcBuilder, std::function<void(void)> work)
501+{
502+ if (isCancelled())
503+ {
504+ throw std::runtime_error("Trying to execute work on a GLib thread that is shutting down.");
505+ }
506+
507+ /* Copy the work so that we can reuse it */
508+ /* Lifecycle is handled with the source pointer when we attach
509+ it to the context. */
510+ auto heapWork = new std::function<void(void)>(work);
511+
512+ auto source = std::shared_ptr<GSource>(srcBuilder(),
513+ [](GSource * src)
514+ {
515+ g_clear_pointer(&src, g_source_unref);
516+ }
517+ );
518+ g_source_set_callback(source.get(),
519+ [](gpointer data) -> gboolean
520+ {
521+ std::function<void(void)>* heapWork = reinterpret_cast<std::function<void(void)> *>(data);
522+ (*heapWork)();
523+ return G_SOURCE_REMOVE;
524+ }, heapWork,
525+ [](gpointer data)
526+ {
527+ std::function<void(void)>* heapWork = reinterpret_cast<std::function<void(void)> *>(data);
528+ delete heapWork;
529+ });
530+
531+ g_source_attach(source.get(), _context.get());
532+}
533+
534+void ContextThread::executeOnThread (std::function<void(void)> work)
535+{
536+ simpleSource(g_idle_source_new, work);
537+}
538+
539+void ContextThread::timeout (const std::chrono::milliseconds& length,
540+ std::function<void(void)> work)
541+{
542+ simpleSource([length]()
543+ {
544+ return g_timeout_source_new(length.count());
545+ }, work);
546+}
547+
548+void ContextThread::timeoutSeconds (const std::chrono::seconds& length,
549+ std::function<void(void)> work)
550+{
551+ simpleSource([length]()
552+ {
553+ return g_timeout_source_new_seconds(length.count());
554+ }, work);
555+}
556+
557+} // ns GLib
558
559=== added file 'service/glib-thread.h'
560--- service/glib-thread.h 1970-01-01 00:00:00 +0000
561+++ service/glib-thread.h 2015-06-08 15:28:16 +0000
562@@ -0,0 +1,81 @@
563+/*
564+ * Copyright © 2015 Canonical Ltd.
565+ *
566+ * This program is free software: you can redistribute it and/or modify it
567+ * under the terms of the GNU General Public License version 3, as published
568+ * by the Free Software Foundation.
569+ *
570+ * This program is distributed in the hope that it will be useful, but
571+ * WITHOUT ANY WARRANTY; without even the implied warranties of
572+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
573+ * PURPOSE. See the GNU General Public License for more details.
574+ *
575+ * You should have received a copy of the GNU General Public License along
576+ * with this program. If not, see <http://www.gnu.org/licenses/>.
577+ *
578+ * Authors:
579+ * Ted Gould <ted.gould@canonical.com>
580+ */
581+
582+#include <thread>
583+#include <future>
584+
585+#include <gio/gio.h>
586+
587+namespace GLib
588+{
589+
590+class ContextThread
591+{
592+ std::thread _thread;
593+ std::shared_ptr<GMainContext> _context;
594+ std::shared_ptr<GMainLoop> _loop;
595+ std::shared_ptr<GCancellable> _cancel;
596+
597+public:
598+ ContextThread (std::function<void(void)> beforeLoop = [] {}, std::function<void(void)> afterLoop = [] {});
599+ ~ContextThread (void);
600+
601+ void quit (void);
602+ bool isCancelled (void);
603+ std::shared_ptr<GCancellable> getCancellable (void);
604+
605+ void executeOnThread (std::function<void(void)> work);
606+ template<typename T> auto executeOnThread (std::function<T(void)> work) -> T
607+ {
608+ if (std::this_thread::get_id() == _thread.get_id())
609+ {
610+ /* Don't block if we're on the same thread */
611+ return work();
612+ }
613+
614+ std::promise<T> promise;
615+ std::function<void(void)> magicFunc = [&promise, &work] (void) -> void {
616+ promise.set_value(work());
617+ };
618+
619+ executeOnThread(magicFunc);
620+
621+ auto future = promise.get_future();
622+ future.wait();
623+ return future.get();
624+ }
625+
626+ void timeout (const std::chrono::milliseconds& length, std::function<void(void)> work);
627+ template<class Rep, class Period> void timeout (const std::chrono::duration<Rep, Period>& length,
628+ std::function<void(void)> work)
629+ {
630+ return timeout(std::chrono::duration_cast<std::chrono::milliseconds>(length), work);
631+ }
632+
633+ void timeoutSeconds (const std::chrono::seconds& length, std::function<void(void)> work);
634+ template<class Rep, class Period> void timeoutSeconds (const std::chrono::duration<Rep, Period>& length,
635+ std::function<void(void)> work)
636+ {
637+ return timeoutSeconds(std::chrono::duration_cast<std::chrono::seconds>(length), work);
638+ }
639+
640+private:
641+ void simpleSource (std::function<GSource * (void)> srcBuilder, std::function<void(void)> work);
642+};
643+}
644
645=== added file 'service/overlay-tracker-iface.h'
646--- service/overlay-tracker-iface.h 1970-01-01 00:00:00 +0000
647+++ service/overlay-tracker-iface.h 2015-06-08 15:28:16 +0000
648@@ -0,0 +1,26 @@
649+/*
650+ * Copyright © 2015 Canonical Ltd.
651+ *
652+ * This program is free software: you can redistribute it and/or modify it
653+ * under the terms of the GNU General Public License version 3, as published
654+ * by the Free Software Foundation.
655+ *
656+ * This program is distributed in the hope that it will be useful, but
657+ * WITHOUT ANY WARRANTY; without even the implied warranties of
658+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
659+ * PURPOSE. See the GNU General Public License for more details.
660+ *
661+ * You should have received a copy of the GNU General Public License along
662+ * with this program. If not, see <http://www.gnu.org/licenses/>.
663+ *
664+ * Authors:
665+ * Ted Gould <ted.gould@canonical.com>
666+ */
667+
668+#pragma once
669+
670+class OverlayTrackerIface {
671+public:
672+ virtual ~OverlayTrackerIface() = default;
673+ virtual bool addOverlay (const char * appid, unsigned long pid, const char * url) = 0;
674+};
675
676=== added file 'service/overlay-tracker-mir.cpp'
677--- service/overlay-tracker-mir.cpp 1970-01-01 00:00:00 +0000
678+++ service/overlay-tracker-mir.cpp 2015-06-08 15:28:16 +0000
679@@ -0,0 +1,153 @@
680+/*
681+ * Copyright © 2015 Canonical Ltd.
682+ *
683+ * This program is free software: you can redistribute it and/or modify it
684+ * under the terms of the GNU General Public License version 3, as published
685+ * by the Free Software Foundation.
686+ *
687+ * This program is distributed in the hope that it will be useful, but
688+ * WITHOUT ANY WARRANTY; without even the implied warranties of
689+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
690+ * PURPOSE. See the GNU General Public License for more details.
691+ *
692+ * You should have received a copy of the GNU General Public License along
693+ * with this program. If not, see <http://www.gnu.org/licenses/>.
694+ *
695+ * Authors:
696+ * Ted Gould <ted.gould@canonical.com>
697+ */
698+
699+#include "overlay-tracker-mir.h"
700+#include <ubuntu-app-launch.h>
701+
702+static const char * HELPER_TYPE = "url-overlay";
703+
704+OverlayTrackerMir::OverlayTrackerMir (void)
705+ : thread([this] {
706+ /* Setup Helper Observer */
707+ ubuntu_app_launch_observer_add_helper_stop(untrustedHelperStoppedStatic, HELPER_TYPE, this);
708+ },
709+ [this] {
710+ /* Remove Helper Observer */
711+ ubuntu_app_launch_observer_delete_helper_stop(untrustedHelperStoppedStatic, HELPER_TYPE, this);
712+ })
713+{
714+ mir = std::shared_ptr<MirConnection>([] {
715+ gchar * path = g_build_filename(g_get_user_runtime_dir(), "mir_socket_trusted", NULL);
716+ MirConnection * con = mir_connect_sync(path, "url-dispatcher");
717+ g_free(path);
718+ return con;
719+ }(),
720+ [] (MirConnection * connection) {
721+ if (connection != nullptr)
722+ mir_connection_release(connection);
723+ });
724+
725+ if (!mir) {
726+ throw std::runtime_error("Unable to connect to Mir");
727+ }
728+}
729+
730+/* Enforce a shutdown order, sessions before connection */
731+OverlayTrackerMir::~OverlayTrackerMir (void)
732+{
733+ thread.executeOnThread<bool>([this] {
734+ while (!ongoingSessions.empty()) {
735+ removeSession(std::get<2>(*ongoingSessions.begin()).get());
736+ }
737+
738+ return true;
739+ });
740+
741+ mir.reset();
742+}
743+
744+bool
745+OverlayTrackerMir::addOverlay (const char * appid, unsigned long pid, const char * url)
746+{
747+ std::string sappid(appid);
748+ std::string surl(url);
749+
750+ return thread.executeOnThread<bool>([this, sappid, pid, surl] {
751+ g_debug("Setting up over lay for PID %d with '%s'", pid, sappid.c_str());
752+
753+ auto session = std::shared_ptr<MirPromptSession>(
754+ mir_connection_create_prompt_session_sync(mir.get(), pid, sessionStateChangedStatic, this),
755+ [] (MirPromptSession * session) { if (session) mir_prompt_session_release_sync(session); });
756+ if (!session) {
757+ g_critical("Unable to create trusted prompt session for %d with appid '%s'", pid, sappid.c_str());
758+ return false;
759+ }
760+
761+ std::array<const char *, 2> urls { surl.c_str(), nullptr };
762+ auto instance = ubuntu_app_launch_start_session_helper(HELPER_TYPE, session.get(), sappid.c_str(), urls.data());
763+ if (instance == nullptr) {
764+ g_critical("Unable to start helper for %d with appid '%s'", pid, sappid.c_str());
765+ return false;
766+ }
767+
768+ ongoingSessions.emplace(std::make_tuple(sappid, std::string(instance), session));
769+ g_free(instance);
770+ return true;
771+ });
772+}
773+
774+void
775+OverlayTrackerMir::sessionStateChangedStatic (MirPromptSession * session, MirPromptSessionState state, void * user_data)
776+{
777+ reinterpret_cast<OverlayTrackerMir *>(user_data)->sessionStateChanged(session, state);
778+}
779+
780+void
781+OverlayTrackerMir::removeSession (MirPromptSession * session)
782+{
783+ for (auto it = ongoingSessions.begin(); it != ongoingSessions.end(); it++) {
784+ if (std::get<2>(*it).get() == session) {
785+ ubuntu_app_launch_stop_multiple_helper(HELPER_TYPE, std::get<0>(*it).c_str(), std::get<1>(*it).c_str());
786+ ongoingSessions.erase(it);
787+ break;
788+ }
789+ }
790+}
791+
792+void
793+OverlayTrackerMir::sessionStateChanged (MirPromptSession * session, MirPromptSessionState state)
794+{
795+ if (state != mir_prompt_session_state_stopped) {
796+ /* We only care about the stopped state */
797+ return;
798+ }
799+
800+ /* Executing on the Mir thread, which is nice and all, but we
801+ want to get back on our thread */
802+ thread.executeOnThread([this, session]() {
803+ removeSession(session);
804+ });
805+}
806+
807+void
808+OverlayTrackerMir::untrustedHelperStoppedStatic (const gchar * appid, const gchar * instanceid, const gchar * helpertype, gpointer user_data)
809+{
810+ reinterpret_cast<OverlayTrackerMir *>(user_data)->untrustedHelperStopped(appid, instanceid, helpertype);
811+}
812+
813+void
814+OverlayTrackerMir::untrustedHelperStopped(const gchar * appid, const gchar * instanceid, const gchar * helpertype)
815+{
816+ /* This callback will happen on our thread already, we don't need
817+ to proxy it on */
818+ if (g_strcmp0(helpertype, HELPER_TYPE) != 0) {
819+ return;
820+ }
821+
822+ /* Making the code in the loop easier to read by using std::string outside */
823+ std::string sappid(appid);
824+ std::string sinstanceid(instanceid);
825+
826+ for (auto it = ongoingSessions.begin(); it != ongoingSessions.end(); it++) {
827+ if (std::get<0>(*it) == sappid && std::get<1>(*it) == sinstanceid) {
828+ ongoingSessions.erase(it);
829+ break;
830+ }
831+ }
832+}
833
834=== added file 'service/overlay-tracker-mir.h'
835--- service/overlay-tracker-mir.h 1970-01-01 00:00:00 +0000
836+++ service/overlay-tracker-mir.h 2015-06-08 15:28:16 +0000
837@@ -0,0 +1,48 @@
838+/*
839+ * Copyright © 2015 Canonical Ltd.
840+ *
841+ * This program is free software: you can redistribute it and/or modify it
842+ * under the terms of the GNU General Public License version 3, as published
843+ * by the Free Software Foundation.
844+ *
845+ * This program is distributed in the hope that it will be useful, but
846+ * WITHOUT ANY WARRANTY; without even the implied warranties of
847+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
848+ * PURPOSE. See the GNU General Public License for more details.
849+ *
850+ * You should have received a copy of the GNU General Public License along
851+ * with this program. If not, see <http://www.gnu.org/licenses/>.
852+ *
853+ * Authors:
854+ * Ted Gould <ted.gould@canonical.com>
855+ */
856+
857+#pragma once
858+
859+#include <set>
860+
861+#include <mir_toolkit/mir_connection.h>
862+#include <mir_toolkit/mir_prompt_session.h>
863+
864+#include "glib-thread.h"
865+#include "overlay-tracker-iface.h"
866+
867+class OverlayTrackerMir : public OverlayTrackerIface {
868+private:
869+ GLib::ContextThread thread;
870+ std::shared_ptr<MirConnection> mir;
871+ std::set<std::tuple<std::string, std::string, std::shared_ptr<MirPromptSession>>> ongoingSessions;
872+
873+public:
874+ OverlayTrackerMir (void);
875+ ~OverlayTrackerMir (void);
876+ bool addOverlay (const char * appid, unsigned long pid, const char * url) override;
877+
878+ void removeSession (MirPromptSession * session);
879+
880+ static void sessionStateChangedStatic (MirPromptSession * session, MirPromptSessionState state, void * user_data);
881+ void sessionStateChanged (MirPromptSession * session, MirPromptSessionState state);
882+
883+ static void untrustedHelperStoppedStatic (const gchar * appid, const gchar * instanceid, const gchar * helpertype, gpointer user_data);
884+ void untrustedHelperStopped(const gchar * appid, const gchar * instanceid, const gchar * helpertype);
885+};
886
887=== added file 'service/overlay-tracker.cpp'
888--- service/overlay-tracker.cpp 1970-01-01 00:00:00 +0000
889+++ service/overlay-tracker.cpp 2015-06-08 15:28:16 +0000
890@@ -0,0 +1,54 @@
891+/*
892+ * Copyright © 2015 Canonical Ltd.
893+ *
894+ * This program is free software: you can redistribute it and/or modify it
895+ * under the terms of the GNU General Public License version 3, as published
896+ * by the Free Software Foundation.
897+ *
898+ * This program is distributed in the hope that it will be useful, but
899+ * WITHOUT ANY WARRANTY; without even the implied warranties of
900+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
901+ * PURPOSE. See the GNU General Public License for more details.
902+ *
903+ * You should have received a copy of the GNU General Public License along
904+ * with this program. If not, see <http://www.gnu.org/licenses/>.
905+ *
906+ * Authors:
907+ * Ted Gould <ted.gould@canonical.com>
908+ */
909+
910+extern "C" {
911+#include "overlay-tracker.h"
912+}
913+
914+#include "overlay-tracker-iface.h"
915+#include "overlay-tracker-mir.h"
916+
917+OverlayTracker *
918+overlay_tracker_new (void) {
919+ try {
920+ OverlayTrackerMir * cpptracker = new OverlayTrackerMir();
921+ return reinterpret_cast<OverlayTracker *>(cpptracker);
922+ } catch (...) {
923+ return nullptr;
924+ }
925+}
926+
927+void
928+overlay_tracker_delete (OverlayTracker * tracker) {
929+ g_return_if_fail(tracker != nullptr);
930+
931+ auto cpptracker = reinterpret_cast<OverlayTrackerMir *>(tracker);
932+ delete cpptracker;
933+ return;
934+}
935+
936+gboolean
937+overlay_tracker_add (OverlayTracker * tracker, const char * appid, unsigned long pid, const gchar * url) {
938+ g_return_val_if_fail(tracker != nullptr, FALSE);
939+ g_return_val_if_fail(appid != nullptr, FALSE);
940+ g_return_val_if_fail(pid != 0, FALSE);
941+ g_return_val_if_fail(url != nullptr, FALSE);
942+
943+ return reinterpret_cast<OverlayTrackerIface *>(tracker)->addOverlay(appid, pid, url) ? TRUE : FALSE;
944+}
945
946=== added file 'service/overlay-tracker.h'
947--- service/overlay-tracker.h 1970-01-01 00:00:00 +0000
948+++ service/overlay-tracker.h 2015-06-08 15:28:16 +0000
949@@ -0,0 +1,28 @@
950+/*
951+ * Copyright © 2015 Canonical Ltd.
952+ *
953+ * This program is free software: you can redistribute it and/or modify it
954+ * under the terms of the GNU General Public License version 3, as published
955+ * by the Free Software Foundation.
956+ *
957+ * This program is distributed in the hope that it will be useful, but
958+ * WITHOUT ANY WARRANTY; without even the implied warranties of
959+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
960+ * PURPOSE. See the GNU General Public License for more details.
961+ *
962+ * You should have received a copy of the GNU General Public License along
963+ * with this program. If not, see <http://www.gnu.org/licenses/>.
964+ *
965+ * Authors:
966+ * Ted Gould <ted.gould@canonical.com>
967+ */
968+
969+#pragma once
970+#include <glib.h>
971+
972+typedef struct _OverlayTracker OverlayTracker;
973+
974+OverlayTracker * overlay_tracker_new (void);
975+void overlay_tracker_delete (OverlayTracker * tracker);
976+gboolean overlay_tracker_add (OverlayTracker * tracker, const char * appid, unsigned long pid, const char * url);
977+
978
979=== modified file 'service/recoverable-problem.c'
980--- service/recoverable-problem.c 2014-05-27 16:51:53 +0000
981+++ service/recoverable-problem.c 2015-06-08 15:28:16 +0000
982@@ -63,6 +63,12 @@
983 void
984 report_recoverable_problem (const gchar * signature, GPid report_pid, gboolean wait, const gchar * additional_properties[])
985 {
986+ /* Allow disabling for testing, we don't want to report bugs on
987+ our tests ;-) */
988+ if (G_UNLIKELY(g_getenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR") != NULL)) {
989+ return;
990+ }
991+
992 GError * error = NULL;
993 gint error_stdin = 0;
994 GPid pid = 0;
995
996=== modified file 'service/service.c'
997--- service/service.c 2014-03-14 19:50:37 +0000
998+++ service/service.c 2015-06-08 15:28:16 +0000
999@@ -39,13 +39,15 @@
1000
1001 guint term_source = g_unix_signal_add(SIGTERM, sig_term, mainloop);
1002
1003- dispatcher_init(mainloop);
1004+ OverlayTracker * tracker = overlay_tracker_new();
1005+ dispatcher_init(mainloop, tracker);
1006
1007 /* Run Main */
1008 g_main_loop_run(mainloop);
1009
1010 /* Clean up globals */
1011 dispatcher_shutdown();
1012+ overlay_tracker_delete(tracker);
1013 g_source_remove(term_source);
1014 g_main_loop_unref(mainloop);
1015
1016
1017=== added file 'service/url-overlay.c'
1018--- service/url-overlay.c 1970-01-01 00:00:00 +0000
1019+++ service/url-overlay.c 2015-06-08 15:28:16 +0000
1020@@ -0,0 +1,183 @@
1021+/*
1022+ * Copyright © 2014 Canonical Ltd.
1023+ *
1024+ * This program is free software: you can redistribute it and/or modify it
1025+ * under the terms of the GNU General Public License version 3, as published
1026+ * by the Free Software Foundation.
1027+ *
1028+ * This program is distributed in the hope that it will be useful, but
1029+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1030+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1031+ * PURPOSE. See the GNU General Public License for more details.
1032+ *
1033+ * You should have received a copy of the GNU General Public License along
1034+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1035+ *
1036+ * Authors:
1037+ * Ted Gould <ted.gould@canonical.com>
1038+ */
1039+
1040+#include <glib.h>
1041+#include <click.h>
1042+#include <ubuntu-app-launch.h>
1043+
1044+#include "recoverable-problem.h"
1045+
1046+gchar *
1047+build_exec (const gchar * appid, const gchar * directory)
1048+{
1049+ gchar * appid_desktop = g_strdup_printf("%s.desktop", appid);
1050+ gchar * desktopfilepath = g_build_filename(directory, appid_desktop, NULL);
1051+ g_free(appid_desktop);
1052+
1053+ if (!g_file_test(desktopfilepath, G_FILE_TEST_EXISTS)) {
1054+ g_free(desktopfilepath);
1055+ return NULL;
1056+ }
1057+
1058+ GError * error = NULL;
1059+ GKeyFile * keyfile = g_key_file_new();
1060+ g_key_file_load_from_file(keyfile, desktopfilepath, G_KEY_FILE_NONE, &error);
1061+
1062+ if (error != NULL) {
1063+ g_error("Unable to read url-overlay desktop file '%s': %s", desktopfilepath, error->message);
1064+ g_free(desktopfilepath);
1065+ g_key_file_free(keyfile);
1066+ g_error_free(error);
1067+ return NULL;
1068+ }
1069+
1070+ g_free(desktopfilepath);
1071+
1072+ if (!g_key_file_has_key(keyfile, "Desktop Entry", "Exec", NULL)) {
1073+ g_error("Desktop file for '%s' in '%s' does not have 'Exec' key", appid, directory);
1074+ g_key_file_free(keyfile);
1075+ return NULL;
1076+ }
1077+
1078+ gchar * exec = g_key_file_get_string(keyfile, "Desktop Entry", "Exec", NULL);
1079+ g_key_file_free(keyfile);
1080+
1081+ return exec;
1082+}
1083+
1084+gchar *
1085+build_dir (const gchar * appid)
1086+{
1087+ GError * error = NULL;
1088+ gchar * package = NULL;
1089+
1090+ /* 'Parse' the App ID */
1091+ if (!ubuntu_app_launch_app_id_parse(appid, &package, NULL, NULL)) {
1092+ g_warning("Unable to parse App ID: '%s'", appid);
1093+ return NULL;
1094+ }
1095+
1096+ /* Check click to find out where the files are */
1097+ ClickDB * db = click_db_new();
1098+
1099+ /* If TEST_CLICK_DB is unset, this reads the system database. */
1100+ click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
1101+ if (error != NULL) {
1102+ g_warning("Unable to read Click database: %s", error->message);
1103+ g_error_free(error);
1104+ g_free(package);
1105+ return NULL;
1106+ }
1107+
1108+ /* If TEST_CLICK_USER is unset, this uses the current user name. */
1109+ ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
1110+ if (error != NULL) {
1111+ g_warning("Unable to read Click database: %s", error->message);
1112+ g_error_free(error);
1113+ g_free(package);
1114+ g_object_unref(db);
1115+ return NULL;
1116+ }
1117+
1118+ gchar * pkgdir = click_user_get_path(user, package, &error);
1119+
1120+ g_object_unref(user);
1121+ g_object_unref(db);
1122+ g_free(package);
1123+
1124+ if (error != NULL) {
1125+ g_warning("Unable to get the Click package directory for %s: %s", package, error->message);
1126+ g_error_free(error);
1127+ return NULL;
1128+ }
1129+
1130+ return pkgdir;
1131+}
1132+
1133+int
1134+main (int argc, char * argv[])
1135+{
1136+ /* Build up our exec */
1137+ const gchar * appid = g_getenv("APP_ID");
1138+ if (appid == NULL) {
1139+ report_recoverable_problem("url-dispatcher-url-overlay-no-appid", 0, TRUE, NULL);
1140+ return -1;
1141+ }
1142+
1143+ gchar * exec = NULL;
1144+
1145+ /* Allow for environment override */
1146+ const gchar * envdir = g_getenv("URL_DISPATCHER_OVERLAY_DIR");
1147+ if (G_UNLIKELY(envdir != NULL)) { /* Mostly for testing */
1148+ exec = build_exec(appid, envdir);
1149+ }
1150+
1151+ /* Try the system directory */
1152+ if (exec == NULL) {
1153+ exec = build_exec(appid, OVERLAY_SYSTEM_DIRECTORY);
1154+ }
1155+
1156+ /* If not there look to the user directory (click) */
1157+ if (exec == NULL) {
1158+ gchar * userdir = g_build_filename(g_get_user_cache_dir(), "url-dispatcher", "url-overlays", NULL);
1159+ exec = build_exec(appid, userdir);
1160+ g_free(userdir);
1161+ }
1162+
1163+ if (exec == NULL) {
1164+ const gchar * props[3] = {
1165+ "AppID",
1166+ appid,
1167+ NULL
1168+ };
1169+
1170+ report_recoverable_problem("url-dispatcher-url-overlay-bad-appid", 0, TRUE, props);
1171+ return -1;
1172+ }
1173+
1174+ gchar * dir = build_dir(appid);
1175+ if (dir == NULL) {
1176+ const gchar * props[3] = {
1177+ "AppID",
1178+ appid,
1179+ NULL
1180+ };
1181+
1182+ report_recoverable_problem("url-dispatcher-url-overlay-bad-appid", 0, TRUE, props);
1183+ return -1;
1184+ }
1185+
1186+ GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1187+ g_return_val_if_fail(bus != NULL, -1);
1188+
1189+ gboolean sended = ubuntu_app_launch_helper_set_exec(exec, dir);
1190+ g_free(exec);
1191+ g_free(dir);
1192+
1193+ /* Ensuring the messages get on the bus before we quit */
1194+ g_dbus_connection_flush_sync(bus, NULL, NULL);
1195+ g_clear_object(&bus);
1196+
1197+ if (sended) {
1198+ return 0;
1199+ } else {
1200+ g_critical("Unable to send exec to Upstart");
1201+ return -1;
1202+ }
1203+}
1204
1205=== modified file 'tests/CMakeLists.txt'
1206--- tests/CMakeLists.txt 2015-01-09 16:11:45 +0000
1207+++ tests/CMakeLists.txt 2015-06-08 15:28:16 +0000
1208@@ -34,6 +34,23 @@
1209 )
1210
1211 ###########################
1212+# Mir Mock Lib
1213+###########################
1214+
1215+add_library(mir-mock-lib SHARED
1216+ mir-mock.h
1217+ mir-mock.cpp)
1218+
1219+target_link_libraries(mir-mock-lib
1220+ -lpthread
1221+ ${GLIB2_LIBRARIES}
1222+)
1223+
1224+set_target_properties(mir-mock-lib PROPERTIES
1225+ OUTPUT_NAME "mir-mock"
1226+)
1227+
1228+###########################
1229 # Dispatcher test
1230 ###########################
1231
1232@@ -55,7 +72,7 @@
1233
1234 include_directories("${CMAKE_BINARY_DIR}/service")
1235
1236-add_executable (app-id-test app-id-test.cc "${CMAKE_SOURCE_DIR}/service/dispatcher.c")
1237+add_executable (app-id-test app-id-test.cc)
1238 target_link_libraries (app-id-test
1239 dispatcher-lib
1240 mock-lib
1241@@ -128,3 +145,32 @@
1242
1243 add_test (url-db-test url-db-test)
1244 add_subdirectory(url_dispatcher_testability)
1245+
1246+###########################
1247+# exec tool test
1248+###########################
1249+
1250+add_executable (exec-tool-test exec-tool-test.cc)
1251+target_link_libraries (exec-tool-test
1252+ gtest
1253+ ${GTEST_LIBS}
1254+ ${GIO2_LIBRARIES}
1255+ ${DBUSTEST_LIBRARIES})
1256+
1257+add_test (exec-tool-test exec-tool-test)
1258+
1259+###########################
1260+# overlay tracker test
1261+###########################
1262+
1263+add_executable (overlay-tracker-test overlay-tracker-test.cpp)
1264+target_link_libraries (overlay-tracker-test
1265+ dispatcher-lib
1266+ mir-mock-lib
1267+ mock-lib
1268+ gtest
1269+ ${GTEST_LIBS}
1270+ ${GIO2_LIBRARIES}
1271+ ${DBUSTEST_LIBRARIES})
1272+
1273+add_test (overlay-tracker-test overlay-tracker-test)
1274
1275=== modified file 'tests/app-id-test.cc'
1276--- tests/app-id-test.cc 2014-10-28 09:49:29 +0000
1277+++ tests/app-id-test.cc 2015-06-08 15:28:16 +0000
1278@@ -21,6 +21,7 @@
1279 #include <gtest/gtest.h>
1280 #include "dispatcher.h"
1281 #include "ubuntu-app-launch-mock.h"
1282+#include "overlay-tracker-mock.h"
1283
1284 class AppIdTest : public ::testing::Test
1285 {
1286@@ -28,6 +29,7 @@
1287 GTestDBus * testbus = nullptr;
1288 GMainLoop * mainloop = nullptr;
1289 gchar * cachedir = nullptr;
1290+ OverlayTrackerMock tracker;
1291
1292 protected:
1293 virtual void SetUp() {
1294@@ -41,7 +43,7 @@
1295 g_test_dbus_up(testbus);
1296
1297 mainloop = g_main_loop_new(nullptr, FALSE);
1298- dispatcher_init(mainloop);
1299+ dispatcher_init(mainloop, reinterpret_cast<OverlayTracker *>(&tracker));
1300
1301 return;
1302 }
1303
1304=== modified file 'tests/dispatcher-test.cc'
1305--- tests/dispatcher-test.cc 2015-01-09 16:11:45 +0000
1306+++ tests/dispatcher-test.cc 2015-06-08 15:28:16 +0000
1307@@ -21,6 +21,7 @@
1308 #include <gtest/gtest.h>
1309 #include "dispatcher.h"
1310 #include "ubuntu-app-launch-mock.h"
1311+#include "overlay-tracker-mock.h"
1312 #include "url-db.h"
1313
1314 class DispatcherTest : public ::testing::Test
1315@@ -31,9 +32,13 @@
1316 gchar * cachedir = nullptr;
1317
1318 protected:
1319+ OverlayTrackerMock tracker;
1320+ GDBusConnection * session = nullptr;
1321+
1322 virtual void SetUp() {
1323 g_setenv("TEST_CLICK_DB", "click-db", TRUE);
1324 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
1325+ g_setenv("URL_DISPATCHER_OVERLAY_DIR", OVERLAY_TEST_DIR, TRUE);
1326
1327 cachedir = g_build_filename(CMAKE_BINARY_DIR, "dispatcher-test-cache", nullptr);
1328 g_setenv("URL_DISPATCHER_CACHE_DIR", cachedir, TRUE);
1329@@ -66,8 +71,10 @@
1330 testbus = g_test_dbus_new(G_TEST_DBUS_NONE);
1331 g_test_dbus_up(testbus);
1332
1333+ session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1334+
1335 mainloop = g_main_loop_new(nullptr, FALSE);
1336- dispatcher_init(mainloop);
1337+ dispatcher_init(mainloop, reinterpret_cast<OverlayTracker *>(&tracker));
1338
1339 return;
1340 }
1341@@ -87,6 +94,8 @@
1342 /* let other threads settle */
1343 g_usleep(500000);
1344
1345+ g_clear_object(&session);
1346+
1347 g_test_dbus_down(testbus);
1348 g_object_unref(testbus);
1349
1350@@ -250,3 +259,18 @@
1351
1352 return;
1353 }
1354+
1355+TEST_F(DispatcherTest, OverlayTest)
1356+{
1357+ EXPECT_TRUE(dispatcher_is_overlay("mock-overlay"));
1358+ EXPECT_FALSE(dispatcher_is_overlay("not-overlay"));
1359+
1360+ EXPECT_TRUE(dispatcher_send_to_overlay ("mock-overlay", "overlay://ubuntu.com", session, g_dbus_connection_get_unique_name(session)));
1361+
1362+ ASSERT_EQ(1, tracker.addedOverlays.size());
1363+ EXPECT_EQ("mock-overlay", std::get<0>(tracker.addedOverlays[0]));
1364+ EXPECT_EQ(getpid(), std::get<1>(tracker.addedOverlays[0]));
1365+ EXPECT_EQ("overlay://ubuntu.com", std::get<2>(tracker.addedOverlays[0]));
1366+
1367+ return;
1368+}
1369
1370=== added file 'tests/exec-tool-test.cc'
1371--- tests/exec-tool-test.cc 1970-01-01 00:00:00 +0000
1372+++ tests/exec-tool-test.cc 2015-06-08 15:28:16 +0000
1373@@ -0,0 +1,111 @@
1374+/**
1375+ * Copyright © 2013-2015 Canonical, Ltd.
1376+ *
1377+ * This program is free software: you can redistribute it and/or modify it under
1378+ * the terms of the GNU Lesser General Public License version 3, as published by
1379+ * the Free Software Foundation.
1380+ *
1381+ * This program is distributed in the hope that it will be useful, but WITHOUT
1382+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1383+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1384+ * Lesser General Public License for more details.
1385+ *
1386+ * You should have received a copy of the GNU Lesser General Public License
1387+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1388+ *
1389+ */
1390+
1391+#include "test-config.h"
1392+
1393+#include <gio/gio.h>
1394+#include <gtest/gtest.h>
1395+#include <libdbustest/dbus-test.h>
1396+
1397+class ExecToolTest : public ::testing::Test
1398+{
1399+ protected:
1400+ DbusTestService * service = nullptr;
1401+ DbusTestDbusMock * mock = nullptr;
1402+ DbusTestDbusMockObject * obj = nullptr;
1403+ GDBusConnection * bus = nullptr;
1404+
1405+ virtual void SetUp() {
1406+ g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);
1407+ g_setenv("URL_DISPATCHER_OVERLAY_DIR", OVERLAY_TEST_DIR, TRUE);
1408+
1409+ service = dbus_test_service_new(nullptr);
1410+
1411+ /* Upstart Mock */
1412+ mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");
1413+ obj = dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", nullptr);
1414+
1415+ dbus_test_dbus_mock_object_add_method(mock, obj,
1416+ "SetEnv",
1417+ G_VARIANT_TYPE("(assb)"),
1418+ NULL,
1419+ "",
1420+ NULL);
1421+
1422+ dbus_test_task_set_name(DBUS_TEST_TASK(mock), "Upstart");
1423+ dbus_test_service_add_task(service, DBUS_TEST_TASK(mock));
1424+
1425+ /* Start your engines! */
1426+ dbus_test_service_start_tasks(service);
1427+
1428+ bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
1429+ g_dbus_connection_set_exit_on_close(bus, FALSE);
1430+ g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus);
1431+ return;
1432+ }
1433+
1434+ virtual void TearDown() {
1435+ /* dbustest should probably do this, not sure */
1436+
1437+ g_clear_object(&mock);
1438+ g_clear_object(&service);
1439+
1440+ g_object_unref(bus);
1441+
1442+ unsigned int cleartry = 0;
1443+ while (bus != nullptr && cleartry < 100) {
1444+ pause(100);
1445+ cleartry++;
1446+ }
1447+
1448+ return;
1449+ }
1450+
1451+ static gboolean quit_loop (gpointer ploop) {
1452+ g_main_loop_quit((GMainLoop *)ploop);
1453+ return FALSE;
1454+ }
1455+
1456+ void pause (int time) {
1457+ GMainLoop * loop = g_main_loop_new(nullptr, FALSE);
1458+ g_timeout_add(time, quit_loop, loop);
1459+ g_main_loop_run(loop);
1460+ g_main_loop_unref(loop);
1461+ }
1462+};
1463+
1464+TEST_F(ExecToolTest, SetOverlay)
1465+{
1466+ g_unsetenv("APP_ID");
1467+ gint retval = 0;
1468+ EXPECT_TRUE(g_spawn_command_line_sync(EXEC_TOOL, nullptr, nullptr, &retval, nullptr));
1469+ EXPECT_NE(0, retval);
1470+
1471+ g_setenv("APP_ID", "mock-overlay", TRUE);
1472+ g_setenv("UPSTART_JOB", "fubar", TRUE);
1473+ EXPECT_TRUE(g_spawn_command_line_sync(EXEC_TOOL, nullptr, nullptr, &retval, nullptr));
1474+ EXPECT_EQ(0, retval);
1475+
1476+ guint len = 0;
1477+ const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
1478+ ASSERT_NE(nullptr, calls);
1479+ ASSERT_EQ(1, len);
1480+
1481+ GVariant * appexecenv = g_variant_get_child_value(calls[0].params, 1);
1482+ EXPECT_STREQ("APP_EXEC=foobar", g_variant_get_string(appexecenv, nullptr));
1483+ g_variant_unref(appexecenv);
1484+}
1485
1486=== added file 'tests/mir-mock.cpp'
1487--- tests/mir-mock.cpp 1970-01-01 00:00:00 +0000
1488+++ tests/mir-mock.cpp 2015-06-08 15:28:16 +0000
1489@@ -0,0 +1,124 @@
1490+/**
1491+ * Copyright © 2015 Canonical, Ltd.
1492+ *
1493+ * This program is free software: you can redistribute it and/or modify it under
1494+ * the terms of the GNU General Public License version 3, as published by
1495+ * the Free Software Foundation.
1496+ * * This program is distributed in the hope that it will be useful, but WITHOUT
1497+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1498+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1499+ * General Public License for more details.
1500+ *
1501+ * You should have received a copy of the GNU General Public License
1502+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1503+ *
1504+ */
1505+
1506+#include "mir-mock.h"
1507+
1508+#include <iostream>
1509+#include <thread>
1510+
1511+MirPromptSession * mir_mock_valid_trust_session = (MirPromptSession *)"In the circle of trust";
1512+static bool valid_trust_connection = true;
1513+static int trusted_fd = 1234;
1514+MirPromptSession * mir_mock_last_released_session = NULL;
1515+pid_t mir_mock_last_trust_pid = 0;
1516+void (*mir_mock_last_trust_func)(MirPromptSession *, MirPromptSessionState, void*data) = NULL;
1517+void * mir_mock_last_trust_data = NULL;
1518+
1519+MirPromptSession *
1520+mir_connection_create_prompt_session_sync(MirConnection * connection, pid_t pid, void (*func)(MirPromptSession *, MirPromptSessionState, void*data), void * context) {
1521+ mir_mock_last_trust_pid = pid;
1522+ mir_mock_last_trust_func = func;
1523+ mir_mock_last_trust_data = context;
1524+
1525+ if (valid_trust_connection) {
1526+ return mir_mock_valid_trust_session;
1527+ } else {
1528+ return nullptr;
1529+ }
1530+}
1531+
1532+void
1533+mir_prompt_session_release_sync (MirPromptSession * session)
1534+{
1535+ mir_mock_last_released_session = session;
1536+ if (session != mir_mock_valid_trust_session) {
1537+ std::cerr << "Releasing a Mir Trusted Prompt that isn't valid" << std::endl;
1538+ exit(1);
1539+ }
1540+}
1541+
1542+MirWaitHandle *
1543+mir_prompt_session_new_fds_for_prompt_providers (MirPromptSession * session, unsigned int numfds, mir_client_fd_callback cb, void * data) {
1544+ if (session != mir_mock_valid_trust_session) {
1545+ std::cerr << "Releasing a Mir Trusted Prompt that isn't valid" << std::endl;
1546+ exit(1);
1547+ }
1548+
1549+ /* TODO: Put in another thread to be more mir like */
1550+ std::thread * thread = new std::thread([session, numfds, cb, data]() {
1551+ int fdlist[numfds];
1552+
1553+ for (unsigned int i = 0; i < numfds; i++)
1554+ fdlist[i] = trusted_fd;
1555+
1556+ cb(session, numfds, fdlist, data);
1557+ });
1558+
1559+ return reinterpret_cast<MirWaitHandle *>(thread);
1560+}
1561+
1562+void
1563+mir_wait_for (MirWaitHandle * wait)
1564+{
1565+ auto thread = reinterpret_cast<std::thread *>(wait);
1566+
1567+ if (thread->joinable())
1568+ thread->join();
1569+
1570+ delete thread;
1571+}
1572+
1573+static const char * valid_connection_str = "Valid Mir Connection";
1574+static std::pair<std::string, std::string> last_connection;
1575+static bool valid_connection = true;
1576+
1577+void
1578+mir_mock_connect_return_valid (bool valid)
1579+{
1580+ valid_connection = valid;
1581+}
1582+
1583+std::pair<std::string, std::string>
1584+mir_mock_connect_last_connect (void)
1585+{
1586+ return last_connection;
1587+}
1588+
1589+MirConnection *
1590+mir_connect_sync (char const * server, char const * appname)
1591+{
1592+ last_connection = std::pair<std::string, std::string>(server, appname);
1593+
1594+ if (valid_connection) {
1595+ return (MirConnection *)(valid_connection_str);
1596+ } else {
1597+ return nullptr;
1598+ }
1599+}
1600+
1601+void
1602+mir_connection_release (MirConnection * con)
1603+{
1604+ if (reinterpret_cast<char *>(con) != valid_connection_str) {
1605+ std::cerr << "Releasing a Mir Connection that isn't valid" << std::endl;
1606+ exit(1);
1607+ }
1608+}
1609+
1610+void mir_mock_set_trusted_fd (int fd)
1611+{
1612+ trusted_fd = fd;
1613+}
1614
1615=== added file 'tests/mir-mock.h'
1616--- tests/mir-mock.h 1970-01-01 00:00:00 +0000
1617+++ tests/mir-mock.h 2015-06-08 15:28:16 +0000
1618@@ -0,0 +1,36 @@
1619+/**
1620+ * Copyright © 2015 Canonical, Ltd.
1621+ *
1622+ * This program is free software: you can redistribute it and/or modify it under
1623+ * the terms of the GNU General Public License version 3, as published by
1624+ * the Free Software Foundation.
1625+ * * This program is distributed in the hope that it will be useful, but WITHOUT
1626+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1627+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1628+ * General Public License for more details.
1629+ *
1630+ * You should have received a copy of the GNU General Public License
1631+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1632+ *
1633+ */
1634+
1635+#ifndef MIR_MOCK_H
1636+#define MIR_MOCK_H 1
1637+
1638+#include <string>
1639+#include <utility>
1640+
1641+#include <mir_toolkit/mir_connection.h>
1642+#include <mir_toolkit/mir_prompt_session.h>
1643+
1644+void mir_mock_connect_return_valid (bool valid);
1645+std::pair<std::string, std::string> mir_mock_connect_last_connect (void);
1646+void mir_mock_set_trusted_fd (int fd);
1647+
1648+extern MirPromptSession * mir_mock_valid_trust_session;
1649+extern MirPromptSession * mir_mock_last_released_session;
1650+extern pid_t mir_mock_last_trust_pid;
1651+extern void (*mir_mock_last_trust_func)(MirPromptSession *, MirPromptSessionState, void*data);
1652+extern void * mir_mock_last_trust_data;
1653+
1654+#endif // MIR_MOCK_H
1655
1656=== added directory 'tests/overlay-dir'
1657=== added file 'tests/overlay-dir/mock-overlay.desktop'
1658--- tests/overlay-dir/mock-overlay.desktop 1970-01-01 00:00:00 +0000
1659+++ tests/overlay-dir/mock-overlay.desktop 2015-06-08 15:28:16 +0000
1660@@ -0,0 +1,2 @@
1661+[Desktop Entry]
1662+Exec=foobar
1663
1664=== added file 'tests/overlay-tracker-mock.h'
1665--- tests/overlay-tracker-mock.h 1970-01-01 00:00:00 +0000
1666+++ tests/overlay-tracker-mock.h 2015-06-08 15:28:16 +0000
1667@@ -0,0 +1,30 @@
1668+/**
1669+ * Copyright © 2015 Canonical, Ltd.
1670+ *
1671+ * This program is free software: you can redistribute it and/or modify it under
1672+ * the terms of the GNU General Public License version 3, as published by
1673+ * the Free Software Foundation.
1674+ * * This program is distributed in the hope that it will be useful, but WITHOUT
1675+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1676+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1677+ * General Public License for more details.
1678+ *
1679+ * You should have received a copy of the GNU General Public License
1680+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1681+ *
1682+ */
1683+
1684+#pragma once
1685+#include "overlay-tracker-iface.h"
1686+#include <utility>
1687+
1688+class OverlayTrackerMock : public OverlayTrackerIface
1689+{
1690+ public:
1691+ std::vector<std::tuple<std::string, unsigned long, std::string>> addedOverlays;
1692+
1693+ bool addOverlay (const char * appid, unsigned long pid, const char * url) {
1694+ addedOverlays.push_back(std::make_tuple(std::string(appid), pid, std::string(url)));
1695+ return true;
1696+ }
1697+};
1698
1699=== added file 'tests/overlay-tracker-test.cpp'
1700--- tests/overlay-tracker-test.cpp 1970-01-01 00:00:00 +0000
1701+++ tests/overlay-tracker-test.cpp 2015-06-08 15:28:16 +0000
1702@@ -0,0 +1,127 @@
1703+/**
1704+ * Copyright © 2015 Canonical, Ltd.
1705+ *
1706+ * This program is free software: you can redistribute it and/or modify it under
1707+ * the terms of the GNU General Public License version 3, as published by
1708+ * the Free Software Foundation.
1709+ * * This program is distributed in the hope that it will be useful, but WITHOUT
1710+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1711+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1712+ * General Public License for more details.
1713+ *
1714+ * You should have received a copy of the GNU General Public License
1715+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1716+ *
1717+ */
1718+
1719+#include <random>
1720+
1721+#include "test-config.h"
1722+
1723+#include <gio/gio.h>
1724+#include <gtest/gtest.h>
1725+#include <libdbustest/dbus-test.h>
1726+
1727+#include "overlay-tracker-mir.h"
1728+#include "ubuntu-app-launch-mock.h"
1729+#include "mir-mock.h"
1730+
1731+class OverlayTrackerTest : public ::testing::Test
1732+{
1733+ protected:
1734+ virtual void SetUp() {
1735+ g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);
1736+
1737+ return;
1738+ }
1739+
1740+ virtual void TearDown() {
1741+ return;
1742+ }
1743+
1744+ static gboolean quit_loop (gpointer ploop) {
1745+ g_main_loop_quit((GMainLoop *)ploop);
1746+ return FALSE;
1747+ }
1748+
1749+ void pause (int time) {
1750+ GMainLoop * loop = g_main_loop_new(nullptr, FALSE);
1751+ g_timeout_add(time, quit_loop, loop);
1752+ g_main_loop_run(loop);
1753+ g_main_loop_unref(loop);
1754+ }
1755+};
1756+
1757+TEST_F(OverlayTrackerTest, BasicCreation) {
1758+ auto tracker = new OverlayTrackerMir();
1759+ delete tracker;
1760+}
1761+
1762+TEST_F(OverlayTrackerTest, AddOverlay) {
1763+ auto tracker = new OverlayTrackerMir();
1764+
1765+ auto mirconn = mir_mock_connect_last_connect();
1766+ EXPECT_EQ("mir_socket_trusted", mirconn.first.substr(mirconn.first.size() - 18));
1767+ EXPECT_EQ("url-dispatcher", mirconn.second);
1768+
1769+ EXPECT_TRUE(tracker->addOverlay("app-id", 5, "http://no-name-yet.com"));
1770+
1771+ EXPECT_EQ(5, mir_mock_last_trust_pid);
1772+
1773+ EXPECT_STREQ("url-overlay", ubuntu_app_launch_mock_last_start_session_helper);
1774+ EXPECT_STREQ("app-id", ubuntu_app_launch_mock_last_start_session_appid);
1775+ EXPECT_STREQ("http://no-name-yet.com", ubuntu_app_launch_mock_last_start_session_uris[0]);
1776+
1777+ delete tracker;
1778+
1779+ EXPECT_STREQ("url-overlay", ubuntu_app_launch_mock_last_stop_helper);
1780+ EXPECT_STREQ("app-id", ubuntu_app_launch_mock_last_stop_appid);
1781+ EXPECT_STREQ("instance", ubuntu_app_launch_mock_last_stop_instance);
1782+}
1783+
1784+TEST_F(OverlayTrackerTest, OverlayABunch) {
1785+ OverlayTrackerMir tracker;
1786+ std::uniform_int_distribution<> randpid(1, 32000);
1787+ std::mt19937 rand;
1788+
1789+ /* Testing adding a bunch of overlays, we're using pretty standard
1790+ data structures, but let's make sure we didn't break 'em */
1791+ for (auto name : std::vector<std::string>{"warty", "hoary", "breezy", "dapper", "edgy", "feisty", "gutsy", "hardy", "intrepid", "jaunty", "karmic", "lucid", "maverick", "natty", "oneiric", "precise", "quantal", "raring", "saucy", "trusty", "utopic", "vivid", "wily"}) {
1792+ int pid = randpid(rand);
1793+ tracker.addOverlay(name.c_str(), pid, "http://ubuntu.com/releases");
1794+
1795+ EXPECT_EQ(pid, mir_mock_last_trust_pid);
1796+ EXPECT_EQ(name, ubuntu_app_launch_mock_last_start_session_appid);
1797+ }
1798+}
1799+
1800+TEST_F(OverlayTrackerTest, UALSignalStop) {
1801+ OverlayTrackerMir tracker;
1802+
1803+ /* Call with the overlay before it is set */
1804+ ubuntu_app_launch_mock_observer_helper_stop_func("app-id", "instance", "url-overlay", ubuntu_app_launch_mock_observer_helper_stop_user_data);
1805+
1806+ EXPECT_TRUE(tracker.addOverlay("app-id", 5, "http://no-name-yet.com"));
1807+
1808+ mir_mock_last_released_session = nullptr;
1809+ ubuntu_app_launch_mock_observer_helper_stop_func("app-id", "instance", "url-overlay", ubuntu_app_launch_mock_observer_helper_stop_user_data);
1810+ EXPECT_NE(nullptr, mir_mock_last_released_session);
1811+}
1812+
1813+TEST_F(OverlayTrackerTest, MirSignalStop) {
1814+ OverlayTrackerMir tracker;
1815+
1816+ EXPECT_TRUE(tracker.addOverlay("app-id", 5, "http://no-name-yet.com"));
1817+
1818+ /* Try a badie */
1819+ mir_mock_last_trust_func((MirPromptSession *)1337, mir_prompt_session_state_stopped, mir_mock_last_trust_data);
1820+
1821+ EXPECT_NE(nullptr, mir_mock_last_trust_func);
1822+ mir_mock_last_trust_func(mir_mock_valid_trust_session, mir_prompt_session_state_stopped, mir_mock_last_trust_data);
1823+
1824+ pause(100);
1825+
1826+ EXPECT_STREQ("url-overlay", ubuntu_app_launch_mock_last_stop_helper);
1827+ EXPECT_STREQ("app-id", ubuntu_app_launch_mock_last_stop_appid);
1828+ EXPECT_STREQ("instance", ubuntu_app_launch_mock_last_stop_instance);
1829+}
1830
1831=== modified file 'tests/service-test.cc'
1832--- tests/service-test.cc 2014-10-28 12:46:32 +0000
1833+++ tests/service-test.cc 2015-06-08 15:28:16 +0000
1834@@ -39,6 +39,7 @@
1835 g_setenv("UBUNTU_APP_LAUNCH_USE_SESSION", "1", TRUE);
1836 g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);
1837 g_setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, TRUE);
1838+ g_setenv("LD_PRELOAD", MIR_MOCK_PATH, TRUE);
1839
1840 SetUpDb();
1841
1842@@ -94,10 +95,6 @@
1843 }
1844
1845 virtual void TearDown() {
1846- /* dbustest should probably do this, not sure */
1847- kill(dbus_test_process_get_pid(dispatcher), SIGTERM);
1848- g_usleep(50000);
1849-
1850 g_clear_object(&dispatcher);
1851 g_clear_object(&mock);
1852 g_clear_object(&dashmock);
1853@@ -107,9 +104,7 @@
1854
1855 unsigned int cleartry = 0;
1856 while (bus != nullptr && cleartry < 100) {
1857- g_usleep(100000);
1858- while (g_main_pending())
1859- g_main_iteration(TRUE);
1860+ pause(100);
1861 cleartry++;
1862 }
1863
1864@@ -249,7 +244,7 @@
1865 }
1866
1867 void
1868-focus_signal_cb (GDBusConnection */*connection*/, const gchar */*sender_name*/, const gchar */*object_path*/, const gchar */*interface_name*/, const gchar */*signal_name*/, GVariant */*parameters*/, gpointer user_data)
1869+focus_signal_cb (GDBusConnection * /*connection*/, const gchar * /*sender_name*/, const gchar * /*object_path*/, const gchar * /*interface_name*/, const gchar * /*signal_name*/, GVariant * /*parameters*/, gpointer user_data)
1870 {
1871 guint * focus_count = (guint *)user_data;
1872 *focus_count = *focus_count + 1;
1873
1874=== modified file 'tests/test-config.h.in'
1875--- tests/test-config.h.in 2015-01-09 16:11:45 +0000
1876+++ tests/test-config.h.in 2015-06-08 15:28:16 +0000
1877@@ -10,3 +10,6 @@
1878 #define UPDATE_DIRECTORY_URLS "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-simple"
1879 #define UPDATE_DIRECTORY_VARIED "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-varied"
1880 #define UPDATE_DIRECTORY_INTENT "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-intent"
1881+#define OVERLAY_TEST_DIR "@CMAKE_CURRENT_SOURCE_DIR@/overlay-dir"
1882+#define EXEC_TOOL "@CMAKE_BINARY_DIR@/service/exec-tool"
1883+#define MIR_MOCK_PATH "@CMAKE_CURRENT_BINARY_DIR@/libmir-mock.so"
1884
1885=== modified file 'tests/ubuntu-app-launch-mock.c'
1886--- tests/ubuntu-app-launch-mock.c 2014-05-26 12:56:47 +0000
1887+++ tests/ubuntu-app-launch-mock.c 2015-06-08 15:28:16 +0000
1888@@ -41,3 +41,68 @@
1889 {
1890 return last_appid;
1891 }
1892+
1893+UbuntuAppLaunchHelperObserver ubuntu_app_launch_mock_observer_helper_stop_func = NULL;
1894+gchar * ubuntu_app_launch_mock_observer_helper_stop_type = NULL;
1895+void * ubuntu_app_launch_mock_observer_helper_stop_user_data = NULL;
1896+
1897+gboolean
1898+ubuntu_app_launch_observer_add_helper_stop (UbuntuAppLaunchHelperObserver func, const gchar * type, gpointer user_data)
1899+{
1900+ ubuntu_app_launch_mock_observer_helper_stop_func = func;
1901+ ubuntu_app_launch_mock_observer_helper_stop_type = g_strdup(type);
1902+ ubuntu_app_launch_mock_observer_helper_stop_user_data = user_data;
1903+
1904+ return TRUE;
1905+}
1906+
1907+gboolean
1908+ubuntu_app_launch_observer_delete_helper_stop (UbuntuAppLaunchHelperObserver func, const gchar * type, gpointer user_data)
1909+{
1910+ gboolean same = ubuntu_app_launch_mock_observer_helper_stop_func == func &&
1911+ g_strcmp0(ubuntu_app_launch_mock_observer_helper_stop_type, type) == 0 &&
1912+ ubuntu_app_launch_mock_observer_helper_stop_user_data == user_data;
1913+
1914+ ubuntu_app_launch_mock_observer_helper_stop_func = NULL;
1915+ g_clear_pointer(&ubuntu_app_launch_mock_observer_helper_stop_type, g_free);
1916+ ubuntu_app_launch_mock_observer_helper_stop_user_data = NULL;
1917+
1918+ return same;
1919+}
1920+
1921+gchar * ubuntu_app_launch_mock_last_start_session_helper = NULL;
1922+MirPromptSession * ubuntu_app_launch_mock_last_start_session_session = NULL;
1923+gchar * ubuntu_app_launch_mock_last_start_session_appid = NULL;
1924+gchar ** ubuntu_app_launch_mock_last_start_session_uris = NULL;
1925+
1926+gchar *
1927+ubuntu_app_launch_start_session_helper (const gchar * type, MirPromptSession * session, const gchar * appid, const gchar * const * uris)
1928+{
1929+ g_clear_pointer(&ubuntu_app_launch_mock_last_start_session_helper, g_free);
1930+ g_clear_pointer(&ubuntu_app_launch_mock_last_start_session_appid, g_free);
1931+ g_clear_pointer(&ubuntu_app_launch_mock_last_start_session_uris, g_strfreev);
1932+
1933+ ubuntu_app_launch_mock_last_start_session_helper = g_strdup(type);
1934+ ubuntu_app_launch_mock_last_start_session_session = session;
1935+ ubuntu_app_launch_mock_last_start_session_appid = g_strdup(appid);
1936+ ubuntu_app_launch_mock_last_start_session_uris = g_strdupv((gchar **)uris);
1937+
1938+ return g_strdup("instance");
1939+}
1940+
1941+gchar * ubuntu_app_launch_mock_last_stop_helper = NULL;
1942+gchar * ubuntu_app_launch_mock_last_stop_appid = NULL;
1943+gchar * ubuntu_app_launch_mock_last_stop_instance = NULL;
1944+
1945+gboolean
1946+ubuntu_app_launch_stop_multiple_helper (const gchar * helper_type, const gchar * appid, const gchar * instance) {
1947+ g_clear_pointer(&ubuntu_app_launch_mock_last_stop_helper, g_free);
1948+ g_clear_pointer(&ubuntu_app_launch_mock_last_stop_appid, g_free);
1949+ g_clear_pointer(&ubuntu_app_launch_mock_last_stop_instance, g_free);
1950+
1951+ ubuntu_app_launch_mock_last_stop_helper = g_strdup(helper_type);
1952+ ubuntu_app_launch_mock_last_stop_appid = g_strdup(appid);
1953+ ubuntu_app_launch_mock_last_stop_instance = g_strdup(instance);
1954+
1955+ return TRUE;
1956+}
1957
1958=== modified file 'tests/ubuntu-app-launch-mock.h'
1959--- tests/ubuntu-app-launch-mock.h 2014-10-24 18:55:11 +0000
1960+++ tests/ubuntu-app-launch-mock.h 2015-06-08 15:28:16 +0000
1961@@ -19,12 +19,24 @@
1962 #define UPSTART_APP_LAUNCH_MOCK 1
1963
1964 #include <glib.h>
1965+#include <ubuntu-app-launch.h>
1966
1967 G_BEGIN_DECLS
1968
1969 void ubuntu_app_launch_mock_clear_last_app_id (void);
1970 gchar * ubuntu_app_launch_mock_get_last_app_id (void);
1971
1972+extern UbuntuAppLaunchHelperObserver ubuntu_app_launch_mock_observer_helper_stop_func;
1973+extern gchar * ubuntu_app_launch_mock_observer_helper_stop_type;
1974+extern void * ubuntu_app_launch_mock_observer_helper_stop_user_data;
1975+extern gchar * ubuntu_app_launch_mock_last_start_session_helper;
1976+extern MirPromptSession * ubuntu_app_launch_mock_last_start_session_session;
1977+extern gchar * ubuntu_app_launch_mock_last_start_session_appid;
1978+extern gchar ** ubuntu_app_launch_mock_last_start_session_uris;
1979+extern gchar * ubuntu_app_launch_mock_last_stop_helper;
1980+extern gchar * ubuntu_app_launch_mock_last_stop_appid;
1981+extern gchar * ubuntu_app_launch_mock_last_stop_instance;
1982+
1983 G_END_DECLS
1984
1985 #endif /* UPSTART_APP_LAUNCH_MOCK */

Subscribers

People subscribed via source and target branches