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

Proposed by Ted Gould
Status: Merged
Approved by: Ted Gould
Approved revision: 138
Merged at revision: 86
Proposed branch: lp:~ted/url-dispatcher/url-overlay
Merge into: lp:url-dispatcher/15.10
Prerequisite: lp:~ted/url-dispatcher/upstart-build-dep
Diff against target: 2046 lines (+1506/-41)
34 files modified
CMakeLists.txt (+5/-2)
data/CMakeLists.txt (+7/-2)
data/url-dispatcher.url-overlay.click-hook.in (+3/-0)
debian/control (+2/-1)
debian/rules (+5/-0)
debian/url-dispatcher.install (+1/-0)
service/CMakeLists.txt (+26/-2)
service/dispatcher.c (+96/-13)
service/dispatcher.h (+6/-3)
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 (+49/-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-db.c (+1/-1)
service/url-db.h (+1/-1)
service/url-overlay.c (+174/-0)
tests/CMakeLists.txt (+47/-1)
tests/app-id-test.cc (+3/-1)
tests/dispatcher-test.cc (+25/-1)
tests/exec-tool-test.cc (+118/-0)
tests/mir-mock.cpp (+124/-0)
tests/mir-mock.h (+36/-0)
tests/overlay-dir/com.test.good_application_1.2.3.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 (+4/-0)
tests/ubuntu-app-launch-mock.c (+67/-2)
tests/ubuntu-app-launch-mock.h (+14/-2)
To merge this branch: bzr merge lp:~ted/url-dispatcher/url-overlay
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Charles Kerr (community) Approve
Review via email: mp+261415@code.launchpad.net

This proposal supersedes a proposal from 2015-05-17.

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 : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~ted/url-dispatcher/url-overlay updated
132. By Ted Gould

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

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

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

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

Allow dir to be NULL

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Some questions/comments inline, no blockers though.

review: Approve
Revision history for this message
Ted Gould (ted) wrote :

On Thu, 2015-06-11 at 17:26 +0000, Charles Kerr wrote:

> Some questions/comments inline, no blockers though.

Great, thanks for reviewing, long MR!

> > + -lpthread
>
> -lpthread reportedly doesn't work on clang, maybe use -pthread instead here?

Oh, didn't realize that. Fixed r135.

> > +ContextThread::~ContextThread (void)
>
> not a blocker, but the 'void' isn't needed in C++ for functions taking no args

Not *needed* but enjoyed. Removed r136.

> > + void removeSession (MirPromptSession * session);
> > +
> > + static void sessionStateChangedStatic (MirPromptSession * session, MirPromptSessionState state, void * user_data);
> > + void sessionStateChanged (MirPromptSession * session, MirPromptSessionState state);
> > +
> > + static void untrustedHelperStoppedStatic (const gchar * appid, const gchar * instanceid, const gchar * helpertype, gpointer user_data);
> > + void untrustedHelperStopped(const gchar * appid, const gchar * instanceid, const gchar * helpertype);
>
> These five should be private

Fixed r137.

> > + OverlayTrackerMir * cpptracker = new OverlayTrackerMir();
> > + return reinterpret_cast<OverlayTracker *>(cpptracker);
>
> I don't think reinterpret_cast<> is needed here; you should be able to upcast implicitly; e.g.
>
> try {
> return new OverlayTrackerMir();
> }
> catch(...) {
> return nullptr;
> }

We're not actually upcasting here, we're casting to an anonymous C
pointer, so that's why we need it. Trying to merge the C and C++ worlds.

> > + } catch (...) {
> > + return nullptr;
> > + }
> > +}
> > +
> > +void
> > +overlay_tracker_delete (OverlayTracker * tracker) {
> > + g_return_if_fail(tracker != nullptr);
> > +
> > + auto cpptracker = reinterpret_cast<OverlayTrackerMir *>(tracker);
> > + delete cpptracker;
>
> no need to reinterpret_cast here either; "delete tracker" should suffice

Same, OverlayTracker isn't a C++ object so it can't be deleted.

> > + /* Allow disabling for testing, we don't want to report bugs on
> > + our tests ;-) */
> > + if (G_UNLIKELY(g_getenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR") != NULL)) {
> > + return;
> > + }
> > +
>
> :-)

As an aside I built this into the libwhoopsie version as well. Make sure
to use it :-)

> > GError * error = NULL;
> > gint error_stdin = 0;
> > GPid pid = 0;
> >
> > === modified file 'service/service.c'
> > --- service/service.c 2014-03-14 19:50:37 +0000
> > +++ service/service.c 2015-06-08 20:42:15 +0000
> > @@ -39,13 +39,15 @@
> >
> > guint term_source = g_unix_signal_add(SIGTERM, sig_term, mainloop);
> >
> > - dispatcher_init(mainloop);
> > + OverlayTracker * tracker = overlay_tracker_new();
> > + dispatcher_init(mainloop, tracker);
>
> Looks like dispatcher.c is the only user of this tracker; why not make it a private variable there, instantiated by dispatcher_init() and released by dispatcher_shutdown()?

Mostly to make the injection of the mock easier in the dispatcher tests.
It doesn't look as nice in C as it does in C++ when you do that. Would
be nice to change main.c to main.cpp, but this MR is big enough ☺

Also fixed a couple warning I hadn't noticed before, r138.

lp:~ted/url-dispatcher/url-overlay updated
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

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-10-28 12:46:32 +0000
+++ CMakeLists.txt 2015-06-12 15:51:31 +0000
@@ -52,7 +52,7 @@
52 set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -Werror")52 set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
53endif() 53endif()
5454
55pkg_check_modules(UBUNTU_APP_LAUNCH REQUIRED ubuntu-app-launch-2)55pkg_check_modules(UBUNTU_APP_LAUNCH REQUIRED ubuntu-app-launch-2>=0.5)
56include_directories(${UBUNTU_APP_LAUNCH_INCLUDE_DIRS})56include_directories(${UBUNTU_APP_LAUNCH_INCLUDE_DIRS})
5757
58pkg_check_modules(GLIB2 REQUIRED glib-2.0)58pkg_check_modules(GLIB2 REQUIRED glib-2.0)
@@ -76,6 +76,9 @@
76pkg_check_modules(SQLITE REQUIRED sqlite3)76pkg_check_modules(SQLITE REQUIRED sqlite3)
77include_directories(${SQLITE_INCLUDE_DIRS})77include_directories(${SQLITE_INCLUDE_DIRS})
7878
79pkg_check_modules(CLICK REQUIRED click-0.4)
80include_directories(${CLICK_INCLUDE_DIRS})
81
79if(${LOCAL_INSTALL})82if(${LOCAL_INSTALL})
80 set(DBUSSERVICEDIR "${CMAKE_INSTALL_DATADIR}/dbus-1/services/")83 set(DBUSSERVICEDIR "${CMAKE_INSTALL_DATADIR}/dbus-1/services/")
81else()84else()
@@ -105,7 +108,7 @@
105if (${enable_tests})108if (${enable_tests})
106 set (GTEST_SOURCE_DIR /usr/src/gtest/src)109 set (GTEST_SOURCE_DIR /usr/src/gtest/src)
107 set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..)110 set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..)
108 set (GTEST_LIBS -lpthread)111 set (GTEST_LIBS -pthread)
109 enable_testing ()112 enable_testing ()
110 if (${enable_lcov})113 if (${enable_lcov})
111# include(GCov)114# include(GCov)
112115
=== modified file 'data/CMakeLists.txt'
--- data/CMakeLists.txt 2014-08-20 18:58:49 +0000
+++ data/CMakeLists.txt 2015-06-12 15:51:31 +0000
@@ -92,8 +92,13 @@
92# Click Hook92# Click Hook
93###########################93###########################
9494
95configure_file("url-dispatcher.click-hook.in"95configure_file("url-dispatcher.urls.click-hook.in"
96 "${CMAKE_SOURCE_DIR}/debian/url-dispatcher.click-hook"96 "${CMAKE_SOURCE_DIR}/debian/url-dispatcher.urls.click-hook"
97 @ONLY
98)
99
100configure_file("url-dispatcher.url-overlay.click-hook.in"
101 "${CMAKE_SOURCE_DIR}/debian/url-dispatcher.url-overlay.click-hook"
97 @ONLY102 @ONLY
98)103)
99104
100105
=== added file 'data/url-dispatcher.url-overlay.click-hook.in'
--- data/url-dispatcher.url-overlay.click-hook.in 1970-01-01 00:00:00 +0000
+++ data/url-dispatcher.url-overlay.click-hook.in 2015-06-12 15:51:31 +0000
@@ -0,0 +1,3 @@
1Pattern: ${home}/.cache/url-dispatcher/url-overlays/${id}.desktop
2User-Level: yes
3Hook-Name: url-overlay
04
=== renamed file 'data/url-dispatcher.click-hook.in' => 'data/url-dispatcher.urls.click-hook.in'
=== modified file 'debian/control'
--- debian/control 2015-06-12 15:51:30 +0000
+++ debian/control 2015-06-12 15:51:31 +0000
@@ -9,13 +9,14 @@
9 dh-autoreconf,9 dh-autoreconf,
10 gtester2xunit,10 gtester2xunit,
11 intltool,11 intltool,
12 libclick-0.4-dev,
12 libdbus-1-dev,13 libdbus-1-dev,
13 libdbustest1-dev (>= 14.04.0),14 libdbustest1-dev (>= 14.04.0),
14 libglib2.0-dev,15 libglib2.0-dev,
15 libjson-glib-dev,16 libjson-glib-dev,
16 libgtest-dev,17 libgtest-dev,
17 libsqlite3-dev,18 libsqlite3-dev,
18 libubuntu-app-launch2-dev (>= 0.3),19 libubuntu-app-launch2-dev (>= 0.5),
19 python3,20 python3,
20 python3-dbusmock,21 python3-dbusmock,
21 python3-fixtures,22 python3-fixtures,
2223
=== modified file 'debian/rules'
--- debian/rules 2014-07-20 11:45:00 +0000
+++ debian/rules 2015-06-12 15:51:31 +0000
@@ -2,6 +2,7 @@
22
3export DPKG_GENSYMBOLS_CHECK_LEVEL = 43export DPKG_GENSYMBOLS_CHECK_LEVEL = 4
4export G_MESSAGES_DEBUG=all4export G_MESSAGES_DEBUG=all
5export URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR=1
56
6%:7%:
7 dh $@ --fail-missing --with click8 dh $@ --fail-missing --with click
@@ -12,3 +13,7 @@
12 mkdir -p debian/url-dispatcher/etc/apport/crashdb.conf.d/13 mkdir -p debian/url-dispatcher/etc/apport/crashdb.conf.d/
13 install -m 644 debian/url-dispatcher-crashdb.conf debian/url-dispatcher/etc/apport/crashdb.conf.d/14 install -m 644 debian/url-dispatcher-crashdb.conf debian/url-dispatcher/etc/apport/crashdb.conf.d/
14 dh_install --fail-missing15 dh_install --fail-missing
16
17override_dh_click:
18 dh_click --name urls
19 dh_click --name url-overlay
1520
=== modified file 'debian/url-dispatcher.install'
--- debian/url-dispatcher.install 2013-06-28 16:35:12 +0000
+++ debian/url-dispatcher.install 2015-06-12 15:51:31 +0000
@@ -1,3 +1,4 @@
1usr/lib/*/url-dispatcher1usr/lib/*/url-dispatcher
2usr/lib/*/ubuntu-app-launch/url-overlay/exec-tool
2usr/share/dbus-13usr/share/dbus-1
3usr/share/upstart/sessions4usr/share/upstart/sessions
45
=== modified file 'service/CMakeLists.txt'
--- service/CMakeLists.txt 2014-05-26 12:56:47 +0000
+++ service/CMakeLists.txt 2015-06-12 15:51:31 +0000
@@ -2,6 +2,8 @@
2include(UseConstantBuilder)2include(UseConstantBuilder)
3include_directories(${CMAKE_CURRENT_SOURCE_DIR})3include_directories(${CMAKE_CURRENT_SOURCE_DIR})
44
5add_definitions( -DOVERLAY_SYSTEM_DIRECTORY="${CMAKE_INSTALL_FULL_DATADIR}/url-dispatcher/url-overlays" )
6
5###########################7###########################
6# Generated Lib8# Generated Lib
7###########################9###########################
@@ -38,15 +40,24 @@
3840
39add_library(dispatcher-lib STATIC41add_library(dispatcher-lib STATIC
40 dispatcher.h42 dispatcher.h
41 dispatcher.c)43 dispatcher.c
44 glib-thread.h
45 glib-thread.cpp
46 overlay-tracker.h
47 overlay-tracker.cpp
48 overlay-tracker-iface.h
49 overlay-tracker-mir.h
50 overlay-tracker-mir.cpp)
4251
43target_link_libraries(dispatcher-lib52target_link_libraries(dispatcher-lib
44 url-db-lib53 url-db-lib
45 service-generated54 service-generated
55 -pthread
46 ${GLIB2_LIBRARIES}56 ${GLIB2_LIBRARIES}
47 ${GOBJECT2_LIBRARIES}57 ${GOBJECT2_LIBRARIES}
48 ${GIO2_LIBRARIES}58 ${GIO2_LIBRARIES}
49 ${SQLITE_LIBRARIES}59 ${SQLITE_LIBRARIES}
60 ${UBUNTU_APP_LAUNCH_LIBRARIES}
50)61)
5162
52###########################63###########################
@@ -84,7 +95,7 @@
8495
85set_target_properties(service-exec PROPERTIES OUTPUT_NAME "url-dispatcher")96set_target_properties(service-exec PROPERTIES OUTPUT_NAME "url-dispatcher")
8697
87target_link_libraries(service-exec dispatcher-lib ${UBUNTU_APP_LAUNCH_LIBRARIES})98target_link_libraries(service-exec dispatcher-lib)
8899
89###########################100###########################
90# Update Directory101# Update Directory
@@ -95,6 +106,14 @@
95target_link_libraries(update-directory ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} url-db-lib)106target_link_libraries(update-directory ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} url-db-lib)
96107
97###########################108###########################
109# URL Overlay Exec Tool
110###########################
111
112add_executable(url-overlay-exec-tool url-overlay.c recoverable-problem.c)
113set_target_properties(url-overlay-exec-tool PROPERTIES OUTPUT_NAME "exec-tool")
114target_link_libraries(url-overlay-exec-tool ${GIO2_LIBRARIES} ${UBUNTU_APP_LAUNCH_LIBRARIES} ${CLICK_LIBRARIES})
115
116###########################
98# Installation117# Installation
99###########################118###########################
100119
@@ -103,3 +122,8 @@
103 RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/url-dispatcher"122 RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/url-dispatcher"
104)123)
105124
125install(
126 TARGETS url-overlay-exec-tool
127 RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/ubuntu-app-launch/url-overlay"
128)
129
106130
=== modified file 'service/dispatcher.c'
--- service/dispatcher.c 2015-01-23 14:34:30 +0000
+++ service/dispatcher.c 2015-06-12 15:51:31 +0000
@@ -26,6 +26,7 @@
26#include "url-db.h"26#include "url-db.h"
2727
28/* Globals */28/* Globals */
29static OverlayTracker * tracker = NULL;
29static GCancellable * cancellable = NULL;30static GCancellable * cancellable = NULL;
30static ServiceIfaceComCanonicalURLDispatcher * skel = NULL;31static ServiceIfaceComCanonicalURLDispatcher * skel = NULL;
31static GRegex * applicationre = NULL;32static GRegex * applicationre = NULL;
@@ -44,7 +45,7 @@
4445
45/* Register our errors */46/* Register our errors */
46static void47static void
47register_dbus_errors (void)48register_dbus_errors ()
48{49{
49 g_dbus_error_register_error(url_dispatcher_error_quark(), ERROR_BAD_URL, "com.canonical.URLDispatcher.BadURL");50 g_dbus_error_register_error(url_dispatcher_error_quark(), ERROR_BAD_URL, "com.canonical.URLDispatcher.BadURL");
50 g_dbus_error_register_error(url_dispatcher_error_quark(), ERROR_RESTRICTED_URL, "com.canonical.URLDispatcher.RestrictedURL");51 g_dbus_error_register_error(url_dispatcher_error_quark(), ERROR_RESTRICTED_URL, "com.canonical.URLDispatcher.RestrictedURL");
@@ -78,11 +79,7 @@
78 NULL79 NULL
79 };80 };
8081
81 /* Allow disabling for testing, we don't want to report bugs on82 report_recoverable_problem("url-dispatcher-bad-url", pid, FALSE, additional);
82 our tests ;-) */
83 if (g_getenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR") == NULL) {
84 report_recoverable_problem("url-dispatcher-bad-url", pid, FALSE, additional);
85 }
8683
87 g_free(badurl);84 g_free(badurl);
8885
@@ -111,7 +108,7 @@
111bad_url (GDBusMethodInvocation * invocation, const gchar * url)108bad_url (GDBusMethodInvocation * invocation, const gchar * url)
112{109{
113 const gchar * sender = g_dbus_method_invocation_get_sender(invocation);110 const gchar * sender = g_dbus_method_invocation_get_sender(invocation);
114 GDBusConnection * conn = g_dbus_method_invocation_get_connection(invocation);111 GDBusConnection * conn = g_dbus_method_invocation_get_connection(invocation); /* transfer: none */
115112
116 g_dbus_connection_call(conn,113 g_dbus_connection_call(conn,
117 "org.freedesktop.DBus",114 "org.freedesktop.DBus",
@@ -213,6 +210,73 @@
213 return TRUE;210 return TRUE;
214}211}
215212
213/* Handles setting up the overlay with the URL */
214gboolean
215dispatcher_send_to_overlay (const gchar * app_id, const gchar * url, GDBusConnection * conn, const gchar * sender)
216{
217 GError * error = NULL;
218
219 /* TODO: Detect if a scope is what we need to overlay on */
220 GVariant * callret = g_dbus_connection_call_sync(conn,
221 "org.freedesktop.DBus",
222 "/",
223 "org.freedesktop.DBus",
224 "GetConnectionUnixProcessID",
225 g_variant_new("(s)", sender),
226 G_VARIANT_TYPE("(u)"),
227 G_DBUS_CALL_FLAGS_NONE,
228 -1, /* timeout */
229 NULL, /* cancellable */
230 &error);
231
232 if (error != NULL) {
233 g_warning("Unable to get PID for '%s' when processing URL '%s': %s", sender, url, error->message);
234 g_error_free(error);
235 return FALSE;
236 }
237
238 unsigned int pid = 0;
239 g_variant_get_child(callret, 0, "u", &pid);
240 g_variant_unref(callret);
241
242 return overlay_tracker_add(tracker, app_id, pid, url);
243}
244
245/* Check to see if this is an overlay AppID */
246gboolean
247dispatcher_is_overlay (const gchar * appid)
248{
249 const gchar * systemdir = NULL;
250 gboolean found = FALSE;
251 gchar * desktopname = g_strdup_printf("%s.desktop", appid);
252
253 /* First time, check the environment */
254 if (G_UNLIKELY(systemdir == NULL)) {
255 systemdir = g_getenv("URL_DISPATCHER_OVERLAY_DIR");
256 if (systemdir == NULL) {
257 systemdir = OVERLAY_SYSTEM_DIRECTORY;
258 }
259 }
260
261 /* Check system dir */
262 if (!found) {
263 gchar * sysdir = g_build_filename(systemdir, desktopname, NULL);
264 found = g_file_test(sysdir, G_FILE_TEST_EXISTS);
265 g_free(sysdir);
266 }
267
268 /* Check user dir (clicks) */
269 if (!found) {
270 gchar * usrdir = g_build_filename(g_get_user_cache_dir(), "url-dispatcher", "url-overlays", desktopname, NULL);
271 found = g_file_test(usrdir, G_FILE_TEST_EXISTS);
272 g_free(usrdir);
273 }
274
275 g_free(desktopname);
276
277 return found;
278}
279
216/* Whether we should restrict this appid based on the package name */280/* Whether we should restrict this appid based on the package name */
217gboolean281gboolean
218dispatcher_appid_restrict (const gchar * appid, const gchar * package)282dispatcher_appid_restrict (const gchar * appid, const gchar * package)
@@ -277,12 +341,25 @@
277 }341 }
278342
279 /* We're cleared to continue */343 /* We're cleared to continue */
280 dispatcher_send_to_app(appid, outurl);344 gboolean sent = FALSE;
345 if (!dispatcher_is_overlay(appid)) {
346 sent = dispatcher_send_to_app(appid, outurl);
347 } else {
348 sent = dispatcher_send_to_overlay(
349 appid,
350 outurl,
351 g_dbus_method_invocation_get_connection(invocation),
352 g_dbus_method_invocation_get_sender(invocation));
353 }
281 g_free(appid);354 g_free(appid);
282355
283 g_dbus_method_invocation_return_value(invocation, NULL);356 if (sent) {
357 g_dbus_method_invocation_return_value(invocation, NULL);
358 } else {
359 bad_url(invocation, url);
360 }
284361
285 return TRUE;362 return sent;
286}363}
287364
288/* Test a URL to find it's AppID */365/* Test a URL to find it's AppID */
@@ -347,6 +424,9 @@
347gboolean424gboolean
348dispatcher_url_to_appid (const gchar * url, gchar ** out_appid, const gchar ** out_url)425dispatcher_url_to_appid (const gchar * url, gchar ** out_appid, const gchar ** out_url)
349{426{
427 g_return_val_if_fail(url != NULL, FALSE);
428 g_return_val_if_fail(out_appid != NULL, FALSE);
429
350 /* Special case the app id */430 /* Special case the app id */
351 GMatchInfo * appidmatch = NULL;431 GMatchInfo * appidmatch = NULL;
352 if (g_regex_match(appidre, url, 0, &appidmatch)) {432 if (g_regex_match(appidre, url, 0, &appidmatch)) {
@@ -398,7 +478,9 @@
398478
399 if (*out_appid != NULL) {479 if (*out_appid != NULL) {
400 found = TRUE;480 found = TRUE;
401 *out_url = url;481 if (out_url != NULL) {
482 *out_url = url;
483 }
402 }484 }
403485
404 g_free(protocol);486 g_free(protocol);
@@ -465,8 +547,9 @@
465547
466/* Initialize all the globals */548/* Initialize all the globals */
467gboolean549gboolean
468dispatcher_init (GMainLoop * mainloop)550dispatcher_init (GMainLoop * mainloop, OverlayTracker * intracker)
469{551{
552 tracker = intracker;
470 cancellable = g_cancellable_new();553 cancellable = g_cancellable_new();
471554
472 urldb = url_db_create_database();555 urldb = url_db_create_database();
@@ -487,7 +570,7 @@
487570
488/* Clean up all the globals */571/* Clean up all the globals */
489gboolean572gboolean
490dispatcher_shutdown (void)573dispatcher_shutdown ()
491{574{
492 g_cancellable_cancel(cancellable);575 g_cancellable_cancel(cancellable);
493576
494577
=== modified file 'service/dispatcher.h'
--- service/dispatcher.h 2014-10-24 18:55:11 +0000
+++ service/dispatcher.h 2015-06-12 15:51:31 +0000
@@ -20,15 +20,18 @@
20#ifndef DISPATCHER_H20#ifndef DISPATCHER_H
21#define DISPATCHER_H 121#define DISPATCHER_H 1
2222
23#include <glib.h>23#include <gio/gio.h>
24#include "overlay-tracker.h"
2425
25G_BEGIN_DECLS26G_BEGIN_DECLS
2627
27gboolean dispatcher_init (GMainLoop * mainloop);28gboolean dispatcher_init (GMainLoop * mainloop, OverlayTracker * tracker);
28gboolean dispatcher_shutdown (void);29gboolean dispatcher_shutdown ();
29gboolean dispatcher_url_to_appid (const gchar * url, gchar ** out_appid, const gchar ** out_url);30gboolean dispatcher_url_to_appid (const gchar * url, gchar ** out_appid, const gchar ** out_url);
30gboolean dispatcher_appid_restrict (const gchar * appid, const gchar * package);31gboolean dispatcher_appid_restrict (const gchar * appid, const gchar * package);
32gboolean dispatcher_is_overlay (const gchar * appid);
31gboolean dispatcher_send_to_app (const gchar * appid, const gchar * url);33gboolean dispatcher_send_to_app (const gchar * appid, const gchar * url);
34gboolean dispatcher_send_to_overlay (const gchar * app_id, const gchar * url, GDBusConnection * conn, const gchar * sender);
3235
33G_END_DECLS36G_END_DECLS
3437
3538
=== added file 'service/glib-thread.cpp'
--- service/glib-thread.cpp 1970-01-01 00:00:00 +0000
+++ service/glib-thread.cpp 2015-06-12 15:51:31 +0000
@@ -0,0 +1,179 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#include "glib-thread.h"
21
22namespace GLib
23{
24
25
26ContextThread::ContextThread (std::function<void()> beforeLoop, std::function<void()> afterLoop)
27 : _context(nullptr)
28 , _loop(nullptr)
29{
30 _cancel = std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable * cancel)
31 {
32 if (cancel != nullptr)
33 {
34 g_cancellable_cancel(cancel);
35 g_object_unref(cancel);
36 }
37 });
38 std::promise<std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>> context_promise;
39
40 /* NOTE: We copy afterLoop but reference beforeLoop. We're blocking so we
41 know that beforeLoop will stay valid long enough, but we can't say the
42 same for afterLoop */
43 _thread = std::thread([&context_promise, &beforeLoop, afterLoop, this]() -> void
44 {
45 /* Build up the context and loop for the async events and a place
46 for GDBus to send its events back to */
47 auto context = std::shared_ptr<GMainContext>(g_main_context_new(), [](GMainContext * context)
48 {
49 g_clear_pointer(&context, g_main_context_unref);
50 });
51 auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(context.get(), FALSE), [](GMainLoop * loop)
52 {
53 g_clear_pointer(&loop, g_main_loop_unref);
54 });
55
56 g_main_context_push_thread_default(context.get());
57
58 beforeLoop();
59
60 /* Free's the constructor to continue */
61 auto pair = std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>(context, loop);
62 context_promise.set_value(pair);
63
64 if (!g_cancellable_is_cancelled(_cancel.get()))
65 {
66 g_main_loop_run(loop.get());
67 }
68
69 afterLoop();
70 });
71
72 /* We need to have the context and the mainloop ready before
73 other functions on this object can work properly. So we wait
74 for them and set them on this thread. */
75 auto context_future = context_promise.get_future();
76 context_future.wait();
77 auto context_value = context_future.get();
78
79 _context = context_value.first;
80 _loop = context_value.second;
81
82 if (_context == nullptr || _loop == nullptr)
83 {
84 throw std::runtime_error("Unable to create GLib Thread");
85 }
86}
87
88ContextThread::~ContextThread ()
89{
90 quit();
91}
92
93void ContextThread::quit ()
94{
95 g_cancellable_cancel(_cancel.get()); /* Force the cancellation on ongoing tasks */
96 if (_loop != nullptr)
97 {
98 g_main_loop_quit(_loop.get()); /* Quit the loop */
99 }
100
101 /* Joining here because we want to ensure that the final afterLoop()
102 function is run before returning */
103 if (std::this_thread::get_id() != _thread.get_id())
104 {
105 if (_thread.joinable())
106 {
107 _thread.join();
108 }
109 }
110}
111
112bool ContextThread::isCancelled ()
113{
114 return g_cancellable_is_cancelled(_cancel.get()) == TRUE;
115}
116
117std::shared_ptr<GCancellable> ContextThread::getCancellable ()
118{
119 return _cancel;
120}
121
122void ContextThread::simpleSource (std::function<GSource * ()> srcBuilder, std::function<void()> work)
123{
124 if (isCancelled())
125 {
126 throw std::runtime_error("Trying to execute work on a GLib thread that is shutting down.");
127 }
128
129 /* Copy the work so that we can reuse it */
130 /* Lifecycle is handled with the source pointer when we attach
131 it to the context. */
132 auto heapWork = new std::function<void()>(work);
133
134 auto source = std::shared_ptr<GSource>(srcBuilder(),
135 [](GSource * src)
136 {
137 g_clear_pointer(&src, g_source_unref);
138 }
139 );
140 g_source_set_callback(source.get(),
141 [](gpointer data) -> gboolean
142 {
143 std::function<void()>* heapWork = reinterpret_cast<std::function<void()> *>(data);
144 (*heapWork)();
145 return G_SOURCE_REMOVE;
146 }, heapWork,
147 [](gpointer data)
148 {
149 std::function<void()>* heapWork = reinterpret_cast<std::function<void()> *>(data);
150 delete heapWork;
151 });
152
153 g_source_attach(source.get(), _context.get());
154}
155
156void ContextThread::executeOnThread (std::function<void()> work)
157{
158 simpleSource(g_idle_source_new, work);
159}
160
161void ContextThread::timeout (const std::chrono::milliseconds& length,
162 std::function<void()> work)
163{
164 simpleSource([length]()
165 {
166 return g_timeout_source_new(length.count());
167 }, work);
168}
169
170void ContextThread::timeoutSeconds (const std::chrono::seconds& length,
171 std::function<void()> work)
172{
173 simpleSource([length]()
174 {
175 return g_timeout_source_new_seconds(length.count());
176 }, work);
177}
178
179} // ns GLib
0180
=== added file 'service/glib-thread.h'
--- service/glib-thread.h 1970-01-01 00:00:00 +0000
+++ service/glib-thread.h 2015-06-12 15:51:31 +0000
@@ -0,0 +1,81 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#include <thread>
21#include <future>
22
23#include <gio/gio.h>
24
25namespace GLib
26{
27
28class ContextThread
29{
30 std::thread _thread;
31 std::shared_ptr<GMainContext> _context;
32 std::shared_ptr<GMainLoop> _loop;
33 std::shared_ptr<GCancellable> _cancel;
34
35public:
36 ContextThread (std::function<void()> beforeLoop = [] {}, std::function<void()> afterLoop = [] {});
37 ~ContextThread ();
38
39 void quit ();
40 bool isCancelled ();
41 std::shared_ptr<GCancellable> getCancellable ();
42
43 void executeOnThread (std::function<void()> work);
44 template<typename T> auto executeOnThread (std::function<T()> work) -> T
45 {
46 if (std::this_thread::get_id() == _thread.get_id())
47 {
48 /* Don't block if we're on the same thread */
49 return work();
50 }
51
52 std::promise<T> promise;
53 std::function<void()> magicFunc = [&promise, &work] () -> void {
54 promise.set_value(work());
55 };
56
57 executeOnThread(magicFunc);
58
59 auto future = promise.get_future();
60 future.wait();
61 return future.get();
62 }
63
64 void timeout (const std::chrono::milliseconds& length, std::function<void()> work);
65 template<class Rep, class Period> void timeout (const std::chrono::duration<Rep, Period>& length,
66 std::function<void()> work)
67 {
68 return timeout(std::chrono::duration_cast<std::chrono::milliseconds>(length), work);
69 }
70
71 void timeoutSeconds (const std::chrono::seconds& length, std::function<void()> work);
72 template<class Rep, class Period> void timeoutSeconds (const std::chrono::duration<Rep, Period>& length,
73 std::function<void()> work)
74 {
75 return timeoutSeconds(std::chrono::duration_cast<std::chrono::seconds>(length), work);
76 }
77
78private:
79 void simpleSource (std::function<GSource * ()> srcBuilder, std::function<void()> work);
80};
81}
082
=== added file 'service/overlay-tracker-iface.h'
--- service/overlay-tracker-iface.h 1970-01-01 00:00:00 +0000
+++ service/overlay-tracker-iface.h 2015-06-12 15:51:31 +0000
@@ -0,0 +1,26 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#pragma once
21
22class OverlayTrackerIface {
23public:
24 virtual ~OverlayTrackerIface() = default;
25 virtual bool addOverlay (const char * appid, unsigned long pid, const char * url) = 0;
26};
027
=== added file 'service/overlay-tracker-mir.cpp'
--- service/overlay-tracker-mir.cpp 1970-01-01 00:00:00 +0000
+++ service/overlay-tracker-mir.cpp 2015-06-12 15:51:31 +0000
@@ -0,0 +1,153 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#include "overlay-tracker-mir.h"
21#include <ubuntu-app-launch.h>
22
23static const char * HELPER_TYPE = "url-overlay";
24
25OverlayTrackerMir::OverlayTrackerMir ()
26 : thread([this] {
27 /* Setup Helper Observer */
28 ubuntu_app_launch_observer_add_helper_stop(untrustedHelperStoppedStatic, HELPER_TYPE, this);
29 },
30 [this] {
31 /* Remove Helper Observer */
32 ubuntu_app_launch_observer_delete_helper_stop(untrustedHelperStoppedStatic, HELPER_TYPE, this);
33 })
34{
35 mir = std::shared_ptr<MirConnection>([] {
36 gchar * path = g_build_filename(g_get_user_runtime_dir(), "mir_socket_trusted", NULL);
37 MirConnection * con = mir_connect_sync(path, "url-dispatcher");
38 g_free(path);
39 return con;
40 }(),
41 [] (MirConnection * connection) {
42 if (connection != nullptr)
43 mir_connection_release(connection);
44 });
45
46 if (!mir) {
47 throw std::runtime_error("Unable to connect to Mir");
48 }
49}
50
51/* Enforce a shutdown order, sessions before connection */
52OverlayTrackerMir::~OverlayTrackerMir ()
53{
54 thread.executeOnThread<bool>([this] {
55 while (!ongoingSessions.empty()) {
56 removeSession(std::get<2>(*ongoingSessions.begin()).get());
57 }
58
59 return true;
60 });
61
62 mir.reset();
63}
64
65bool
66OverlayTrackerMir::addOverlay (const char * appid, unsigned long pid, const char * url)
67{
68 std::string sappid(appid);
69 std::string surl(url);
70
71 return thread.executeOnThread<bool>([this, sappid, pid, surl] {
72 g_debug("Setting up over lay for PID %d with '%s'", pid, sappid.c_str());
73
74 auto session = std::shared_ptr<MirPromptSession>(
75 mir_connection_create_prompt_session_sync(mir.get(), pid, sessionStateChangedStatic, this),
76 [] (MirPromptSession * session) { if (session) mir_prompt_session_release_sync(session); });
77 if (!session) {
78 g_critical("Unable to create trusted prompt session for %d with appid '%s'", pid, sappid.c_str());
79 return false;
80 }
81
82 std::array<const char *, 2> urls { surl.c_str(), nullptr };
83 auto instance = ubuntu_app_launch_start_session_helper(HELPER_TYPE, session.get(), sappid.c_str(), urls.data());
84 if (instance == nullptr) {
85 g_critical("Unable to start helper for %d with appid '%s'", pid, sappid.c_str());
86 return false;
87 }
88
89 ongoingSessions.emplace(std::make_tuple(sappid, std::string(instance), session));
90 g_free(instance);
91 return true;
92 });
93}
94
95void
96OverlayTrackerMir::sessionStateChangedStatic (MirPromptSession * session, MirPromptSessionState state, void * user_data)
97{
98 reinterpret_cast<OverlayTrackerMir *>(user_data)->sessionStateChanged(session, state);
99}
100
101void
102OverlayTrackerMir::removeSession (MirPromptSession * session)
103{
104 for (auto it = ongoingSessions.begin(); it != ongoingSessions.end(); it++) {
105 if (std::get<2>(*it).get() == session) {
106 ubuntu_app_launch_stop_multiple_helper(HELPER_TYPE, std::get<0>(*it).c_str(), std::get<1>(*it).c_str());
107 ongoingSessions.erase(it);
108 break;
109 }
110 }
111}
112
113void
114OverlayTrackerMir::sessionStateChanged (MirPromptSession * session, MirPromptSessionState state)
115{
116 if (state != mir_prompt_session_state_stopped) {
117 /* We only care about the stopped state */
118 return;
119 }
120
121 /* Executing on the Mir thread, which is nice and all, but we
122 want to get back on our thread */
123 thread.executeOnThread([this, session]() {
124 removeSession(session);
125 });
126}
127
128void
129OverlayTrackerMir::untrustedHelperStoppedStatic (const gchar * appid, const gchar * instanceid, const gchar * helpertype, gpointer user_data)
130{
131 reinterpret_cast<OverlayTrackerMir *>(user_data)->untrustedHelperStopped(appid, instanceid, helpertype);
132}
133
134void
135OverlayTrackerMir::untrustedHelperStopped(const gchar * appid, const gchar * instanceid, const gchar * helpertype)
136{
137 /* This callback will happen on our thread already, we don't need
138 to proxy it on */
139 if (g_strcmp0(helpertype, HELPER_TYPE) != 0) {
140 return;
141 }
142
143 /* Making the code in the loop easier to read by using std::string outside */
144 std::string sappid(appid);
145 std::string sinstanceid(instanceid);
146
147 for (auto it = ongoingSessions.begin(); it != ongoingSessions.end(); it++) {
148 if (std::get<0>(*it) == sappid && std::get<1>(*it) == sinstanceid) {
149 ongoingSessions.erase(it);
150 break;
151 }
152 }
153}
0154
=== added file 'service/overlay-tracker-mir.h'
--- service/overlay-tracker-mir.h 1970-01-01 00:00:00 +0000
+++ service/overlay-tracker-mir.h 2015-06-12 15:51:31 +0000
@@ -0,0 +1,49 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#pragma once
21
22#include <set>
23
24#include <mir_toolkit/mir_connection.h>
25#include <mir_toolkit/mir_prompt_session.h>
26
27#include "glib-thread.h"
28#include "overlay-tracker-iface.h"
29
30class OverlayTrackerMir : public OverlayTrackerIface {
31private:
32 GLib::ContextThread thread;
33 std::shared_ptr<MirConnection> mir;
34 std::set<std::tuple<std::string, std::string, std::shared_ptr<MirPromptSession>>> ongoingSessions;
35
36public:
37 OverlayTrackerMir ();
38 ~OverlayTrackerMir ();
39 bool addOverlay (const char * appid, unsigned long pid, const char * url) override;
40
41private:
42 void removeSession (MirPromptSession * session);
43
44 static void sessionStateChangedStatic (MirPromptSession * session, MirPromptSessionState state, void * user_data);
45 void sessionStateChanged (MirPromptSession * session, MirPromptSessionState state);
46
47 static void untrustedHelperStoppedStatic (const gchar * appid, const gchar * instanceid, const gchar * helpertype, gpointer user_data);
48 void untrustedHelperStopped(const gchar * appid, const gchar * instanceid, const gchar * helpertype);
49};
050
=== added file 'service/overlay-tracker.cpp'
--- service/overlay-tracker.cpp 1970-01-01 00:00:00 +0000
+++ service/overlay-tracker.cpp 2015-06-12 15:51:31 +0000
@@ -0,0 +1,54 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20extern "C" {
21#include "overlay-tracker.h"
22}
23
24#include "overlay-tracker-iface.h"
25#include "overlay-tracker-mir.h"
26
27OverlayTracker *
28overlay_tracker_new () {
29 try {
30 OverlayTrackerMir * cpptracker = new OverlayTrackerMir();
31 return reinterpret_cast<OverlayTracker *>(cpptracker);
32 } catch (...) {
33 return nullptr;
34 }
35}
36
37void
38overlay_tracker_delete (OverlayTracker * tracker) {
39 g_return_if_fail(tracker != nullptr);
40
41 auto cpptracker = reinterpret_cast<OverlayTrackerMir *>(tracker);
42 delete cpptracker;
43 return;
44}
45
46gboolean
47overlay_tracker_add (OverlayTracker * tracker, const char * appid, unsigned long pid, const gchar * url) {
48 g_return_val_if_fail(tracker != nullptr, FALSE);
49 g_return_val_if_fail(appid != nullptr, FALSE);
50 g_return_val_if_fail(pid != 0, FALSE);
51 g_return_val_if_fail(url != nullptr, FALSE);
52
53 return reinterpret_cast<OverlayTrackerIface *>(tracker)->addOverlay(appid, pid, url) ? TRUE : FALSE;
54}
055
=== added file 'service/overlay-tracker.h'
--- service/overlay-tracker.h 1970-01-01 00:00:00 +0000
+++ service/overlay-tracker.h 2015-06-12 15:51:31 +0000
@@ -0,0 +1,28 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#pragma once
21#include <glib.h>
22
23typedef struct _OverlayTracker OverlayTracker;
24
25OverlayTracker * overlay_tracker_new ();
26void overlay_tracker_delete (OverlayTracker * tracker);
27gboolean overlay_tracker_add (OverlayTracker * tracker, const char * appid, unsigned long pid, const char * url);
28
029
=== modified file 'service/recoverable-problem.c'
--- service/recoverable-problem.c 2014-05-27 16:51:53 +0000
+++ service/recoverable-problem.c 2015-06-12 15:51:31 +0000
@@ -63,6 +63,12 @@
63void63void
64report_recoverable_problem (const gchar * signature, GPid report_pid, gboolean wait, const gchar * additional_properties[])64report_recoverable_problem (const gchar * signature, GPid report_pid, gboolean wait, const gchar * additional_properties[])
65{65{
66 /* Allow disabling for testing, we don't want to report bugs on
67 our tests ;-) */
68 if (G_UNLIKELY(g_getenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR") != NULL)) {
69 return;
70 }
71
66 GError * error = NULL;72 GError * error = NULL;
67 gint error_stdin = 0;73 gint error_stdin = 0;
68 GPid pid = 0;74 GPid pid = 0;
6975
=== modified file 'service/service.c'
--- service/service.c 2014-03-14 19:50:37 +0000
+++ service/service.c 2015-06-12 15:51:31 +0000
@@ -39,13 +39,15 @@
3939
40 guint term_source = g_unix_signal_add(SIGTERM, sig_term, mainloop);40 guint term_source = g_unix_signal_add(SIGTERM, sig_term, mainloop);
4141
42 dispatcher_init(mainloop);42 OverlayTracker * tracker = overlay_tracker_new();
43 dispatcher_init(mainloop, tracker);
4344
44 /* Run Main */45 /* Run Main */
45 g_main_loop_run(mainloop);46 g_main_loop_run(mainloop);
4647
47 /* Clean up globals */48 /* Clean up globals */
48 dispatcher_shutdown();49 dispatcher_shutdown();
50 overlay_tracker_delete(tracker);
49 g_source_remove(term_source);51 g_source_remove(term_source);
50 g_main_loop_unref(mainloop);52 g_main_loop_unref(mainloop);
5153
5254
=== modified file 'service/url-db.c'
--- service/url-db.c 2014-10-31 15:51:23 +0000
+++ service/url-db.c 2015-06-12 15:51:31 +0000
@@ -24,7 +24,7 @@
24#define DB_SCHEMA_VERSION "1"24#define DB_SCHEMA_VERSION "1"
2525
26sqlite3 *26sqlite3 *
27url_db_create_database (void)27url_db_create_database ()
28{28{
29 const gchar * cachedir = g_getenv("URL_DISPATCHER_CACHE_DIR"); /* Mostly for testing */29 const gchar * cachedir = g_getenv("URL_DISPATCHER_CACHE_DIR"); /* Mostly for testing */
3030
3131
=== modified file 'service/url-db.h'
--- service/url-db.h 2014-10-24 18:55:11 +0000
+++ service/url-db.h 2015-06-12 15:51:31 +0000
@@ -25,7 +25,7 @@
2525
26G_BEGIN_DECLS26G_BEGIN_DECLS
2727
28sqlite3 * url_db_create_database (void);28sqlite3 * url_db_create_database ();
29gboolean url_db_get_file_motification_time (sqlite3 * db,29gboolean url_db_get_file_motification_time (sqlite3 * db,
30 const gchar * filename,30 const gchar * filename,
31 GTimeVal * timeval);31 GTimeVal * timeval);
3232
=== added file 'service/url-overlay.c'
--- service/url-overlay.c 1970-01-01 00:00:00 +0000
+++ service/url-overlay.c 2015-06-12 15:51:31 +0000
@@ -0,0 +1,174 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted.gould@canonical.com>
18 */
19
20#include <glib.h>
21#include <click.h>
22#include <ubuntu-app-launch.h>
23
24#include "recoverable-problem.h"
25
26gchar *
27build_exec (const gchar * appid, const gchar * directory)
28{
29 gchar * appid_desktop = g_strdup_printf("%s.desktop", appid);
30 gchar * desktopfilepath = g_build_filename(directory, appid_desktop, NULL);
31 g_free(appid_desktop);
32
33 if (!g_file_test(desktopfilepath, G_FILE_TEST_EXISTS)) {
34 g_free(desktopfilepath);
35 return NULL;
36 }
37
38 GError * error = NULL;
39 GKeyFile * keyfile = g_key_file_new();
40 g_key_file_load_from_file(keyfile, desktopfilepath, G_KEY_FILE_NONE, &error);
41
42 if (error != NULL) {
43 g_error("Unable to read url-overlay desktop file '%s': %s", desktopfilepath, error->message);
44 g_free(desktopfilepath);
45 g_key_file_free(keyfile);
46 g_error_free(error);
47 return NULL;
48 }
49
50 g_free(desktopfilepath);
51
52 if (!g_key_file_has_key(keyfile, "Desktop Entry", "Exec", NULL)) {
53 g_error("Desktop file for '%s' in '%s' does not have 'Exec' key", appid, directory);
54 g_key_file_free(keyfile);
55 return NULL;
56 }
57
58 gchar * exec = g_key_file_get_string(keyfile, "Desktop Entry", "Exec", NULL);
59 g_key_file_free(keyfile);
60
61 return exec;
62}
63
64gchar *
65build_dir (const gchar * appid)
66{
67 GError * error = NULL;
68 gchar * package = NULL;
69
70 /* 'Parse' the App ID */
71 if (!ubuntu_app_launch_app_id_parse(appid, &package, NULL, NULL)) {
72 g_warning("Unable to parse App ID: '%s'", appid);
73 return NULL;
74 }
75
76 /* Check click to find out where the files are */
77 ClickDB * db = click_db_new();
78
79 /* If TEST_CLICK_DB is unset, this reads the system database. */
80 click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
81 if (error != NULL) {
82 g_warning("Unable to read Click database: %s", error->message);
83 g_error_free(error);
84 g_free(package);
85 return NULL;
86 }
87
88 /* If TEST_CLICK_USER is unset, this uses the current user name. */
89 ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
90 if (error != NULL) {
91 g_warning("Unable to read Click database: %s", error->message);
92 g_error_free(error);
93 g_free(package);
94 g_object_unref(db);
95 return NULL;
96 }
97
98 gchar * pkgdir = click_user_get_path(user, package, &error);
99
100 g_object_unref(user);
101 g_object_unref(db);
102 g_free(package);
103
104 if (error != NULL) {
105 g_warning("Unable to get the Click package directory for %s: %s", package, error->message);
106 g_error_free(error);
107 return NULL;
108 }
109
110 return pkgdir;
111}
112
113int
114main (int argc, char * argv[])
115{
116 /* Build up our exec */
117 const gchar * appid = g_getenv("APP_ID");
118 if (appid == NULL) {
119 report_recoverable_problem("url-dispatcher-url-overlay-no-appid", 0, TRUE, NULL);
120 return -1;
121 }
122
123 gchar * exec = NULL;
124
125 /* Allow for environment override */
126 const gchar * envdir = g_getenv("URL_DISPATCHER_OVERLAY_DIR");
127 if (G_UNLIKELY(envdir != NULL)) { /* Mostly for testing */
128 exec = build_exec(appid, envdir);
129 }
130
131 /* Try the system directory */
132 if (exec == NULL) {
133 exec = build_exec(appid, OVERLAY_SYSTEM_DIRECTORY);
134 }
135
136 /* If not there look to the user directory (click) */
137 if (exec == NULL) {
138 gchar * userdir = g_build_filename(g_get_user_cache_dir(), "url-dispatcher", "url-overlays", NULL);
139 exec = build_exec(appid, userdir);
140 g_free(userdir);
141 }
142
143 if (exec == NULL) {
144 const gchar * props[3] = {
145 "AppID",
146 appid,
147 NULL
148 };
149
150 report_recoverable_problem("url-dispatcher-url-overlay-bad-appid", 0, TRUE, props);
151 return -1;
152 }
153
154 gchar * dir = build_dir(appid);
155 /* NOTE: Dir will be NULL for system apps */
156
157 GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
158 g_return_val_if_fail(bus != NULL, -1);
159
160 gboolean sended = ubuntu_app_launch_helper_set_exec(exec, dir);
161 g_free(exec);
162 g_free(dir);
163
164 /* Ensuring the messages get on the bus before we quit */
165 g_dbus_connection_flush_sync(bus, NULL, NULL);
166 g_clear_object(&bus);
167
168 if (sended) {
169 return 0;
170 } else {
171 g_critical("Unable to send exec to Upstart");
172 return -1;
173 }
174}
0175
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2015-01-09 16:11:45 +0000
+++ tests/CMakeLists.txt 2015-06-12 15:51:31 +0000
@@ -34,6 +34,23 @@
34)34)
3535
36###########################36###########################
37# Mir Mock Lib
38###########################
39
40add_library(mir-mock-lib SHARED
41 mir-mock.h
42 mir-mock.cpp)
43
44target_link_libraries(mir-mock-lib
45 -pthread
46 ${GLIB2_LIBRARIES}
47)
48
49set_target_properties(mir-mock-lib PROPERTIES
50 OUTPUT_NAME "mir-mock"
51)
52
53###########################
37# Dispatcher test54# Dispatcher test
38###########################55###########################
3956
@@ -55,7 +72,7 @@
5572
56include_directories("${CMAKE_BINARY_DIR}/service")73include_directories("${CMAKE_BINARY_DIR}/service")
5774
58add_executable (app-id-test app-id-test.cc "${CMAKE_SOURCE_DIR}/service/dispatcher.c")75add_executable (app-id-test app-id-test.cc)
59target_link_libraries (app-id-test76target_link_libraries (app-id-test
60 dispatcher-lib77 dispatcher-lib
61 mock-lib78 mock-lib
@@ -128,3 +145,32 @@
128145
129add_test (url-db-test url-db-test)146add_test (url-db-test url-db-test)
130add_subdirectory(url_dispatcher_testability)147add_subdirectory(url_dispatcher_testability)
148
149###########################
150# exec tool test
151###########################
152
153add_executable (exec-tool-test exec-tool-test.cc)
154target_link_libraries (exec-tool-test
155 gtest
156 ${GTEST_LIBS}
157 ${GIO2_LIBRARIES}
158 ${DBUSTEST_LIBRARIES})
159
160add_test (exec-tool-test exec-tool-test)
161
162###########################
163# overlay tracker test
164###########################
165
166add_executable (overlay-tracker-test overlay-tracker-test.cpp)
167target_link_libraries (overlay-tracker-test
168 dispatcher-lib
169 mir-mock-lib
170 mock-lib
171 gtest
172 ${GTEST_LIBS}
173 ${GIO2_LIBRARIES}
174 ${DBUSTEST_LIBRARIES})
175
176add_test (overlay-tracker-test overlay-tracker-test)
131177
=== modified file 'tests/app-id-test.cc'
--- tests/app-id-test.cc 2014-10-28 09:49:29 +0000
+++ tests/app-id-test.cc 2015-06-12 15:51:31 +0000
@@ -21,6 +21,7 @@
21#include <gtest/gtest.h>21#include <gtest/gtest.h>
22#include "dispatcher.h"22#include "dispatcher.h"
23#include "ubuntu-app-launch-mock.h"23#include "ubuntu-app-launch-mock.h"
24#include "overlay-tracker-mock.h"
2425
25class AppIdTest : public ::testing::Test26class AppIdTest : public ::testing::Test
26{27{
@@ -28,6 +29,7 @@
28 GTestDBus * testbus = nullptr;29 GTestDBus * testbus = nullptr;
29 GMainLoop * mainloop = nullptr;30 GMainLoop * mainloop = nullptr;
30 gchar * cachedir = nullptr;31 gchar * cachedir = nullptr;
32 OverlayTrackerMock tracker;
3133
32 protected:34 protected:
33 virtual void SetUp() {35 virtual void SetUp() {
@@ -41,7 +43,7 @@
41 g_test_dbus_up(testbus);43 g_test_dbus_up(testbus);
4244
43 mainloop = g_main_loop_new(nullptr, FALSE);45 mainloop = g_main_loop_new(nullptr, FALSE);
44 dispatcher_init(mainloop);46 dispatcher_init(mainloop, reinterpret_cast<OverlayTracker *>(&tracker));
4547
46 return;48 return;
47 }49 }
4850
=== modified file 'tests/dispatcher-test.cc'
--- tests/dispatcher-test.cc 2015-01-09 16:11:45 +0000
+++ tests/dispatcher-test.cc 2015-06-12 15:51:31 +0000
@@ -21,6 +21,7 @@
21#include <gtest/gtest.h>21#include <gtest/gtest.h>
22#include "dispatcher.h"22#include "dispatcher.h"
23#include "ubuntu-app-launch-mock.h"23#include "ubuntu-app-launch-mock.h"
24#include "overlay-tracker-mock.h"
24#include "url-db.h"25#include "url-db.h"
2526
26class DispatcherTest : public ::testing::Test27class DispatcherTest : public ::testing::Test
@@ -31,9 +32,13 @@
31 gchar * cachedir = nullptr;32 gchar * cachedir = nullptr;
3233
33 protected:34 protected:
35 OverlayTrackerMock tracker;
36 GDBusConnection * session = nullptr;
37
34 virtual void SetUp() {38 virtual void SetUp() {
35 g_setenv("TEST_CLICK_DB", "click-db", TRUE);39 g_setenv("TEST_CLICK_DB", "click-db", TRUE);
36 g_setenv("TEST_CLICK_USER", "test-user", TRUE);40 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
41 g_setenv("URL_DISPATCHER_OVERLAY_DIR", OVERLAY_TEST_DIR, TRUE);
3742
38 cachedir = g_build_filename(CMAKE_BINARY_DIR, "dispatcher-test-cache", nullptr);43 cachedir = g_build_filename(CMAKE_BINARY_DIR, "dispatcher-test-cache", nullptr);
39 g_setenv("URL_DISPATCHER_CACHE_DIR", cachedir, TRUE);44 g_setenv("URL_DISPATCHER_CACHE_DIR", cachedir, TRUE);
@@ -66,8 +71,10 @@
66 testbus = g_test_dbus_new(G_TEST_DBUS_NONE);71 testbus = g_test_dbus_new(G_TEST_DBUS_NONE);
67 g_test_dbus_up(testbus);72 g_test_dbus_up(testbus);
6873
74 session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
75
69 mainloop = g_main_loop_new(nullptr, FALSE);76 mainloop = g_main_loop_new(nullptr, FALSE);
70 dispatcher_init(mainloop);77 dispatcher_init(mainloop, reinterpret_cast<OverlayTracker *>(&tracker));
7178
72 return;79 return;
73 }80 }
@@ -87,6 +94,8 @@
87 /* let other threads settle */94 /* let other threads settle */
88 g_usleep(500000);95 g_usleep(500000);
8996
97 g_clear_object(&session);
98
90 g_test_dbus_down(testbus);99 g_test_dbus_down(testbus);
91 g_object_unref(testbus);100 g_object_unref(testbus);
92101
@@ -250,3 +259,18 @@
250259
251 return;260 return;
252}261}
262
263TEST_F(DispatcherTest, OverlayTest)
264{
265 EXPECT_TRUE(dispatcher_is_overlay("com.test.good_application_1.2.3"));
266 EXPECT_FALSE(dispatcher_is_overlay("com.test.bad_application_1.2.3"));
267
268 EXPECT_TRUE(dispatcher_send_to_overlay ("com.test.good_application_1.2.3", "overlay://ubuntu.com", session, g_dbus_connection_get_unique_name(session)));
269
270 ASSERT_EQ(1, tracker.addedOverlays.size());
271 EXPECT_EQ("com.test.good_application_1.2.3", std::get<0>(tracker.addedOverlays[0]));
272 EXPECT_EQ(getpid(), std::get<1>(tracker.addedOverlays[0]));
273 EXPECT_EQ("overlay://ubuntu.com", std::get<2>(tracker.addedOverlays[0]));
274
275 return;
276}
253277
=== added file 'tests/exec-tool-test.cc'
--- tests/exec-tool-test.cc 1970-01-01 00:00:00 +0000
+++ tests/exec-tool-test.cc 2015-06-12 15:51:31 +0000
@@ -0,0 +1,118 @@
1/**
2 * Copyright © 2013-2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#include "test-config.h"
19
20#include <gio/gio.h>
21#include <gtest/gtest.h>
22#include <libdbustest/dbus-test.h>
23
24class ExecToolTest : public ::testing::Test
25{
26 protected:
27 DbusTestService * service = nullptr;
28 DbusTestDbusMock * mock = nullptr;
29 DbusTestDbusMockObject * obj = nullptr;
30 GDBusConnection * bus = nullptr;
31
32 virtual void SetUp() {
33 g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);
34 g_setenv("URL_DISPATCHER_OVERLAY_DIR", OVERLAY_TEST_DIR, TRUE);
35
36 g_setenv("TEST_CLICK_DB", "click-db", TRUE);
37 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
38
39 service = dbus_test_service_new(nullptr);
40
41 /* Upstart Mock */
42 mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");
43 obj = dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", nullptr);
44
45 dbus_test_dbus_mock_object_add_method(mock, obj,
46 "SetEnv",
47 G_VARIANT_TYPE("(assb)"),
48 NULL,
49 "",
50 NULL);
51
52 dbus_test_task_set_name(DBUS_TEST_TASK(mock), "Upstart");
53 dbus_test_service_add_task(service, DBUS_TEST_TASK(mock));
54
55 /* Start your engines! */
56 dbus_test_service_start_tasks(service);
57
58 bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
59 g_dbus_connection_set_exit_on_close(bus, FALSE);
60 g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus);
61 return;
62 }
63
64 virtual void TearDown() {
65 /* dbustest should probably do this, not sure */
66
67 g_clear_object(&mock);
68 g_clear_object(&service);
69
70 g_object_unref(bus);
71
72 unsigned int cleartry = 0;
73 while (bus != nullptr && cleartry < 100) {
74 pause(100);
75 cleartry++;
76 }
77
78 return;
79 }
80
81 static gboolean quit_loop (gpointer ploop) {
82 g_main_loop_quit((GMainLoop *)ploop);
83 return FALSE;
84 }
85
86 void pause (int time) {
87 GMainLoop * loop = g_main_loop_new(nullptr, FALSE);
88 g_timeout_add(time, quit_loop, loop);
89 g_main_loop_run(loop);
90 g_main_loop_unref(loop);
91 }
92};
93
94TEST_F(ExecToolTest, SetOverlay)
95{
96 g_unsetenv("APP_ID");
97 gint retval = 0;
98 EXPECT_TRUE(g_spawn_command_line_sync(EXEC_TOOL, nullptr, nullptr, &retval, nullptr));
99 EXPECT_NE(0, retval);
100
101 g_setenv("APP_ID", "com.test.good_application_1.2.3", TRUE);
102 g_setenv("UPSTART_JOB", "fubar", TRUE);
103 EXPECT_TRUE(g_spawn_command_line_sync(EXEC_TOOL, nullptr, nullptr, &retval, nullptr));
104 EXPECT_EQ(0, retval);
105
106 guint len = 0;
107 const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
108 ASSERT_NE(nullptr, calls);
109 ASSERT_EQ(2, len);
110
111 GVariant * appexecenv = g_variant_get_child_value(calls[0].params, 1);
112 EXPECT_STREQ("APP_EXEC=foobar", g_variant_get_string(appexecenv, nullptr));
113 g_variant_unref(appexecenv);
114
115 GVariant * appdirenv = g_variant_get_child_value(calls[1].params, 1);
116 EXPECT_STREQ("APP_DIR=" CLICK_DATA_DIR "/.click/users/test-user/com.test.good", g_variant_get_string(appdirenv, nullptr));
117 g_variant_unref(appdirenv);
118}
0119
=== added file 'tests/mir-mock.cpp'
--- tests/mir-mock.cpp 1970-01-01 00:00:00 +0000
+++ tests/mir-mock.cpp 2015-06-12 15:51:31 +0000
@@ -0,0 +1,124 @@
1/**
2 * Copyright © 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License version 3, as published by
6 * the Free Software Foundation.
7 * * This program is distributed in the hope that it will be useful, but WITHOUT
8 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
9 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 */
16
17#include "mir-mock.h"
18
19#include <vector>
20#include <iostream>
21#include <thread>
22
23MirPromptSession * mir_mock_valid_trust_session = (MirPromptSession *)"In the circle of trust";
24static bool valid_trust_connection = true;
25static int trusted_fd = 1234;
26MirPromptSession * mir_mock_last_released_session = NULL;
27pid_t mir_mock_last_trust_pid = 0;
28void (*mir_mock_last_trust_func)(MirPromptSession *, MirPromptSessionState, void*data) = NULL;
29void * mir_mock_last_trust_data = NULL;
30
31MirPromptSession *
32mir_connection_create_prompt_session_sync(MirConnection *, pid_t pid, void (*func)(MirPromptSession *, MirPromptSessionState, void*data), void * context) {
33 mir_mock_last_trust_pid = pid;
34 mir_mock_last_trust_func = func;
35 mir_mock_last_trust_data = context;
36
37 if (valid_trust_connection) {
38 return mir_mock_valid_trust_session;
39 } else {
40 return nullptr;
41 }
42}
43
44void
45mir_prompt_session_release_sync (MirPromptSession * session)
46{
47 mir_mock_last_released_session = session;
48 if (session != mir_mock_valid_trust_session) {
49 std::cerr << "Releasing a Mir Trusted Prompt that isn't valid" << std::endl;
50 exit(1);
51 }
52}
53
54MirWaitHandle *
55mir_prompt_session_new_fds_for_prompt_providers (MirPromptSession * session, unsigned int numfds, mir_client_fd_callback cb, void * data) {
56 if (session != mir_mock_valid_trust_session) {
57 std::cerr << "Releasing a Mir Trusted Prompt that isn't valid" << std::endl;
58 exit(1);
59 }
60
61 std::thread * thread = new std::thread([session, numfds, cb, data]() {
62 std::vector<int> fdlist(numfds);
63
64 for (unsigned int i = 0; i < numfds; i++)
65 fdlist[i] = trusted_fd;
66
67 cb(session, numfds, fdlist.data(), data);
68 });
69
70 return reinterpret_cast<MirWaitHandle *>(thread);
71}
72
73void
74mir_wait_for (MirWaitHandle * wait)
75{
76 auto thread = reinterpret_cast<std::thread *>(wait);
77
78 if (thread->joinable())
79 thread->join();
80
81 delete thread;
82}
83
84static const char * valid_connection_str = "Valid Mir Connection";
85static std::pair<std::string, std::string> last_connection;
86static bool valid_connection = true;
87
88void
89mir_mock_connect_return_valid (bool valid)
90{
91 valid_connection = valid;
92}
93
94std::pair<std::string, std::string>
95mir_mock_connect_last_connect ()
96{
97 return last_connection;
98}
99
100MirConnection *
101mir_connect_sync (char const * server, char const * appname)
102{
103 last_connection = std::pair<std::string, std::string>(server, appname);
104
105 if (valid_connection) {
106 return (MirConnection *)(valid_connection_str);
107 } else {
108 return nullptr;
109 }
110}
111
112void
113mir_connection_release (MirConnection * con)
114{
115 if (reinterpret_cast<char *>(con) != valid_connection_str) {
116 std::cerr << "Releasing a Mir Connection that isn't valid" << std::endl;
117 exit(1);
118 }
119}
120
121void mir_mock_set_trusted_fd (int fd)
122{
123 trusted_fd = fd;
124}
0125
=== added file 'tests/mir-mock.h'
--- tests/mir-mock.h 1970-01-01 00:00:00 +0000
+++ tests/mir-mock.h 2015-06-12 15:51:31 +0000
@@ -0,0 +1,36 @@
1/**
2 * Copyright © 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License version 3, as published by
6 * the Free Software Foundation.
7 * * This program is distributed in the hope that it will be useful, but WITHOUT
8 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
9 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 */
16
17#ifndef MIR_MOCK_H
18#define MIR_MOCK_H 1
19
20#include <string>
21#include <utility>
22
23#include <mir_toolkit/mir_connection.h>
24#include <mir_toolkit/mir_prompt_session.h>
25
26void mir_mock_connect_return_valid (bool valid);
27std::pair<std::string, std::string> mir_mock_connect_last_connect ();
28void mir_mock_set_trusted_fd (int fd);
29
30extern MirPromptSession * mir_mock_valid_trust_session;
31extern MirPromptSession * mir_mock_last_released_session;
32extern pid_t mir_mock_last_trust_pid;
33extern void (*mir_mock_last_trust_func)(MirPromptSession *, MirPromptSessionState, void*data);
34extern void * mir_mock_last_trust_data;
35
36#endif // MIR_MOCK_H
037
=== added directory 'tests/overlay-dir'
=== added file 'tests/overlay-dir/com.test.good_application_1.2.3.desktop'
--- tests/overlay-dir/com.test.good_application_1.2.3.desktop 1970-01-01 00:00:00 +0000
+++ tests/overlay-dir/com.test.good_application_1.2.3.desktop 2015-06-12 15:51:31 +0000
@@ -0,0 +1,2 @@
1[Desktop Entry]
2Exec=foobar
03
=== added file 'tests/overlay-tracker-mock.h'
--- tests/overlay-tracker-mock.h 1970-01-01 00:00:00 +0000
+++ tests/overlay-tracker-mock.h 2015-06-12 15:51:31 +0000
@@ -0,0 +1,30 @@
1/**
2 * Copyright © 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License version 3, as published by
6 * the Free Software Foundation.
7 * * This program is distributed in the hope that it will be useful, but WITHOUT
8 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
9 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 */
16
17#pragma once
18#include "overlay-tracker-iface.h"
19#include <utility>
20
21class OverlayTrackerMock : public OverlayTrackerIface
22{
23 public:
24 std::vector<std::tuple<std::string, unsigned long, std::string>> addedOverlays;
25
26 bool addOverlay (const char * appid, unsigned long pid, const char * url) {
27 addedOverlays.push_back(std::make_tuple(std::string(appid), pid, std::string(url)));
28 return true;
29 }
30};
031
=== added file 'tests/overlay-tracker-test.cpp'
--- tests/overlay-tracker-test.cpp 1970-01-01 00:00:00 +0000
+++ tests/overlay-tracker-test.cpp 2015-06-12 15:51:31 +0000
@@ -0,0 +1,127 @@
1/**
2 * Copyright © 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License version 3, as published by
6 * the Free Software Foundation.
7 * * This program is distributed in the hope that it will be useful, but WITHOUT
8 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
9 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 */
16
17#include <random>
18
19#include "test-config.h"
20
21#include <gio/gio.h>
22#include <gtest/gtest.h>
23#include <libdbustest/dbus-test.h>
24
25#include "overlay-tracker-mir.h"
26#include "ubuntu-app-launch-mock.h"
27#include "mir-mock.h"
28
29class OverlayTrackerTest : public ::testing::Test
30{
31 protected:
32 virtual void SetUp() {
33 g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);
34
35 return;
36 }
37
38 virtual void TearDown() {
39 return;
40 }
41
42 static gboolean quit_loop (gpointer ploop) {
43 g_main_loop_quit((GMainLoop *)ploop);
44 return FALSE;
45 }
46
47 void pause (int time) {
48 GMainLoop * loop = g_main_loop_new(nullptr, FALSE);
49 g_timeout_add(time, quit_loop, loop);
50 g_main_loop_run(loop);
51 g_main_loop_unref(loop);
52 }
53};
54
55TEST_F(OverlayTrackerTest, BasicCreation) {
56 auto tracker = new OverlayTrackerMir();
57 delete tracker;
58}
59
60TEST_F(OverlayTrackerTest, AddOverlay) {
61 auto tracker = new OverlayTrackerMir();
62
63 auto mirconn = mir_mock_connect_last_connect();
64 EXPECT_EQ("mir_socket_trusted", mirconn.first.substr(mirconn.first.size() - 18));
65 EXPECT_EQ("url-dispatcher", mirconn.second);
66
67 EXPECT_TRUE(tracker->addOverlay("app-id", 5, "http://no-name-yet.com"));
68
69 EXPECT_EQ(5, mir_mock_last_trust_pid);
70
71 EXPECT_STREQ("url-overlay", ubuntu_app_launch_mock_last_start_session_helper);
72 EXPECT_STREQ("app-id", ubuntu_app_launch_mock_last_start_session_appid);
73 EXPECT_STREQ("http://no-name-yet.com", ubuntu_app_launch_mock_last_start_session_uris[0]);
74
75 delete tracker;
76
77 EXPECT_STREQ("url-overlay", ubuntu_app_launch_mock_last_stop_helper);
78 EXPECT_STREQ("app-id", ubuntu_app_launch_mock_last_stop_appid);
79 EXPECT_STREQ("instance", ubuntu_app_launch_mock_last_stop_instance);
80}
81
82TEST_F(OverlayTrackerTest, OverlayABunch) {
83 OverlayTrackerMir tracker;
84 std::uniform_int_distribution<> randpid(1, 32000);
85 std::mt19937 rand;
86
87 /* Testing adding a bunch of overlays, we're using pretty standard
88 data structures, but let's make sure we didn't break 'em */
89 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"}) {
90 int pid = randpid(rand);
91 tracker.addOverlay(name.c_str(), pid, "http://ubuntu.com/releases");
92
93 EXPECT_EQ(pid, mir_mock_last_trust_pid);
94 EXPECT_EQ(name, ubuntu_app_launch_mock_last_start_session_appid);
95 }
96}
97
98TEST_F(OverlayTrackerTest, UALSignalStop) {
99 OverlayTrackerMir tracker;
100
101 /* Call with the overlay before it is set */
102 ubuntu_app_launch_mock_observer_helper_stop_func("app-id", "instance", "url-overlay", ubuntu_app_launch_mock_observer_helper_stop_user_data);
103
104 EXPECT_TRUE(tracker.addOverlay("app-id", 5, "http://no-name-yet.com"));
105
106 mir_mock_last_released_session = nullptr;
107 ubuntu_app_launch_mock_observer_helper_stop_func("app-id", "instance", "url-overlay", ubuntu_app_launch_mock_observer_helper_stop_user_data);
108 EXPECT_NE(nullptr, mir_mock_last_released_session);
109}
110
111TEST_F(OverlayTrackerTest, MirSignalStop) {
112 OverlayTrackerMir tracker;
113
114 EXPECT_TRUE(tracker.addOverlay("app-id", 5, "http://no-name-yet.com"));
115
116 /* Try a badie */
117 mir_mock_last_trust_func((MirPromptSession *)1337, mir_prompt_session_state_stopped, mir_mock_last_trust_data);
118
119 EXPECT_NE(nullptr, mir_mock_last_trust_func);
120 mir_mock_last_trust_func(mir_mock_valid_trust_session, mir_prompt_session_state_stopped, mir_mock_last_trust_data);
121
122 pause(100);
123
124 EXPECT_STREQ("url-overlay", ubuntu_app_launch_mock_last_stop_helper);
125 EXPECT_STREQ("app-id", ubuntu_app_launch_mock_last_stop_appid);
126 EXPECT_STREQ("instance", ubuntu_app_launch_mock_last_stop_instance);
127}
0128
=== modified file 'tests/service-test.cc'
--- tests/service-test.cc 2014-10-28 12:46:32 +0000
+++ tests/service-test.cc 2015-06-12 15:51:31 +0000
@@ -39,6 +39,7 @@
39 g_setenv("UBUNTU_APP_LAUNCH_USE_SESSION", "1", TRUE);39 g_setenv("UBUNTU_APP_LAUNCH_USE_SESSION", "1", TRUE);
40 g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);40 g_setenv("URL_DISPATCHER_DISABLE_RECOVERABLE_ERROR", "1", TRUE);
41 g_setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, TRUE);41 g_setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, TRUE);
42 g_setenv("LD_PRELOAD", MIR_MOCK_PATH, TRUE);
4243
43 SetUpDb();44 SetUpDb();
4445
@@ -94,10 +95,6 @@
94 }95 }
9596
96 virtual void TearDown() {97 virtual void TearDown() {
97 /* dbustest should probably do this, not sure */
98 kill(dbus_test_process_get_pid(dispatcher), SIGTERM);
99 g_usleep(50000);
100
101 g_clear_object(&dispatcher);98 g_clear_object(&dispatcher);
102 g_clear_object(&mock);99 g_clear_object(&mock);
103 g_clear_object(&dashmock);100 g_clear_object(&dashmock);
@@ -107,9 +104,7 @@
107104
108 unsigned int cleartry = 0;105 unsigned int cleartry = 0;
109 while (bus != nullptr && cleartry < 100) {106 while (bus != nullptr && cleartry < 100) {
110 g_usleep(100000);107 pause(100);
111 while (g_main_pending())
112 g_main_iteration(TRUE);
113 cleartry++;108 cleartry++;
114 }109 }
115110
@@ -249,7 +244,7 @@
249}244}
250245
251void246void
252focus_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)247focus_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)
253{248{
254 guint * focus_count = (guint *)user_data;249 guint * focus_count = (guint *)user_data;
255 *focus_count = *focus_count + 1;250 *focus_count = *focus_count + 1;
256251
=== modified file 'tests/test-config.h.in'
--- tests/test-config.h.in 2015-01-09 16:11:45 +0000
+++ tests/test-config.h.in 2015-06-12 15:51:31 +0000
@@ -10,3 +10,7 @@
10#define UPDATE_DIRECTORY_URLS "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-simple"10#define UPDATE_DIRECTORY_URLS "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-simple"
11#define UPDATE_DIRECTORY_VARIED "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-varied"11#define UPDATE_DIRECTORY_VARIED "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-varied"
12#define UPDATE_DIRECTORY_INTENT "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-intent"12#define UPDATE_DIRECTORY_INTENT "@CMAKE_CURRENT_SOURCE_DIR@/test-urls-intent"
13#define OVERLAY_TEST_DIR "@CMAKE_CURRENT_SOURCE_DIR@/overlay-dir"
14#define EXEC_TOOL "@CMAKE_BINARY_DIR@/service/exec-tool"
15#define MIR_MOCK_PATH "@CMAKE_CURRENT_BINARY_DIR@/libmir-mock.so"
16#define CLICK_DATA_DIR "@CMAKE_CURRENT_SOURCE_DIR@/click-data"
1317
=== modified file 'tests/ubuntu-app-launch-mock.c'
--- tests/ubuntu-app-launch-mock.c 2014-05-26 12:56:47 +0000
+++ tests/ubuntu-app-launch-mock.c 2015-06-12 15:51:31 +0000
@@ -29,7 +29,7 @@
29}29}
3030
31void31void
32ubuntu_app_launch_mock_clear_last_app_id (void)32ubuntu_app_launch_mock_clear_last_app_id ()
33{33{
34 g_free(last_appid);34 g_free(last_appid);
35 last_appid = NULL;35 last_appid = NULL;
@@ -37,7 +37,72 @@
37}37}
3838
39gchar *39gchar *
40ubuntu_app_launch_mock_get_last_app_id (void)40ubuntu_app_launch_mock_get_last_app_id ()
41{41{
42 return last_appid;42 return last_appid;
43}43}
44
45UbuntuAppLaunchHelperObserver ubuntu_app_launch_mock_observer_helper_stop_func = NULL;
46gchar * ubuntu_app_launch_mock_observer_helper_stop_type = NULL;
47void * ubuntu_app_launch_mock_observer_helper_stop_user_data = NULL;
48
49gboolean
50ubuntu_app_launch_observer_add_helper_stop (UbuntuAppLaunchHelperObserver func, const gchar * type, gpointer user_data)
51{
52 ubuntu_app_launch_mock_observer_helper_stop_func = func;
53 ubuntu_app_launch_mock_observer_helper_stop_type = g_strdup(type);
54 ubuntu_app_launch_mock_observer_helper_stop_user_data = user_data;
55
56 return TRUE;
57}
58
59gboolean
60ubuntu_app_launch_observer_delete_helper_stop (UbuntuAppLaunchHelperObserver func, const gchar * type, gpointer user_data)
61{
62 gboolean same = ubuntu_app_launch_mock_observer_helper_stop_func == func &&
63 g_strcmp0(ubuntu_app_launch_mock_observer_helper_stop_type, type) == 0 &&
64 ubuntu_app_launch_mock_observer_helper_stop_user_data == user_data;
65
66 ubuntu_app_launch_mock_observer_helper_stop_func = NULL;
67 g_clear_pointer(&ubuntu_app_launch_mock_observer_helper_stop_type, g_free);
68 ubuntu_app_launch_mock_observer_helper_stop_user_data = NULL;
69
70 return same;
71}
72
73gchar * ubuntu_app_launch_mock_last_start_session_helper = NULL;
74MirPromptSession * ubuntu_app_launch_mock_last_start_session_session = NULL;
75gchar * ubuntu_app_launch_mock_last_start_session_appid = NULL;
76gchar ** ubuntu_app_launch_mock_last_start_session_uris = NULL;
77
78gchar *
79ubuntu_app_launch_start_session_helper (const gchar * type, MirPromptSession * session, const gchar * appid, const gchar * const * uris)
80{
81 g_clear_pointer(&ubuntu_app_launch_mock_last_start_session_helper, g_free);
82 g_clear_pointer(&ubuntu_app_launch_mock_last_start_session_appid, g_free);
83 g_clear_pointer(&ubuntu_app_launch_mock_last_start_session_uris, g_strfreev);
84
85 ubuntu_app_launch_mock_last_start_session_helper = g_strdup(type);
86 ubuntu_app_launch_mock_last_start_session_session = session;
87 ubuntu_app_launch_mock_last_start_session_appid = g_strdup(appid);
88 ubuntu_app_launch_mock_last_start_session_uris = g_strdupv((gchar **)uris);
89
90 return g_strdup("instance");
91}
92
93gchar * ubuntu_app_launch_mock_last_stop_helper = NULL;
94gchar * ubuntu_app_launch_mock_last_stop_appid = NULL;
95gchar * ubuntu_app_launch_mock_last_stop_instance = NULL;
96
97gboolean
98ubuntu_app_launch_stop_multiple_helper (const gchar * helper_type, const gchar * appid, const gchar * instance) {
99 g_clear_pointer(&ubuntu_app_launch_mock_last_stop_helper, g_free);
100 g_clear_pointer(&ubuntu_app_launch_mock_last_stop_appid, g_free);
101 g_clear_pointer(&ubuntu_app_launch_mock_last_stop_instance, g_free);
102
103 ubuntu_app_launch_mock_last_stop_helper = g_strdup(helper_type);
104 ubuntu_app_launch_mock_last_stop_appid = g_strdup(appid);
105 ubuntu_app_launch_mock_last_stop_instance = g_strdup(instance);
106
107 return TRUE;
108}
44109
=== modified file 'tests/ubuntu-app-launch-mock.h'
--- tests/ubuntu-app-launch-mock.h 2014-10-24 18:55:11 +0000
+++ tests/ubuntu-app-launch-mock.h 2015-06-12 15:51:31 +0000
@@ -19,11 +19,23 @@
19#define UPSTART_APP_LAUNCH_MOCK 119#define UPSTART_APP_LAUNCH_MOCK 1
2020
21#include <glib.h>21#include <glib.h>
22#include <ubuntu-app-launch.h>
2223
23G_BEGIN_DECLS24G_BEGIN_DECLS
2425
25void ubuntu_app_launch_mock_clear_last_app_id (void);26void ubuntu_app_launch_mock_clear_last_app_id ();
26gchar * ubuntu_app_launch_mock_get_last_app_id (void);27gchar * ubuntu_app_launch_mock_get_last_app_id ();
28
29extern UbuntuAppLaunchHelperObserver ubuntu_app_launch_mock_observer_helper_stop_func;
30extern gchar * ubuntu_app_launch_mock_observer_helper_stop_type;
31extern void * ubuntu_app_launch_mock_observer_helper_stop_user_data;
32extern gchar * ubuntu_app_launch_mock_last_start_session_helper;
33extern MirPromptSession * ubuntu_app_launch_mock_last_start_session_session;
34extern gchar * ubuntu_app_launch_mock_last_start_session_appid;
35extern gchar ** ubuntu_app_launch_mock_last_start_session_uris;
36extern gchar * ubuntu_app_launch_mock_last_stop_helper;
37extern gchar * ubuntu_app_launch_mock_last_stop_appid;
38extern gchar * ubuntu_app_launch_mock_last_stop_instance;
2739
28G_END_DECLS40G_END_DECLS
2941

Subscribers

People subscribed via source and target branches