Merge lp:~bregma/ubuntu-app-launch/app-object into lp:ubuntu-app-launch/16.04

Proposed by Stephen M. Webb
Status: Rejected
Rejected by: Ted Gould
Proposed branch: lp:~bregma/ubuntu-app-launch/app-object
Merge into: lp:ubuntu-app-launch/16.04
Diff against target: 5605 lines (+4756/-236)
57 files modified
.bzrignore (+6/-0)
CMakeLists.txt (+5/-1)
_clang-format (+48/-0)
debian/changelog (+13/-0)
debian/control (+2/-0)
debian/libubuntu-app-launch2.shlibs (+1/-0)
debian/libubuntu-app-launch2.symbols (+0/-41)
debian/rules (+0/-3)
helpers-shared.c (+11/-2)
libubuntu-app-launch/CMakeLists.txt (+34/-0)
libubuntu-app-launch/appid.h (+83/-0)
libubuntu-app-launch/application-impl-base.cpp (+162/-0)
libubuntu-app-launch/application-impl-base.h (+52/-0)
libubuntu-app-launch/application-impl-click.cpp (+157/-0)
libubuntu-app-launch/application-impl-click.h (+56/-0)
libubuntu-app-launch/application-impl-legacy.cpp (+128/-0)
libubuntu-app-launch/application-impl-legacy.h (+57/-0)
libubuntu-app-launch/application-impl-libertine.cpp (+127/-0)
libubuntu-app-launch/application-impl-libertine.h (+56/-0)
libubuntu-app-launch/application-info-desktop.cpp (+202/-0)
libubuntu-app-launch/application-info-desktop.h (+87/-0)
libubuntu-app-launch/application.cpp (+225/-0)
libubuntu-app-launch/application.h (+150/-0)
libubuntu-app-launch/desktop-exec.c (+1/-1)
libubuntu-app-launch/glib-thread.cpp (+156/-0)
libubuntu-app-launch/glib-thread.h (+90/-0)
libubuntu-app-launch/helper-impl-click.cpp (+175/-0)
libubuntu-app-launch/helper-impl-click.h (+62/-0)
libubuntu-app-launch/helper.cpp (+36/-0)
libubuntu-app-launch/helper.h (+71/-0)
libubuntu-app-launch/libubuntu-app-launch.map (+17/-0)
libubuntu-app-launch/registry-impl.cpp (+185/-0)
libubuntu-app-launch/registry-impl.h (+69/-0)
libubuntu-app-launch/registry.cpp (+99/-0)
libubuntu-app-launch/registry.h (+91/-0)
libubuntu-app-launch/type-tagger.h (+37/-0)
libubuntu-app-launch/ubuntu-app-launch.c (+7/-7)
libubuntu-app-launch/ubuntu-app-launch.h (+12/-0)
tests/CMakeLists.txt (+20/-0)
tests/application-info-desktop.cpp (+148/-0)
tests/click-app-dir/application.desktop (+3/-1)
tests/exec-util-test.cc (+1/-1)
tests/helper-handshake-test.cc (+3/-0)
tests/helper-test.cc (+0/-2)
tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop (+1/-0)
tests/libual-cpp-test.cc (+1599/-0)
tests/libual-test.cc (+1/-2)
tests/mir-mock.cpp (+1/-1)
tools/CMakeLists.txt (+16/-7)
tools/ubuntu-app-list-pids.cpp (+44/-0)
tools/ubuntu-app-list.cpp (+11/-17)
tools/ubuntu-app-pid.cpp (+25/-19)
tools/ubuntu-app-stop.cpp (+21/-15)
tools/ubuntu-app-triplet.cpp (+26/-32)
tools/ubuntu-helper-list.cpp (+20/-31)
tools/ubuntu-helper-start.cpp (+21/-27)
tools/ubuntu-helper-stop.cpp (+25/-26)
To merge this branch: bzr merge lp:~bregma/ubuntu-app-launch/app-object
Reviewer Review Type Date Requested Status
Indicator Applet Developers Pending
Review via email: mp+284521@code.launchpad.net

Commit message

fix build deps

Description of the change

test merge for Libertine demo

To post a comment you must log in.
229. By Ted Gould

Removing signals from the API for this MR

230. By Ted Gould

Revert back to 'C' versions of these tools

231. By Ted Gould

Fix symbols

232. By Ted Gould

Switch to a different arbitrary style

233. By Ted Gould

Switch from symbols to shlibs

234. By Ted Gould

Namespace bike shedding

235. By Ted Gould

Remove as member functions

236. By Ted Gould

Adding the class keyword to enums

237. By Ted Gould

Optimize constructors

238. By Ted Gould

Explicit constructor

239. By Ted Gould

Check to ensure cpath isn't null

240. By Ted Gould

Make URL vectors const references

241. By Ted Gould

Make the constructor fail if there is no keyfile instead of waiting when we get info()

242. By Ted Gould

Passing the manifests by const reference

243. By Ted Gould

Adding a format target

244. By Ted Gould

Exception on manifest failures and ensure that we have it early

245. By Ted Gould

Stop incrementing ref counts to make the code faster

246. By Ted Gould

Check for keyfile in constructor

247. By Ted Gould

Check for keyfile in constructor

248. By Ted Gould

Move prototype

249. By Ted Gould

Making voids implicit instead of explicit

250. By Ted Gould

Move from one implementation only location to another implementation only location

251. By Ted Gould

Constexpr

252. By Ted Gould

Adding extra commas

253. By Ted Gould

Less splash, more namespacing

254. By Ted Gould

Change to supportsUbuntuLifecycle

255. By Stephen M. Webb

added missing build-dep on libproperties-cpp-dev

256. By Stephen M. Webb

use -std=gnu99 instead of c99 to fix FTBFS on some architectures

257. By Stephen M. Webb

Fixed a coupla FTBFS and bumped version for Silo 58 testing.

258. By Stephen M. Webb

Disabled a bunch of chronically-failing tests.

259. By Stephen M. Webb

merge latest ted upstream branch

Unmerged revisions

259. By Stephen M. Webb

merge latest ted upstream branch

258. By Stephen M. Webb

Disabled a bunch of chronically-failing tests.

257. By Stephen M. Webb

Fixed a coupla FTBFS and bumped version for Silo 58 testing.

256. By Stephen M. Webb

use -std=gnu99 instead of c99 to fix FTBFS on some architectures

255. By Stephen M. Webb

added missing build-dep on libproperties-cpp-dev

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2014-06-14 16:32:00 +0000
+++ .bzrignore 2016-03-04 11:34:19 +0000
@@ -5,3 +5,9 @@
5cmake_install.cmake5cmake_install.cmake
6*-trace.[ch]6*-trace.[ch]
7cgroup-reap-all7cgroup-reap-all
8libubuntu-app-launch/UbuntuAppLaunch-2.gir
9libubuntu-app-launch/UbuntuAppLaunch-2.typelib
10libubuntu-app-launch/libubuntu-app-launch.so.2*
11libubuntu-app-launch/proxy-socket-demangler.c
12libubuntu-app-launch/proxy-socket-demangler.h
13libubuntu-app-launch/ubuntu-app-launch-2.pc
814
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-08-11 02:45:49 +0000
+++ CMakeLists.txt 2016-03-04 11:34:19 +0000
@@ -37,7 +37,8 @@
37set(ubuntu_app_launch_arch "${UBUNTU_APP_LAUNCH_ARCH}")37set(ubuntu_app_launch_arch "${UBUNTU_APP_LAUNCH_ARCH}")
3838
39# Deprecated needed for g_atexit() in libual39# Deprecated needed for g_atexit() in libual
40set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-error=unused-function -Wno-error=deprecated-declarations -std=c99")40set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-error=unused-function -Wno-error=deprecated-declarations -std=gnu99")
41set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11 -pthread")
4142
42enable_testing()43enable_testing()
4344
@@ -77,6 +78,9 @@
77pkg_check_modules(MIR mirclient)78pkg_check_modules(MIR mirclient)
78include_directories(${MIR_INCLUDE_DIRS})79include_directories(${MIR_INCLUDE_DIRS})
7980
81pkg_check_modules(LIBERTINE libertine)
82include_directories(${LIBERTINE_INCLUDE_DIRS})
83
80include_directories(${CMAKE_CURRENT_SOURCE_DIR})84include_directories(${CMAKE_CURRENT_SOURCE_DIR})
8185
82set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")86set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
8387
=== added file '_clang-format'
--- _clang-format 1970-01-01 00:00:00 +0000
+++ _clang-format 2016-03-04 11:34:19 +0000
@@ -0,0 +1,48 @@
1---
2AccessModifierOffset: -4
3AlignEscapedNewlinesLeft: true
4AlignTrailingComments: true
5AllowAllParametersOfDeclarationOnNextLine: true
6AllowShortFunctionsOnASingleLine: false
7AllowShortIfStatementsOnASingleLine: false
8AllowShortLoopsOnASingleLine: false
9AlwaysBreakBeforeMultilineStrings: true
10AlwaysBreakTemplateDeclarations: true
11BinPackParameters: false
12BreakBeforeBinaryOperators: false
13BreakBeforeBraces: Allman
14BreakBeforeTernaryOperators: false
15BreakConstructorInitializersBeforeComma: true
16ColumnLimit: 120
17ConstructorInitializerAllOnOneLineOrOnePerLine: false
18ConstructorInitializerIndentWidth: 4
19ContinuationIndentWidth: 4
20Cpp11BracedListStyle: true
21DerivePointerBinding: true
22ExperimentalAutoDetectBinPacking: false
23IndentCaseLabels: true
24IndentFunctionDeclarationAfterType: true
25IndentWidth: 4
26Language: Cpp
27MaxEmptyLinesToKeep: 1
28NamespaceIndentation: None
29ObjCSpaceBeforeProtocolList: false
30PenaltyBreakBeforeFirstCallParameter: 1
31PenaltyBreakComment: 60
32PenaltyBreakFirstLessLess: 120
33PenaltyBreakString: 1000
34PenaltyExcessCharacter: 1000000
35PenaltyReturnTypeOnItsOwnLine: 200
36PointerBindsToType: true
37SpaceBeforeAssignmentOperators: true
38SpaceBeforeParens: ControlStatements
39SpaceInEmptyParentheses: false
40SpacesBeforeTrailingComments: 2
41SpacesInAngles: false
42SpacesInCStyleCastParentheses: false
43SpacesInParentheses: false
44Standard: Cpp11
45TabWidth: 8
46UseTab: Never
47...
48
049
=== modified file 'debian/changelog'
--- debian/changelog 2015-08-17 21:38:34 +0000
+++ debian/changelog 2016-03-04 11:34:19 +0000
@@ -1,3 +1,16 @@
1ubuntu-app-launch (0.9~smw2) UNRELEASED; urgency=medium
2
3 [ Ted Gould ]
4 * Add new C++ API that has a retained object for maintaining
5 connections through multiple calls. Also provides for getting
6 consistent application metadata.
7
8 [ Stephen M. Webb ]
9 * Fixed a coupla FTBFS and bumped version for Silo 58 testing.
10 * Disabled a bunch of chronically-failing tests.
11
12 -- Ted Gould <ted@ubuntu.com> Tue, 26 Jan 2016 15:22:06 -0600
13
1ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium14ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium
215
3 [ CI Train Bot ]16 [ CI Train Bot ]
417
=== modified file 'debian/control'
--- debian/control 2015-07-31 00:15:57 +0000
+++ debian/control 2016-03-04 11:34:19 +0000
@@ -15,10 +15,12 @@
15 libglib2.0-dev,15 libglib2.0-dev,
16 libgtest-dev,16 libgtest-dev,
17 libjson-glib-dev,17 libjson-glib-dev,
18 liblibertine-dev,
18 liblttng-ust-dev,19 liblttng-ust-dev,
19 libmirclient-dev (>= 0.5),20 libmirclient-dev (>= 0.5),
20 libnih-dbus-dev,21 libnih-dbus-dev,
21 libnih-dev,22 libnih-dev,
23 libproperties-cpp-dev,
22 libupstart-dev,24 libupstart-dev,
23 libzeitgeist-2.0-dev,25 libzeitgeist-2.0-dev,
24 gobject-introspection,26 gobject-introspection,
2527
=== added file 'debian/libubuntu-app-launch2.shlibs'
--- debian/libubuntu-app-launch2.shlibs 1970-01-01 00:00:00 +0000
+++ debian/libubuntu-app-launch2.shlibs 2016-03-04 11:34:19 +0000
@@ -0,0 +1,1 @@
1libubuntu-app-launch 2 libubuntu-app-launch2 (>= 0.9)
02
=== removed file 'debian/libubuntu-app-launch2.symbols'
--- debian/libubuntu-app-launch2.symbols 2015-08-17 21:38:34 +0000
+++ debian/libubuntu-app-launch2.symbols 1970-01-01 00:00:00 +0000
@@ -1,41 +0,0 @@
1libubuntu-app-launch.so.2 libubuntu-app-launch2 #MINVER#
2 ubuntu_app_launch_app_id_parse@Base 0.4
3 ubuntu_app_launch_application_info@Base 0.5+15.10.20150817
4 ubuntu_app_launch_application_log_path@Base 0.4
5 ubuntu_app_launch_get_primary_pid@Base 0.4
6 ubuntu_app_launch_helper_set_exec@Base 0.5+15.10.20150604
7 ubuntu_app_launch_list_helper_instances@Base 0.4
8 ubuntu_app_launch_list_helpers@Base 0.4
9 ubuntu_app_launch_list_running_apps@Base 0.4
10 ubuntu_app_launch_observer_add_app_failed@Base 0.4
11 ubuntu_app_launch_observer_add_app_focus@Base 0.4
12 ubuntu_app_launch_observer_add_app_paused@Base 0.4+15.04.20150305.1
13 ubuntu_app_launch_observer_add_app_resume@Base 0.4
14 ubuntu_app_launch_observer_add_app_resumed@Base 0.4+15.04.20150305.1
15 ubuntu_app_launch_observer_add_app_started@Base 0.4
16 ubuntu_app_launch_observer_add_app_starting@Base 0.4
17 ubuntu_app_launch_observer_add_app_stop@Base 0.4
18 ubuntu_app_launch_observer_add_helper_started@Base 0.4
19 ubuntu_app_launch_observer_add_helper_stop@Base 0.4
20 ubuntu_app_launch_observer_delete_app_failed@Base 0.4
21 ubuntu_app_launch_observer_delete_app_focus@Base 0.4
22 ubuntu_app_launch_observer_delete_app_paused@Base 0.4+15.04.20150305.1
23 ubuntu_app_launch_observer_delete_app_resume@Base 0.4
24 ubuntu_app_launch_observer_delete_app_resumed@Base 0.4+15.04.20150305.1
25 ubuntu_app_launch_observer_delete_app_started@Base 0.4
26 ubuntu_app_launch_observer_delete_app_starting@Base 0.4
27 ubuntu_app_launch_observer_delete_app_stop@Base 0.4
28 ubuntu_app_launch_observer_delete_helper_started@Base 0.4
29 ubuntu_app_launch_observer_delete_helper_stop@Base 0.4
30 ubuntu_app_launch_pause_application@Base 0.4+14.10.20140915.3
31 ubuntu_app_launch_pid_in_app_id@Base 0.4
32 ubuntu_app_launch_resume_application@Base 0.4+14.10.20140915.3
33 ubuntu_app_launch_start_application@Base 0.4
34 ubuntu_app_launch_start_application_test@Base 0.4
35 ubuntu_app_launch_start_helper@Base 0.4
36 ubuntu_app_launch_start_multiple_helper@Base 0.4
37 ubuntu_app_launch_start_session_helper@Base 0.5+15.10.20150604
38 ubuntu_app_launch_stop_application@Base 0.4
39 ubuntu_app_launch_stop_helper@Base 0.4
40 ubuntu_app_launch_stop_multiple_helper@Base 0.4
41 ubuntu_app_launch_triplet_to_app_id@Base 0.4
420
=== modified file 'debian/rules'
--- debian/rules 2014-11-21 16:43:12 +0000
+++ debian/rules 2016-03-04 11:34:19 +0000
@@ -1,9 +1,6 @@
1#!/usr/bin/make -f1#!/usr/bin/make -f
2# -*- makefile -*-2# -*- makefile -*-
33
4# Error on symbol errors
5export DPKG_GENSYMBOLS_CHECK_LEVEL=4
6
7# Get full logs in tests4# Get full logs in tests
8export G_MESSAGES_DEBUG=all5export G_MESSAGES_DEBUG=all
96
107
=== modified file 'helpers-shared.c'
--- helpers-shared.c 2015-07-15 02:32:54 +0000
+++ helpers-shared.c 2016-03-04 11:34:19 +0000
@@ -145,6 +145,8 @@
145 cgroup_manager_connection_core_cb(g_dbus_connection_new_for_address_finish, res, (cgm_connection_t *)data);145 cgroup_manager_connection_core_cb(g_dbus_connection_new_for_address_finish, res, (cgm_connection_t *)data);
146}146}
147147
148G_DEFINE_QUARK(CGMANAGER_CONTEXT, cgmanager_context);
149
148/* Get the connection to the cgroup manager */150/* Get the connection to the cgroup manager */
149GDBusConnection *151GDBusConnection *
150cgroup_manager_connection (void)152cgroup_manager_connection (void)
@@ -191,7 +193,9 @@
191 g_main_context_pop_thread_default(context);193 g_main_context_pop_thread_default(context);
192194
193 if (!use_session_bus && connection.con != NULL) {195 if (!use_session_bus && connection.con != NULL) {
194 g_object_set_data(G_OBJECT(connection.con), "cgmanager-context", context);196 g_object_set_qdata(G_OBJECT(connection.con),
197 cgmanager_context_quark(),
198 context);
195 } else {199 } else {
196 g_main_context_unref(context);200 g_main_context_unref(context);
197 }201 }
@@ -213,7 +217,7 @@
213 if (cgmanager == NULL)217 if (cgmanager == NULL)
214 return;218 return;
215219
216 GMainContext * creationcontext = g_object_get_data(G_OBJECT(cgmanager), "cgmanager-context");220 GMainContext * creationcontext = g_object_get_qdata(G_OBJECT(cgmanager), cgmanager_context_quark());
217 if (creationcontext == NULL) {221 if (creationcontext == NULL) {
218 g_object_unref(cgmanager);222 g_object_unref(cgmanager);
219 return;223 return;
@@ -228,6 +232,11 @@
228 }232 }
229233
230 g_object_unref(cgmanager);234 g_object_unref(cgmanager);
235
236 while (g_main_context_pending(creationcontext)) {
237 g_main_context_iteration(creationcontext, TRUE /* may block */);
238 }
239
231 g_main_context_unref(creationcontext);240 g_main_context_unref(creationcontext);
232}241}
233242
234243
=== modified file 'libubuntu-app-launch/CMakeLists.txt'
--- libubuntu-app-launch/CMakeLists.txt 2015-08-11 19:11:15 +0000
+++ libubuntu-app-launch/CMakeLists.txt 2016-03-04 11:34:19 +0000
@@ -17,6 +17,7 @@
17add_lttng_gen_tp(NAME ubuntu-app-launch-trace)17add_lttng_gen_tp(NAME ubuntu-app-launch-trace)
1818
19set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")19set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
20set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
20add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" -DDEMANGLER_PATH="${pkglibexecdir}/socket-demangler" )21add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" -DDEMANGLER_PATH="${pkglibexecdir}/socket-demangler" )
21add_definitions ( -DLIBERTINE_LAUNCH="${CMAKE_INSTALL_FULL_BINDIR}/libertine-launch" )22add_definitions ( -DLIBERTINE_LAUNCH="${CMAKE_INSTALL_FULL_BINDIR}/libertine-launch" )
2223
@@ -24,6 +25,28 @@
24ubuntu-app-launch.h25ubuntu-app-launch.h
25)26)
2627
28set(LAUNCHER_CPP_HEADERS
29appid.h
30application.h
31helper.h
32registry.h
33type-tagger.h
34)
35
36set(LAUNCHER_CPP_SOURCES
37application.cpp
38helper.cpp
39registry.cpp
40registry-impl.cpp
41application-impl-base.cpp
42application-impl-click.cpp
43application-impl-legacy.cpp
44application-impl-libertine.cpp
45application-info-desktop.cpp
46helper-impl-click.cpp
47glib-thread.cpp
48)
49
27set(LAUNCHER_SOURCES50set(LAUNCHER_SOURCES
28ubuntu-app-launch.c51ubuntu-app-launch.c
29second-exec-core.c52second-exec-core.c
@@ -31,6 +54,11 @@
31desktop-exec.c54desktop-exec.c
32ubuntu-app-launch-trace.c55ubuntu-app-launch-trace.c
33app-info.c56app-info.c
57${LAUNCHER_CPP_SOURCES}
58)
59
60add_custom_target(format
61 COMMAND clang-format -i -style=file ${LAUNCHER_CPP_HEADERS} ${LAUNCHER_CPP_SOURCES}
34)62)
3563
36set(LAUNCHER_GEN_SOURCES64set(LAUNCHER_GEN_SOURCES
@@ -59,6 +87,7 @@
59 ${CLICK_LIBRARIES}87 ${CLICK_LIBRARIES}
60 ${ZEITGEIST_LIBRARIES}88 ${ZEITGEIST_LIBRARIES}
61 ${MIR_LIBRARIES}89 ${MIR_LIBRARIES}
90 ${LIBERTINE_LIBRARIES}
62 helpers91 helpers
63 -Wl,--no-undefined92 -Wl,--no-undefined
64)93)
@@ -69,6 +98,11 @@
69)98)
7099
71install(100install(
101 FILES ${LAUNCHER_CPP_HEADERS}
102 DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libubuntu-app-launch-${API_VERSION}/ubuntu-app-launch"
103)
104
105install(
72 TARGETS ubuntu-launcher106 TARGETS ubuntu-launcher
73 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}107 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
74)108)
75109
=== added file 'libubuntu-app-launch/appid.h'
--- libubuntu-app-launch/appid.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/appid.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,83 @@
1/*
2 * Copyright © 2016 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 <string>
21
22#include "type-tagger.h"
23
24#pragma once
25#pragma GCC visibility push(default)
26
27namespace ubuntu
28{
29namespace app_launch
30{
31
32struct AppID
33{
34 struct PackageTag;
35 struct AppNameTag;
36 struct VersionTag;
37
38 typedef TypeTagger<PackageTag, std::string> Package;
39 typedef TypeTagger<AppNameTag, std::string> AppName;
40 typedef TypeTagger<VersionTag, std::string> Version;
41
42 Package package;
43 AppName appname;
44 Version version;
45
46 operator std::string() const;
47
48 AppID();
49 AppID(Package pkg, AppName app, Version ver);
50 bool empty() const;
51
52 static AppID parse(const std::string& appid);
53
54 enum class ApplicationWildcard
55 {
56 FIRST_LISTED,
57 LAST_LISTED,
58 ONLY_LISTED,
59 };
60 enum class VersionWildcard
61 {
62 CURRENT_USER_VERSION,
63 };
64
65 static AppID discover(const std::string& package,
66 ApplicationWildcard appwildcard = ApplicationWildcard::FIRST_LISTED,
67 VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION);
68 static AppID discover(const std::string& package,
69 const std::string& appname,
70 VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION);
71 static AppID discover(const std::string& package, const std::string& appname, const std::string& version);
72
73 static AppID find(const std::string& sappid);
74 static bool valid(const std::string& sappid);
75};
76
77bool operator==(const AppID& a, const AppID& b);
78bool operator!=(const AppID& a, const AppID& b);
79
80}; // namespace app_launch
81}; // namespace ubuntu
82
83#pragma GCC visibility pop
084
=== added file 'libubuntu-app-launch/application-impl-base.cpp'
--- libubuntu-app-launch/application-impl-base.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-base.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,162 @@
1/*
2 * Copyright © 2016 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 "application-impl-base.h"
21
22namespace ubuntu
23{
24namespace app_launch
25{
26namespace app_impls
27{
28
29Base::Base(const std::shared_ptr<Registry>& registry)
30 : _registry(registry)
31{
32}
33
34bool Base::hasInstances()
35{
36 std::string sappid = appId();
37 return ubuntu_app_launch_get_primary_pid(sappid.c_str()) != 0;
38}
39
40class BaseInstance : public Application::Instance
41{
42public:
43 explicit BaseInstance(const std::string& appId);
44
45 /* Query lifecycle */
46 bool isRunning() override
47 {
48 return ubuntu_app_launch_get_primary_pid(_appId.c_str()) != 0;
49 }
50 pid_t primaryPid() override
51 {
52 return ubuntu_app_launch_get_primary_pid(_appId.c_str());
53 }
54 bool hasPid(pid_t pid) override
55 {
56 return ubuntu_app_launch_pid_in_app_id(pid, _appId.c_str()) == TRUE;
57 }
58 std::string logPath() override
59 {
60 auto cpath = ubuntu_app_launch_application_log_path(_appId.c_str());
61 if (cpath != nullptr)
62 {
63 std::string retval(cpath);
64 g_free(cpath);
65 return retval;
66 }
67 else
68 {
69 return {};
70 }
71 }
72 std::vector<pid_t> pids() override
73 {
74 std::vector<pid_t> vector;
75 GList* list = ubuntu_app_launch_get_pids(_appId.c_str());
76
77 for (GList* pntr = list; pntr != nullptr; pntr = g_list_next(pntr))
78 {
79 vector.push_back(static_cast<pid_t>(GPOINTER_TO_INT(list->data)));
80 }
81
82 g_list_free(list);
83
84 return vector;
85 }
86
87 /* Manage lifecycle */
88 void pause() override
89 {
90 ubuntu_app_launch_pause_application(_appId.c_str());
91 }
92 void resume() override
93 {
94 ubuntu_app_launch_resume_application(_appId.c_str());
95 }
96 void stop() override
97 {
98 ubuntu_app_launch_stop_application(_appId.c_str());
99 }
100
101private:
102 std::string _appId;
103};
104
105BaseInstance::BaseInstance(const std::string& appId)
106 : _appId(appId)
107{
108}
109
110std::vector<std::shared_ptr<Application::Instance>> Base::instances()
111{
112 std::vector<std::shared_ptr<Instance>> vect;
113 vect.emplace_back(std::make_shared<BaseInstance>(appId()));
114 return vect;
115}
116
117std::shared_ptr<gchar*> urlsToStrv(std::vector<Application::URL> urls)
118{
119 auto array = g_array_new(TRUE, FALSE, sizeof(gchar*));
120
121 for (auto url : urls)
122 {
123 auto str = g_strdup(url.value().c_str());
124 g_array_append_val(array, str);
125 }
126
127 return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev);
128}
129
130std::shared_ptr<Application::Instance> Base::launch(const std::vector<Application::URL>& urls)
131{
132 std::string appIdStr = appId();
133 std::shared_ptr<gchar*> urlstrv;
134
135 if (urls.size() > 0)
136 {
137 urlstrv = urlsToStrv(urls);
138 }
139
140 ubuntu_app_launch_start_application(appIdStr.c_str(), urlstrv.get());
141
142 return std::make_shared<BaseInstance>(appIdStr);
143}
144
145std::shared_ptr<Application::Instance> Base::launchTest(const std::vector<Application::URL>& urls)
146{
147 std::string appIdStr = appId();
148 std::shared_ptr<gchar*> urlstrv;
149
150 if (urls.size() > 0)
151 {
152 urlstrv = urlsToStrv(urls);
153 }
154
155 ubuntu_app_launch_start_application_test(appIdStr.c_str(), urlstrv.get());
156
157 return std::make_shared<BaseInstance>(appIdStr);
158}
159
160}; // namespace app_impls
161}; // namespace app_launch
162}; // namespace ubuntu
0163
=== added file 'libubuntu-app-launch/application-impl-base.h'
--- libubuntu-app-launch/application-impl-base.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-base.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,52 @@
1/*
2 * Copyright © 2016 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 "application.h"
21
22extern "C" {
23#include "ubuntu-app-launch.h"
24}
25
26#pragma once
27
28namespace ubuntu
29{
30namespace app_launch
31{
32namespace app_impls
33{
34
35class Base : public ubuntu::app_launch::Application
36{
37public:
38 Base(const std::shared_ptr<Registry> &registry);
39
40 bool hasInstances() override;
41 std::vector<std::shared_ptr<Instance>> instances() override;
42
43 std::shared_ptr<Instance> launch(const std::vector<Application::URL> &urls = {}) override;
44 std::shared_ptr<Instance> launchTest(const std::vector<Application::URL> &urls = {}) override;
45
46protected:
47 std::shared_ptr<Registry> _registry;
48};
49
50}; // namespace app_impls
51}; // namespace app_launch
52}; // namespace ubuntu
053
=== added file 'libubuntu-app-launch/application-impl-click.cpp'
--- libubuntu-app-launch/application-impl-click.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-click.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,157 @@
1/*
2 * Copyright © 2016 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 "application-impl-click.h"
21#include "application-info-desktop.h"
22#include "registry-impl.h"
23
24namespace ubuntu
25{
26namespace app_launch
27{
28namespace app_impls
29{
30
31AppID::Version manifestVersion(const std::shared_ptr<JsonObject>& manifest);
32std::list<AppID::AppName> manifestApps(const std::shared_ptr<JsonObject>& manifest);
33std::shared_ptr<GKeyFile> manifestAppDesktop(const std::shared_ptr<JsonObject>& manifest,
34 const std::string& app,
35 const std::string& clickDir);
36
37Click::Click(const AppID& appid, const std::shared_ptr<Registry>& registry)
38 : Click(appid, registry->impl->getClickManifest(appid.package), registry)
39{
40}
41
42Click::Click(const AppID& appid, const std::shared_ptr<JsonObject>& manifest, const std::shared_ptr<Registry>& registry)
43 : Base(registry)
44 , _appid(appid)
45 , _manifest(manifest)
46 , _clickDir(registry->impl->getClickDir(appid.package))
47 , _keyfile(manifestAppDesktop(manifest, appid.appname, _clickDir))
48{
49 if (!_keyfile)
50 throw std::runtime_error{"No keyfile found for click application: " + (std::string)appid};
51}
52
53AppID Click::appId()
54{
55 return _appid;
56}
57
58std::shared_ptr<Application::Info> Click::info()
59{
60 return std::make_shared<app_info::Desktop>(_keyfile, _clickDir);
61}
62
63AppID::Version manifestVersion(const std::shared_ptr<JsonObject>& manifest)
64{
65 auto cstr = json_object_get_string_member(manifest.get(), "version");
66
67 if (cstr == nullptr)
68 throw std::runtime_error("Unable to find version number in manifest");
69
70 auto cppstr = AppID::Version::from_raw((const gchar*)cstr);
71 return cppstr;
72}
73
74std::list<AppID::AppName> manifestApps(const std::shared_ptr<JsonObject>& manifest)
75{
76 auto hooks = json_object_get_object_member(manifest.get(), "hooks");
77 if (hooks == nullptr)
78 throw std::runtime_error("Manifest for application does not have a 'hooks' field");
79
80 auto gapps = json_object_get_members(hooks);
81 if (gapps == nullptr)
82 throw std::runtime_error("GLib JSON confusion, please talk to your library vendor");
83
84 std::list<AppID::AppName> apps;
85
86 for (GList* item = gapps; item != nullptr; item = g_list_next(item))
87 {
88 auto appname = (const gchar*)item->data;
89
90 auto hooklist = json_object_get_object_member(hooks, appname);
91
92 if (json_object_has_member(hooklist, "desktop") == TRUE)
93 {
94 apps.emplace_back(AppID::AppName::from_raw(appname));
95 }
96 }
97
98 g_list_free_full(gapps, g_free);
99 return apps;
100}
101
102std::shared_ptr<GKeyFile> manifestAppDesktop(const std::shared_ptr<JsonObject>& manifest,
103 const std::string& app,
104 const std::string& clickDir)
105{
106 auto hooks = json_object_get_object_member(manifest.get(), "hooks");
107 if (hooks == nullptr)
108 throw std::runtime_error("Manifest for application '" + app + "' does not have a 'hooks' field");
109
110 auto gapps = json_object_get_members(hooks);
111 if (gapps == nullptr)
112 throw std::runtime_error("GLib JSON confusion, please talk to your library vendor");
113
114 auto hooklist = json_object_get_object_member(hooks, app.c_str());
115 if (hooklist == nullptr)
116 throw std::runtime_error("Manifest for does not have an application '" + app + "'");
117
118 auto desktoppath = json_object_get_string_member(hooklist, "desktop");
119 if (desktoppath == nullptr)
120 throw std::runtime_error("Manifest for application '" + app + "' does not have a 'desktop' hook");
121
122 auto path = std::shared_ptr<gchar>(g_build_filename(clickDir.c_str(), desktoppath, nullptr), g_free);
123
124 std::shared_ptr<GKeyFile> keyfile(g_key_file_new(), g_key_file_free);
125 GError* error = nullptr;
126 g_key_file_load_from_file(keyfile.get(), path.get(), G_KEY_FILE_NONE, &error);
127 if (error != nullptr)
128 {
129 auto perror = std::shared_ptr<GError>(error, g_error_free);
130 throw std::runtime_error(perror.get()->message);
131 }
132
133 return keyfile;
134}
135
136std::list<std::shared_ptr<Application>> Click::list(const std::shared_ptr<Registry>& registry)
137{
138 std::list<std::shared_ptr<Application>> applist;
139
140 for (auto pkg : registry->impl->getClickPackages())
141 {
142 auto manifest = registry->impl->getClickManifest(pkg);
143
144 for (auto appname : manifestApps(manifest))
145 {
146 AppID appid{package : pkg, appname : appname, version : manifestVersion(manifest)};
147 auto app = std::make_shared<Click>(appid, manifest, registry);
148 applist.push_back(app);
149 }
150 }
151
152 return applist;
153}
154
155}; // namespace app_impls
156}; // namespace app_launch
157}; // namespace ubuntu
0158
=== added file 'libubuntu-app-launch/application-impl-click.h'
--- libubuntu-app-launch/application-impl-click.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-click.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,56 @@
1/*
2 * Copyright © 2016 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 <gio/gdesktopappinfo.h>
21#include <json-glib/json-glib.h>
22#include "application-impl-base.h"
23
24#pragma once
25
26namespace ubuntu
27{
28namespace app_launch
29{
30namespace app_impls
31{
32
33class Click : public Base
34{
35public:
36 Click(const AppID& appid, const std::shared_ptr<Registry>& registry);
37 Click(const AppID& appid, const std::shared_ptr<JsonObject>& manifest, const std::shared_ptr<Registry>& registry);
38
39 static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry>& registry);
40
41 AppID appId() override;
42
43 std::shared_ptr<Info> info() override;
44
45private:
46 AppID _appid;
47
48 std::shared_ptr<JsonObject> _manifest;
49
50 std::string _clickDir;
51 std::shared_ptr<GKeyFile> _keyfile;
52};
53
54}; // namespace app_impls
55}; // namespace app_launch
56}; // namespace ubuntu
057
=== added file 'libubuntu-app-launch/application-impl-legacy.cpp'
--- libubuntu-app-launch/application-impl-legacy.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-legacy.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,128 @@
1/*
2 * Copyright © 2016 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 "application-impl-legacy.h"
21#include "application-info-desktop.h"
22
23namespace ubuntu
24{
25namespace app_launch
26{
27namespace app_impls
28{
29
30std::shared_ptr<GKeyFile> keyfileForApp(const AppID::AppName& name);
31
32void clear_keyfile(GKeyFile* keyfile)
33{
34 if (keyfile != nullptr)
35 {
36 g_key_file_free(keyfile);
37 }
38}
39
40Legacy::Legacy(const AppID::AppName& appname,
41 const std::shared_ptr<GKeyFile>& keyfile,
42 const std::shared_ptr<Registry>& registry)
43 : Base(registry)
44 , _appname(appname)
45 , _keyfile(keyfile)
46{
47 if (!_keyfile)
48 throw std::runtime_error{"Unable to find keyfile for legacy application: " + appname.value()};
49}
50
51Legacy::Legacy(const AppID::AppName& appname, const std::shared_ptr<Registry>& registry)
52 : Legacy(appname, keyfileForApp(appname), registry)
53{
54}
55
56std::shared_ptr<GKeyFile> keyfileForApp(const AppID::AppName& name)
57{
58 std::string desktopName = name.value() + ".desktop";
59 auto keyfilecheck = [desktopName](const gchar* dir) -> std::shared_ptr<GKeyFile> {
60 auto fullname = g_build_filename(dir, "applications", desktopName.c_str(), nullptr);
61 if (!g_file_test(fullname, G_FILE_TEST_EXISTS))
62 {
63 g_free(fullname);
64 return {};
65 }
66
67 auto keyfile = std::shared_ptr<GKeyFile>(g_key_file_new(), clear_keyfile);
68
69 GError* error = nullptr;
70 g_key_file_load_from_file(keyfile.get(), fullname, G_KEY_FILE_NONE, &error);
71 g_free(fullname);
72
73 if (error != nullptr)
74 {
75 g_debug("Unable to load keyfile '%s' becuase: %s", desktopName.c_str(), error->message);
76 g_error_free(error);
77 return {};
78 }
79
80 return keyfile;
81 };
82
83 auto retval = keyfilecheck(g_get_user_data_dir());
84
85 auto systemDirs = g_get_system_data_dirs();
86 for (auto i = 0; !retval && systemDirs[i] != nullptr; i++)
87 {
88 retval = keyfilecheck(systemDirs[i]);
89 }
90
91 return retval;
92}
93
94std::shared_ptr<Application::Info> Legacy::info()
95{
96 return std::make_shared<app_info::Desktop>(_keyfile, "/usr/share/icons/");
97}
98
99std::list<std::shared_ptr<Application>> Legacy::list(const std::shared_ptr<Registry>& registry)
100{
101 std::list<std::shared_ptr<Application>> list;
102 GList* head = g_app_info_get_all();
103 for (GList* item = head; item != nullptr; item = g_list_next(item))
104 {
105 GDesktopAppInfo* appinfo = G_DESKTOP_APP_INFO(item->data);
106
107 if (appinfo == nullptr)
108 {
109 continue;
110 }
111
112 if (g_app_info_should_show(G_APP_INFO(appinfo)) == FALSE)
113 {
114 continue;
115 }
116
117 auto app = std::make_shared<Legacy>(AppID::AppName::from_raw(g_app_info_get_id(G_APP_INFO(appinfo))), registry);
118 list.push_back(app);
119 }
120
121 g_list_free_full(head, g_object_unref);
122
123 return list;
124}
125
126}; // namespace app_impls
127}; // namespace app_launch
128}; // namespace ubuntu
0129
=== added file 'libubuntu-app-launch/application-impl-legacy.h'
--- libubuntu-app-launch/application-impl-legacy.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-legacy.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,57 @@
1/*
2 * Copyright © 2016 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 <gio/gdesktopappinfo.h>
21
22#include "application-impl-base.h"
23
24#pragma once
25
26namespace ubuntu
27{
28namespace app_launch
29{
30namespace app_impls
31{
32
33class Legacy : public Base
34{
35public:
36 Legacy(const AppID::AppName& appname, const std::shared_ptr<Registry>& registry);
37 Legacy(const AppID::AppName& appname,
38 const std::shared_ptr<GKeyFile>& keyfile,
39 const std::shared_ptr<Registry>& registry);
40
41 AppID appId() override
42 {
43 return {package : AppID::Package::from_raw({}), appname : _appname, version : AppID::Version::from_raw({})};
44 }
45
46 std::shared_ptr<Info> info() override;
47
48 static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry> &registry);
49
50private:
51 AppID::AppName _appname;
52 std::shared_ptr<GKeyFile> _keyfile;
53};
54
55}; // namespace app_impls
56}; // namespace app_launch
57}; // namespace ubuntu
058
=== added file 'libubuntu-app-launch/application-impl-libertine.cpp'
--- libubuntu-app-launch/application-impl-libertine.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-libertine.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,127 @@
1/*
2 * Copyright © 2016 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 "application-impl-libertine.h"
21#include "application-info-desktop.h"
22#include "libertine.h"
23
24namespace ubuntu
25{
26namespace app_launch
27{
28namespace app_impls
29{
30
31std::shared_ptr<GKeyFile> keyfileFromPath(const gchar* pathname);
32
33Libertine::Libertine(const AppID::Package& container,
34 const AppID::AppName& appname,
35 const std::shared_ptr<Registry>& registry)
36 : Base(registry)
37 , _container(container)
38 , _appname(appname)
39{
40 if (!_keyfile)
41 {
42 auto container_path = libertine_container_path(container.value().c_str());
43 auto container_app_path = g_build_filename(container_path, "usr", "share", "applications",
44 (appname.value() + ".desktop").c_str(), NULL);
45
46 _keyfile = keyfileFromPath(container_app_path);
47
48 g_free(container_app_path);
49 g_free(container_path);
50 }
51
52 if (!_keyfile)
53 {
54 auto home_path = libertine_container_home_path(container.value().c_str());
55 auto home_app_path = g_build_filename(home_path, ".local", "share", "applications",
56 (appname.value() + ".desktop").c_str(), NULL);
57
58 _keyfile = keyfileFromPath(home_app_path);
59
60 g_free(home_app_path);
61 g_free(home_path);
62 }
63
64 if (!_keyfile)
65 throw std::runtime_error{"Unable to find a keyfile for application '" + appname.value() + "' in container '" +
66 container.value() + "'"};
67}
68
69std::shared_ptr<GKeyFile> keyfileFromPath(const gchar* pathname)
70{
71 if (!g_file_test(pathname, G_FILE_TEST_EXISTS))
72 {
73 return {};
74 }
75
76 std::shared_ptr<GKeyFile> keyfile(g_key_file_new(), [](GKeyFile* keyfile) {
77 if (keyfile != nullptr)
78 {
79 g_key_file_free(keyfile);
80 }
81 });
82 GError* error = nullptr;
83
84 g_key_file_load_from_file(keyfile.get(), pathname, G_KEY_FILE_NONE, &error);
85
86 if (error != nullptr)
87 {
88 g_error_free(error);
89 return {};
90 }
91
92 return keyfile;
93}
94
95std::list<std::shared_ptr<Application>> Libertine::list(const std::shared_ptr<Registry>& registry)
96{
97 std::list<std::shared_ptr<Application>> applist;
98
99 auto containers = libertine_list_containers();
100
101 for (int i = 0; containers[i] != nullptr; i++)
102 {
103 auto container = containers[i];
104 auto apps = libertine_list_apps_for_container(container);
105
106 for (int i = 0; apps[i] != nullptr; i++)
107 {
108 auto sapp = std::make_shared<Libertine>(AppID::Package::from_raw(container),
109 AppID::AppName::from_raw(apps[i]), registry);
110 applist.push_back(sapp);
111 }
112
113 g_strfreev(apps);
114 }
115 g_strfreev(containers);
116
117 return applist;
118}
119
120std::shared_ptr<Application::Info> Libertine::info()
121{
122 return std::make_shared<app_info::Desktop>(_keyfile, libertine_container_path(_container.value().c_str()));
123}
124
125}; // namespace app_impls
126}; // namespace app_launch
127}; // namespace ubuntu
0128
=== added file 'libubuntu-app-launch/application-impl-libertine.h'
--- libubuntu-app-launch/application-impl-libertine.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-impl-libertine.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,56 @@
1/*
2 * Copyright © 2016 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 "application-impl-base.h"
21#include <gio/gdesktopappinfo.h>
22
23#pragma once
24
25namespace ubuntu
26{
27namespace app_launch
28{
29namespace app_impls
30{
31
32class Libertine : public Base
33{
34public:
35 Libertine(const AppID::Package& container,
36 const AppID::AppName& appname,
37 const std::shared_ptr<Registry>& registry);
38
39 static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry> &registry);
40
41 AppID appId() override
42 {
43 return {package : _container, appname : _appname, version : AppID::Version::from_raw("0.0")};
44 }
45
46 std::shared_ptr<Info> info() override;
47
48private:
49 AppID::Package _container;
50 AppID::AppName _appname;
51 std::shared_ptr<GKeyFile> _keyfile;
52};
53
54}; // namespace app_impls
55}; // namespace app_launch
56}; // namespace ubuntu
057
=== added file 'libubuntu-app-launch/application-info-desktop.cpp'
--- libubuntu-app-launch/application-info-desktop.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-info-desktop.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,202 @@
1/*
2 * Copyright © 2016 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 "application-info-desktop.h"
21
22namespace ubuntu
23{
24namespace app_launch
25{
26namespace app_info
27{
28
29constexpr const char* DESKTOP_GROUP = "Desktop Entry";
30
31template <typename T>
32auto stringFromKeyfile(std::shared_ptr<GKeyFile> keyfile, const std::string& key, const std::string& exceptionText = {})
33 -> T
34{
35 GError* error = nullptr;
36 auto keyval = g_key_file_get_locale_string(keyfile.get(), DESKTOP_GROUP, key.c_str(), nullptr, &error);
37
38 if (error != nullptr)
39 {
40 auto perror = std::shared_ptr<GError>(error, g_error_free);
41 if (!exceptionText.empty())
42 {
43 throw std::runtime_error(exceptionText + perror.get()->message);
44 }
45
46 return T::from_raw({});
47 }
48
49 T retval = T::from_raw(keyval);
50 g_free(keyval);
51 return retval;
52}
53
54template <typename T>
55auto fileFromKeyfile(std::shared_ptr<GKeyFile> keyfile,
56 const std::string basePath,
57 const std::string& key,
58 const std::string& exceptionText = {}) -> T
59{
60 GError* error = nullptr;
61 auto keyval = g_key_file_get_locale_string(keyfile.get(), DESKTOP_GROUP, key.c_str(), nullptr, &error);
62
63 if (error != nullptr)
64 {
65 auto perror = std::shared_ptr<GError>(error, g_error_free);
66 if (!exceptionText.empty())
67 {
68 throw std::runtime_error(exceptionText + perror.get()->message);
69 }
70
71 return T::from_raw({});
72 }
73
74 /* If we're already an absolute path, don't prepend the base path */
75 if (keyval[0] == '/')
76 {
77 T retval = T::from_raw(keyval);
78 g_free(keyval);
79 return retval;
80 }
81
82 auto cpath = g_build_filename(basePath.c_str(), keyval, nullptr);
83
84 T retval = T::from_raw(cpath);
85
86 g_free(keyval);
87 g_free(cpath);
88
89 return retval;
90}
91
92template <typename T>
93auto boolFromKeyfile(std::shared_ptr<GKeyFile> keyfile,
94 const std::string& key,
95 bool defaultReturn,
96 const std::string& exceptionText = {}) -> T
97{
98 GError* error = nullptr;
99 auto keyval = g_key_file_get_boolean(keyfile.get(), DESKTOP_GROUP, key.c_str(), &error);
100
101 if (error != nullptr)
102 {
103 auto perror = std::shared_ptr<GError>(error, g_error_free);
104 if (!exceptionText.empty())
105 {
106 throw std::runtime_error(exceptionText + perror.get()->message);
107 }
108
109 return T::from_raw(defaultReturn);
110 }
111
112 T retval = T::from_raw(keyval == TRUE);
113 return retval;
114}
115
116Desktop::Desktop(std::shared_ptr<GKeyFile> keyfile, const std::string& basePath)
117 : _keyfile([keyfile]() {
118 if (!keyfile)
119 {
120 throw std::runtime_error("Can not build a desktop application info object with a null keyfile");
121 }
122 return keyfile;
123 }())
124 , _basePath(basePath)
125 , _name(stringFromKeyfile<Application::Info::Name>(keyfile, "Name", "Unable to get name from keyfile"))
126 , _description(stringFromKeyfile<Application::Info::Description>(keyfile, "Comment"))
127 , _iconPath(
128 fileFromKeyfile<Application::Info::IconPath>(keyfile, basePath, "Icon", "Missing icon for desktop file"))
129 , _splashInfo({
130 title : stringFromKeyfile<Application::Info::Splash::Title>(keyfile, "X-Ubuntu-Splash-Title"),
131 image : fileFromKeyfile<Application::Info::Splash::Image>(keyfile, basePath, "X-Ubuntu-Splash-Image"),
132 backgroundColor : stringFromKeyfile<Application::Info::Splash::Color>(keyfile, "X-Ubuntu-Splash-Color"),
133 headerColor : stringFromKeyfile<Application::Info::Splash::Color>(keyfile, "X-Ubuntu-Splash-Color-Header"),
134 footerColor : stringFromKeyfile<Application::Info::Splash::Color>(keyfile, "X-Ubuntu-Splash-Color-Footer"),
135 showHeader :
136 boolFromKeyfile<Application::Info::Splash::ShowHeader>(keyfile, "X-Ubuntu-Splash-Show-Header", false)
137 })
138 , _supportedOrientations([keyfile]() {
139 Orientations all = {portrait : true, landscape : true, invertedPortrait : true, invertedLandscape : true};
140
141 GError* error = nullptr;
142 auto orientationStrv = g_key_file_get_string_list(keyfile.get(), DESKTOP_GROUP,
143 "X-Ubuntu-Supported-Orientations", nullptr, &error);
144
145 if (error != nullptr)
146 {
147 g_error_free(error);
148 return all;
149 }
150
151 Orientations retval =
152 {portrait : false, landscape : false, invertedPortrait : false, invertedLandscape : false};
153
154 try
155 {
156 for (auto i = 0; orientationStrv[i] != nullptr; i++)
157 {
158 g_strstrip(orientationStrv[i]); /* remove whitespace */
159
160 if (g_ascii_strcasecmp("portrait", orientationStrv[i]) == 0)
161 {
162 retval.portrait = true;
163 }
164 else if (g_ascii_strcasecmp("landscape", orientationStrv[i]) == 0)
165 {
166 retval.landscape = true;
167 }
168 else if (g_ascii_strcasecmp("invertedPortrait", orientationStrv[i]) == 0)
169 {
170 retval.invertedPortrait = true;
171 }
172 else if (g_ascii_strcasecmp("invertedLandscape", orientationStrv[i]) == 0)
173 {
174 retval.invertedLandscape = true;
175 }
176 else if (g_ascii_strcasecmp("primary", orientationStrv[i]) == 0 && i == 0)
177 {
178 /* Pass, we'll let primary be the first entry, it should be the only. */
179 }
180 else
181 {
182 throw std::runtime_error("Invalid orientation string '" + std::string(orientationStrv[i]) + "'");
183 }
184 }
185 }
186 catch (...)
187 {
188 retval = all;
189 }
190
191 g_strfreev(orientationStrv);
192 return retval;
193 }())
194 , _rotatesWindow(
195 boolFromKeyfile<Application::Info::RotatesWindow>(keyfile, "X-Ubuntu-Rotates-Window-Content", false))
196 , _ubuntuLifecycle(boolFromKeyfile<Application::Info::UbuntuLifecycle>(keyfile, "X-Ubuntu-Touch", false))
197{
198}
199
200}; // namespace app_info
201}; // namespace app_launch
202}; // namespace ubuntu
0203
=== added file 'libubuntu-app-launch/application-info-desktop.h'
--- libubuntu-app-launch/application-info-desktop.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application-info-desktop.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,87 @@
1/*
2 * Copyright © 2016 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 "application.h"
21#include <glib.h>
22#include <mutex>
23
24#pragma once
25
26namespace ubuntu
27{
28namespace app_launch
29{
30namespace app_info
31{
32
33class Desktop : public Application::Info
34{
35public:
36 Desktop(std::shared_ptr<GKeyFile> keyfile, const std::string& basePath);
37
38 const Application::Info::Name& name() override
39 {
40 return _name;
41 }
42 const Application::Info::Description& description() override
43 {
44 return _description;
45 }
46 const Application::Info::IconPath& iconPath() override
47 {
48 return _iconPath;
49 }
50
51 Application::Info::Splash splash() override
52 {
53 return _splashInfo;
54 }
55
56 Application::Info::Orientations supportedOrientations() override
57 {
58 return _supportedOrientations;
59 }
60
61 Application::Info::RotatesWindow rotatesWindowContents() override
62 {
63 return _rotatesWindow;
64 }
65
66 Application::Info::UbuntuLifecycle supportsUbuntuLifecycle() override
67 {
68 return _ubuntuLifecycle;
69 }
70
71private:
72 std::shared_ptr<GKeyFile> _keyfile;
73 std::string _basePath;
74
75 Application::Info::Name _name;
76 Application::Info::Description _description;
77 Application::Info::IconPath _iconPath;
78
79 Application::Info::Splash _splashInfo;
80 Application::Info::Orientations _supportedOrientations;
81 Application::Info::RotatesWindow _rotatesWindow;
82 Application::Info::UbuntuLifecycle _ubuntuLifecycle;
83};
84
85}; // namespace AppInfo
86}; // namespace AppLaunch
87}; // namespace Ubuntu
088
=== added file 'libubuntu-app-launch/application.cpp'
--- libubuntu-app-launch/application.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,225 @@
1/*
2 * Copyright © 2016 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 "app-info.h"
22#include "ubuntu-app-launch.h"
23}
24
25#include "application-impl-click.h"
26#include "application-impl-legacy.h"
27#include "application-impl-libertine.h"
28#include "application.h"
29
30#include <iostream>
31#include <regex>
32
33namespace ubuntu
34{
35namespace app_launch
36{
37
38std::shared_ptr<Application> Application::create(const AppID& appid, const std::shared_ptr<Registry>& registry)
39{
40 if (appid.empty())
41 {
42 throw std::runtime_error("AppID is empty");
43 }
44
45 std::string sappid = appid;
46 if (app_info_click(sappid.c_str(), NULL, NULL))
47 {
48 return std::make_shared<app_impls::Click>(appid, registry);
49 }
50 else if (app_info_libertine(sappid.c_str(), NULL, NULL))
51 {
52 return std::make_shared<app_impls::Libertine>(appid.package, appid.appname, registry);
53 }
54 else if (app_info_legacy(sappid.c_str(), NULL, NULL))
55 {
56 return std::make_shared<app_impls::Legacy>(appid.appname, registry);
57 }
58 else
59 {
60 throw std::runtime_error("Invalid app ID: " + sappid);
61 }
62}
63
64AppID::AppID()
65 : package(Package::from_raw({}))
66 , appname(AppName::from_raw({}))
67 , version(Version::from_raw({}))
68{
69}
70
71AppID::AppID(Package pkg, AppName app, Version ver)
72 : package(pkg)
73 , appname(app)
74 , version(ver)
75{
76}
77
78/* These are the Regex's taken from the Click Reviewer Tools
79 on Jan 16, 2016 revision 566 */
80#define REGEX_PKGNAME "([a-z0-9][a-z0-9+.-]+)"
81#define REGEX_APPNAME "([A-Za-z0-9+-.:~-]+)"
82#define REGEX_VERSION "([\\d+:]?[A-Za-z0-9.+:~-]+?[-[A-Za-z0-9+.~]+]?)"
83
84const std::regex full_appid_regex("^" REGEX_PKGNAME "_" REGEX_APPNAME "_" REGEX_VERSION "$");
85const std::regex short_appid_regex("^" REGEX_PKGNAME "_" REGEX_APPNAME "$");
86const std::regex legacy_appid_regex("^" REGEX_APPNAME "$");
87
88AppID AppID::parse(const std::string& sappid)
89{
90 std::smatch match;
91
92 if (std::regex_match(sappid, match, full_appid_regex))
93 {
94 return {AppID::Package::from_raw(match[1].str()), AppID::AppName::from_raw(match[2].str()),
95 AppID::Version::from_raw(match[3].str())};
96 }
97 else
98 {
99 /* Allow returning an empty AppID with empty internal */
100 return {AppID::Package::from_raw({}), AppID::AppName::from_raw({}), AppID::Version::from_raw({})};
101 }
102}
103
104bool AppID::valid(const std::string& sappid)
105{
106 return std::regex_match(sappid, full_appid_regex);
107}
108
109AppID AppID::find(const std::string& sappid)
110{
111 std::smatch match;
112
113 if (std::regex_match(sappid, match, full_appid_regex))
114 {
115 return {AppID::Package::from_raw(match[1].str()), AppID::AppName::from_raw(match[2].str()),
116 AppID::Version::from_raw(match[3].str())};
117 }
118 else if (std::regex_match(sappid, match, short_appid_regex))
119 {
120 return discover(match[1].str(), match[2].str());
121 }
122 else if (std::regex_match(sappid, match, legacy_appid_regex))
123 {
124 return {AppID::Package::from_raw({}), AppID::AppName::from_raw(sappid), AppID::Version::from_raw({})};
125 }
126 else
127 {
128 return {AppID::Package::from_raw({}), AppID::AppName::from_raw({}), AppID::Version::from_raw({})};
129 }
130}
131
132AppID::operator std::string() const
133{
134 if (package.value().empty() && version.value().empty())
135 {
136 if (appname.value().empty())
137 {
138 return {};
139 }
140 else
141 {
142 return appname.value();
143 }
144 }
145
146 return package.value() + "_" + appname.value() + "_" + version.value();
147}
148
149bool operator==(const AppID& a, const AppID& b)
150{
151 return a.package.value() == b.package.value() && a.appname.value() == b.appname.value() &&
152 a.version.value() == b.version.value();
153}
154
155bool operator!=(const AppID& a, const AppID& b)
156{
157 return a.package.value() != b.package.value() || a.appname.value() != b.appname.value() ||
158 a.version.value() != b.version.value();
159}
160
161bool AppID::empty() const
162{
163 return package.value().empty() && appname.value().empty() && version.value().empty();
164}
165
166std::string app_wildcard(AppID::ApplicationWildcard card)
167{
168 switch (card)
169 {
170 case AppID::ApplicationWildcard::FIRST_LISTED:
171 return "first-listed-app";
172 case AppID::ApplicationWildcard::LAST_LISTED:
173 return "last-listed-app";
174 case AppID::ApplicationWildcard::ONLY_LISTED:
175 return "only-listed-app";
176 }
177
178 return "";
179}
180
181std::string ver_wildcard(AppID::VersionWildcard card)
182{
183 switch (card)
184 {
185 case AppID::VersionWildcard::CURRENT_USER_VERSION:
186 return "current-user-version";
187 }
188
189 return "";
190}
191
192AppID AppID::discover(const std::string& package, const std::string& appname, const std::string& version)
193{
194 auto cappid = ubuntu_app_launch_triplet_to_app_id(package.c_str(), appname.c_str(), version.c_str());
195
196 auto appid = cappid != nullptr ? AppID::parse(cappid) : AppID::parse("");
197
198 g_free(cappid);
199
200 return appid;
201}
202
203AppID AppID::discover(const std::string& package, ApplicationWildcard appwildcard, VersionWildcard versionwildcard)
204{
205 return discover(package, app_wildcard(appwildcard), ver_wildcard(versionwildcard));
206}
207
208AppID AppID::discover(const std::string& package, const std::string& appname, VersionWildcard versionwildcard)
209{
210 auto appid = discover(package, appname, ver_wildcard(versionwildcard));
211
212 if (appid.empty())
213 {
214 /* If we weren't able to go that route, we can see if it's libertine */
215 if (app_info_libertine((package + "_" + appname + "_0.0").c_str(), nullptr, nullptr))
216 {
217 appid = AppID(Package::from_raw(package), AppName::from_raw(appname), Version::from_raw("0.0"));
218 }
219 }
220
221 return appid;
222}
223
224}; // namespace app_launch
225}; // namespace ubuntu
0226
=== added file 'libubuntu-app-launch/application.h'
--- libubuntu-app-launch/application.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/application.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,150 @@
1/*
2 * Copyright © 2016 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 <list>
21#include <memory>
22#include <sys/types.h>
23#include <vector>
24
25#include "appid.h"
26#include "type-tagger.h"
27
28#pragma once
29#pragma GCC visibility push(default)
30
31namespace ubuntu
32{
33namespace app_launch
34{
35
36class Registry;
37
38class Application
39{
40public:
41 struct URLTag;
42 typedef TypeTagger<URLTag, std::string> URL;
43
44 static std::shared_ptr<Application> create(const AppID& appid, const std::shared_ptr<Registry>& registry);
45
46 /* System level info */
47 virtual AppID appId() = 0;
48
49 class Info
50 {
51 public:
52 /* Basic information */
53 struct NameTag;
54 struct DescriptionTag;
55 struct IconPathTag;
56
57 typedef TypeTagger<NameTag, std::string> Name;
58 typedef TypeTagger<DescriptionTag, std::string> Description;
59 typedef TypeTagger<IconPathTag, std::string> IconPath;
60
61 virtual const Name& name() = 0;
62 virtual const Description& description() = 0;
63 virtual const IconPath& iconPath() = 0;
64
65 /* Splash information */
66 struct Splash
67 {
68 struct TitleTag;
69 struct ImageTag;
70 struct ColorTag;
71 struct ShowHeaderTag;
72
73 typedef TypeTagger<TitleTag, std::string> Title;
74 typedef TypeTagger<ImageTag, std::string> Image;
75 typedef TypeTagger<ColorTag, std::string> Color;
76 typedef TypeTagger<ShowHeaderTag, bool> ShowHeader;
77
78 Title title;
79 Image image;
80 Color backgroundColor;
81 Color headerColor;
82 Color footerColor;
83 ShowHeader showHeader;
84 };
85
86 virtual Splash splash() = 0;
87
88 /* Orientation and placement */
89 struct Orientations
90 {
91 bool portrait;
92 bool landscape;
93 bool invertedPortrait;
94 bool invertedLandscape;
95
96 bool operator==(const Orientations& b) const
97 {
98 return portrait == b.portrait && landscape == b.landscape && invertedPortrait == b.invertedPortrait &&
99 invertedLandscape == b.invertedLandscape;
100 }
101 };
102
103 struct RotatesWindowTag;
104
105 typedef TypeTagger<RotatesWindowTag, bool> RotatesWindow;
106
107 virtual Orientations supportedOrientations() = 0;
108 virtual RotatesWindow rotatesWindowContents() = 0;
109
110 /* Lifecycle */
111 struct UbuntuLifecycleTag;
112
113 typedef TypeTagger<UbuntuLifecycleTag, bool> UbuntuLifecycle;
114
115 virtual UbuntuLifecycle supportsUbuntuLifecycle() = 0;
116 };
117
118 virtual std::shared_ptr<Info> info() = 0;
119
120 class Instance
121 {
122 public:
123 /* Query lifecycle */
124 virtual bool isRunning() = 0;
125
126 /* Instance Info */
127 virtual std::string logPath() = 0;
128
129 /* PIDs */
130 virtual pid_t primaryPid() = 0;
131 virtual bool hasPid(pid_t pid) = 0;
132 virtual std::vector<pid_t> pids() = 0;
133
134 /* Manage lifecycle */
135 virtual void pause() = 0;
136 virtual void resume() = 0;
137 virtual void stop() = 0;
138 };
139
140 virtual bool hasInstances() = 0;
141 virtual std::vector<std::shared_ptr<Instance>> instances() = 0;
142
143 virtual std::shared_ptr<Instance> launch(const std::vector<URL>& urls = {}) = 0;
144 virtual std::shared_ptr<Instance> launchTest(const std::vector<URL>& urls = {}) = 0;
145};
146
147}; // namespace app_launch
148}; // namespace ubuntu
149
150#pragma GCC visibility pop
0151
=== modified file 'libubuntu-app-launch/desktop-exec.c'
--- libubuntu-app-launch/desktop-exec.c 2015-08-12 02:52:05 +0000
+++ libubuntu-app-launch/desktop-exec.c 2016-03-04 11:34:19 +0000
@@ -186,7 +186,7 @@
186 /* This string is quoted using desktop file quoting:186 /* This string is quoted using desktop file quoting:
187 http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */187 http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */
188 gchar * execline = desktop_to_exec(keyfile, app_id);188 gchar * execline = desktop_to_exec(keyfile, app_id);
189 g_return_val_if_fail(execline != NULL, 1);189 g_return_val_if_fail(execline != NULL, FALSE);
190190
191 if (is_libertine) {191 if (is_libertine) {
192 static const gchar * libertine_launch = NULL;192 static const gchar * libertine_launch = NULL;
193193
=== added file 'libubuntu-app-launch/glib-thread.cpp'
--- libubuntu-app-launch/glib-thread.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/glib-thread.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,156 @@
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
25ContextThread::ContextThread(std::function<void()> beforeLoop, std::function<void()> afterLoop)
26{
27 _cancel = std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable* cancel) {
28 if (cancel != nullptr)
29 {
30 g_cancellable_cancel(cancel);
31 g_object_unref(cancel);
32 }
33 });
34 std::promise<std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>> context_promise;
35
36 /* NOTE: We copy afterLoop but reference beforeLoop. We're blocking so we
37 know that beforeLoop will stay valid long enough, but we can't say the
38 same for afterLoop */
39 _thread = std::thread([&context_promise, &beforeLoop, afterLoop, this]() {
40 /* Build up the context and loop for the async events and a place
41 for GDBus to send its events back to */
42 auto context = std::shared_ptr<GMainContext>(
43 g_main_context_new(), [](GMainContext* context) { g_clear_pointer(&context, g_main_context_unref); });
44 auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(context.get(), FALSE),
45 [](GMainLoop* loop) { g_clear_pointer(&loop, g_main_loop_unref); });
46
47 g_main_context_push_thread_default(context.get());
48
49 beforeLoop();
50
51 /* Free's the constructor to continue */
52 auto pair = std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>(context, loop);
53 context_promise.set_value(pair);
54
55 if (!g_cancellable_is_cancelled(_cancel.get()))
56 {
57 g_main_loop_run(loop.get());
58 }
59
60 afterLoop();
61 });
62
63 /* We need to have the context and the mainloop ready before
64 other functions on this object can work properly. So we wait
65 for them and set them on this thread. */
66 auto context_future = context_promise.get_future();
67 context_future.wait();
68 auto context_value = context_future.get();
69
70 _context = context_value.first;
71 _loop = context_value.second;
72
73 if (!_context || !_loop)
74 {
75 throw std::runtime_error("Unable to create GLib Thread");
76 }
77}
78
79ContextThread::~ContextThread()
80{
81 quit();
82}
83
84void ContextThread::quit()
85{
86 g_cancellable_cancel(_cancel.get()); /* Force the cancellation on ongoing tasks */
87 if (_loop)
88 {
89 g_main_loop_quit(_loop.get()); /* Quit the loop */
90 }
91
92 /* Joining here because we want to ensure that the final afterLoop()
93 function is run before returning */
94 if (std::this_thread::get_id() != _thread.get_id())
95 {
96 if (_thread.joinable())
97 {
98 _thread.join();
99 }
100 }
101}
102
103bool ContextThread::isCancelled()
104{
105 return g_cancellable_is_cancelled(_cancel.get()) == TRUE;
106}
107
108std::shared_ptr<GCancellable> ContextThread::getCancellable()
109{
110 return _cancel;
111}
112
113void ContextThread::simpleSource(std::function<GSource*()> srcBuilder, std::function<void()> work)
114{
115 if (isCancelled())
116 {
117 throw std::runtime_error("Trying to execute work on a GLib thread that is shutting down.");
118 }
119
120 /* Copy the work so that we can reuse it */
121 /* Lifecycle is handled with the source pointer when we attach
122 it to the context. */
123 auto heapWork = new std::function<void()>(work);
124
125 auto source = std::shared_ptr<GSource>(srcBuilder(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); });
126 g_source_set_callback(source.get(),
127 [](gpointer data) {
128 auto heapWork = static_cast<std::function<void()>*>(data);
129 (*heapWork)();
130 return G_SOURCE_REMOVE;
131 },
132 heapWork,
133 [](gpointer data) {
134 auto heapWork = static_cast<std::function<void()>*>(data);
135 delete heapWork;
136 });
137
138 g_source_attach(source.get(), _context.get());
139}
140
141void ContextThread::executeOnThread(std::function<void()> work)
142{
143 simpleSource(g_idle_source_new, work);
144}
145
146void ContextThread::timeout(const std::chrono::milliseconds& length, std::function<void()> work)
147{
148 simpleSource([length]() { return g_timeout_source_new(length.count()); }, work);
149}
150
151void ContextThread::timeoutSeconds(const std::chrono::seconds& length, std::function<void()> work)
152{
153 simpleSource([length]() { return g_timeout_source_new_seconds(length.count()); }, work);
154}
155
156} // ns GLib
0157
=== added file 'libubuntu-app-launch/glib-thread.h'
--- libubuntu-app-launch/glib-thread.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/glib-thread.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,90 @@
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 =
37 []
38 {
39 },
40 std::function<void()> afterLoop =
41 []
42 {
43 });
44 ~ContextThread();
45
46 void quit();
47 bool isCancelled();
48 std::shared_ptr<GCancellable> getCancellable();
49
50 void executeOnThread(std::function<void()> work);
51 template <typename T>
52 auto executeOnThread(std::function<T()> work) -> T
53 {
54 if (std::this_thread::get_id() == _thread.get_id())
55 {
56 /* Don't block if we're on the same thread */
57 return work();
58 }
59
60 std::promise<T> promise;
61 std::function<void()> magicFunc = [&promise, &work]()
62 {
63 promise.set_value(work());
64 };
65
66 executeOnThread(magicFunc);
67
68 auto future = promise.get_future();
69 future.wait();
70 return future.get();
71 }
72
73 void timeout(const std::chrono::milliseconds& length, std::function<void()> work);
74 template <class Rep, class Period>
75 void timeout(const std::chrono::duration<Rep, Period>& length, std::function<void()> work)
76 {
77 return timeout(std::chrono::duration_cast<std::chrono::milliseconds>(length), work);
78 }
79
80 void timeoutSeconds(const std::chrono::seconds& length, std::function<void()> work);
81 template <class Rep, class Period>
82 void timeoutSeconds(const std::chrono::duration<Rep, Period>& length, std::function<void()> work)
83 {
84 return timeoutSeconds(std::chrono::duration_cast<std::chrono::seconds>(length), work);
85 }
86
87private:
88 void simpleSource(std::function<GSource*()> srcBuilder, std::function<void()> work);
89};
90}
091
=== added file 'libubuntu-app-launch/helper-impl-click.cpp'
--- libubuntu-app-launch/helper-impl-click.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/helper-impl-click.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,175 @@
1/*
2 * Copyright © 2016 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 "helper-impl-click.h"
21#include "registry-impl.h"
22
23#include "ubuntu-app-launch.h"
24
25namespace ubuntu
26{
27namespace app_launch
28{
29namespace helper_impls
30{
31
32bool Click::hasInstances()
33{
34 return _registry->impl->thread.executeOnThread<bool>([this]() {
35 auto instances = ubuntu_app_launch_list_helper_instances(_type.value().c_str(), ((std::string)_appid).c_str());
36 auto retval = (g_strv_length(instances) != 0);
37
38 g_strfreev(instances);
39
40 return retval;
41 });
42}
43
44class ClickInstance : public Helper::Instance
45{
46public: /* all one file, no one to hide from */
47 AppID _appid;
48 Helper::Type _type;
49 std::string _instanceid;
50 std::shared_ptr<Registry> _registry;
51
52 ClickInstance(const AppID& appid,
53 const Helper::Type& type,
54 const std::string& instanceid,
55 std::shared_ptr<Registry> registry)
56 : _appid(appid)
57 , _type(type)
58 , _instanceid(instanceid)
59 , _registry(registry)
60 {
61 }
62
63 bool isRunning() override
64 {
65 return _registry->impl->thread.executeOnThread<bool>([this]() {
66 bool found = false;
67
68 auto instances =
69 ubuntu_app_launch_list_helper_instances(_type.value().c_str(), ((std::string)_appid).c_str());
70 for (int i = 0; instances[i] != nullptr; i++)
71 {
72 if (_instanceid == std::string(instances[i]))
73 {
74 found = true;
75 break;
76 }
77 }
78
79 g_strfreev(instances);
80
81 return found;
82 });
83 }
84
85 void stop() override
86 {
87 _registry->impl->thread.executeOnThread<bool>([this]() {
88 return ubuntu_app_launch_stop_multiple_helper(_type.value().c_str(), ((std::string)_appid).c_str(),
89 _instanceid.c_str()) == TRUE;
90 });
91 }
92};
93
94std::vector<std::shared_ptr<Click::Instance>> Click::instances()
95{
96 return _registry->impl->thread.executeOnThread<std::vector<std::shared_ptr<Click::Instance>>>(
97 [this]() -> std::vector<std::shared_ptr<Click::Instance>> {
98 std::vector<std::shared_ptr<Click::Instance>> vect;
99 auto instances =
100 ubuntu_app_launch_list_helper_instances(_type.value().c_str(), ((std::string)_appid).c_str());
101 for (int i = 0; instances[i] != nullptr; i++)
102 {
103 auto inst = std::make_shared<ClickInstance>(_appid, _type, instances[i], _registry);
104 vect.push_back(inst);
105 }
106
107 g_strfreev(instances);
108
109 return vect;
110 });
111}
112
113std::shared_ptr<gchar*> urlsToStrv(std::vector<Helper::URL> urls)
114{
115 if (urls.size() == 0)
116 {
117 return {};
118 }
119
120 auto array = g_array_new(TRUE, FALSE, sizeof(gchar*));
121
122 for (auto url : urls)
123 {
124 auto str = g_strdup(url.value().c_str());
125 g_array_append_val(array, str);
126 }
127
128 return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev);
129}
130
131std::shared_ptr<Click::Instance> Click::launch(std::vector<Helper::URL> urls)
132{
133 auto urlstrv = urlsToStrv(urls);
134
135 return _registry->impl->thread.executeOnThread<std::shared_ptr<Click::Instance>>([this, urlstrv]() {
136 auto instanceid = ubuntu_app_launch_start_multiple_helper(_type.value().c_str(), ((std::string)_appid).c_str(),
137 urlstrv.get());
138
139 return std::make_shared<ClickInstance>(_appid, _type, instanceid, _registry);
140 });
141}
142
143std::shared_ptr<Click::Instance> Click::launch(MirPromptSession* session, std::vector<Helper::URL> urls)
144{
145 auto urlstrv = urlsToStrv(urls);
146
147 return _registry->impl->thread.executeOnThread<std::shared_ptr<Click::Instance>>([this, session, urlstrv]() {
148 auto instanceid = ubuntu_app_launch_start_session_helper(_type.value().c_str(), session,
149 ((std::string)_appid).c_str(), urlstrv.get());
150
151 return std::make_shared<ClickInstance>(_appid, _type, instanceid, _registry);
152 });
153}
154
155std::list<std::shared_ptr<Helper>> Click::running(Helper::Type type, std::shared_ptr<Registry> registry)
156{
157 return registry->impl->thread.executeOnThread<std::list<std::shared_ptr<Helper>>>([type, registry]() {
158 std::list<std::shared_ptr<Helper>> helpers;
159
160 auto appidv = ubuntu_app_launch_list_helpers(type.value().c_str());
161 for (int i = 0; appidv[i] != nullptr; i++)
162 {
163 auto helper = std::make_shared<Click>(type, AppID::parse(appidv[i]), registry);
164 helpers.push_back(helper);
165 }
166
167 g_strfreev(appidv);
168
169 return helpers;
170 });
171}
172
173}; // namespace helper_impl
174}; // namespace app_launch
175}; // namespace ubuntu
0176
=== added file 'libubuntu-app-launch/helper-impl-click.h'
--- libubuntu-app-launch/helper-impl-click.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/helper-impl-click.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,62 @@
1/*
2 * Copyright © 2016 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 <list>
21
22#include "helper.h"
23
24namespace ubuntu
25{
26namespace app_launch
27{
28namespace helper_impls
29{
30
31class Click : public Helper
32{
33public:
34 Click(const Helper::Type& type, const AppID& appid, const std::shared_ptr<Registry>& registry)
35 : _type(type)
36 , _appid(appid)
37 , _registry(registry)
38 {
39 }
40
41 AppID appId() override
42 {
43 return _appid;
44 }
45
46 bool hasInstances() override;
47 std::vector<std::shared_ptr<Helper::Instance>> instances() override;
48
49 std::shared_ptr<Helper::Instance> launch(std::vector<Helper::URL> urls = {}) override;
50 std::shared_ptr<Helper::Instance> launch(MirPromptSession* session, std::vector<Helper::URL> urls = {}) override;
51
52 static std::list<std::shared_ptr<Helper>> running(Helper::Type type, std::shared_ptr<Registry> registry);
53
54private:
55 Helper::Type _type;
56 AppID _appid;
57 std::shared_ptr<Registry> _registry;
58};
59
60}; // namespace helper_impl
61}; // namespace app_launch
62}; // namespace ubuntu
063
=== added file 'libubuntu-app-launch/helper.cpp'
--- libubuntu-app-launch/helper.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/helper.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,36 @@
1/*
2 * Copyright © 2016 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 "helper.h"
21
22#include "helper-impl-click.h"
23
24namespace ubuntu
25{
26namespace app_launch
27{
28
29std::shared_ptr<Helper> Helper::create(Type type, AppID appid, std::shared_ptr<Registry> registry)
30{
31 /* Only one type today */
32 return std::make_shared<helper_impls::Click>(type, appid, registry);
33}
34
35}; // namespace AppLaunch
36}; // namespace Ubuntu
037
=== added file 'libubuntu-app-launch/helper.h'
--- libubuntu-app-launch/helper.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/helper.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,71 @@
1/*
2 * Copyright © 2016 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 <memory>
21#include <vector>
22
23#include <mir_toolkit/mir_prompt_session.h>
24
25#include "appid.h"
26#include "type-tagger.h"
27
28#pragma once
29#pragma GCC visibility push(default)
30
31namespace ubuntu
32{
33namespace app_launch
34{
35
36class Registry;
37
38class Helper
39{
40public:
41 struct TypeTag;
42 struct URLTag;
43
44 typedef TypeTagger<TypeTag, std::string> Type;
45 typedef TypeTagger<URLTag, std::string> URL;
46
47 static std::shared_ptr<Helper> create(Type type, AppID appid, std::shared_ptr<Registry> registry);
48
49 virtual AppID appId() = 0;
50
51 class Instance
52 {
53 public:
54 /* Query lifecycle */
55 virtual bool isRunning() = 0;
56
57 /* Manage lifecycle */
58 virtual void stop() = 0;
59 };
60
61 virtual bool hasInstances() = 0;
62 virtual std::vector<std::shared_ptr<Instance>> instances() = 0;
63
64 virtual std::shared_ptr<Instance> launch(std::vector<URL> urls = {}) = 0;
65 virtual std::shared_ptr<Instance> launch(MirPromptSession* session, std::vector<URL> urls = {}) = 0;
66};
67
68}; // namespace app_launch
69}; // namespace ubuntu
70
71#pragma GCC visibility pop
072
=== modified file 'libubuntu-app-launch/libubuntu-app-launch.map'
--- libubuntu-app-launch/libubuntu-app-launch.map 2014-11-21 15:57:08 +0000
+++ libubuntu-app-launch/libubuntu-app-launch.map 2016-03-04 11:34:19 +0000
@@ -1,6 +1,23 @@
1{1{
2global:2global:
3 ubuntu_app_launch_*;3 ubuntu_app_launch_*;
4 extern "C++" {
5 ubuntu::app_launch::Registry::[!I]*;
6 typeinfo?for?ubuntu::app_launch::Registry;
7 typeinfo?name?for?ubuntu::app_launch::Registry;
8 ubuntu::app_launch::Application::*;
9 typeinfo?for?ubuntu::app_launch::Application;
10 typeinfo?name?for?ubuntu::app_launch::Application;
11 ubuntu::app_launch::AppID::*;
12 typeinfo?for?ubuntu::app_launch::AppID;
13 typeinfo?name?for?ubuntu::app_launch::AppID;
14 ubuntu::app_launch::Helper::*;
15 typeinfo?for?ubuntu::app_launch::Helper;
16 typeinfo?name?for?ubuntu::app_launch::Helper;
17 };
4local:18local:
5 *;19 *;
20 extern "C++" {
21 *;
22 };
6};23};
724
=== added file 'libubuntu-app-launch/registry-impl.cpp'
--- libubuntu-app-launch/registry-impl.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/registry-impl.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,185 @@
1/*
2 * Copyright © 2016 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 "registry-impl.h"
21
22namespace ubuntu
23{
24namespace app_launch
25{
26
27Registry::Impl::Impl(Registry* registry)
28 : thread([]() {},
29 [this]() {
30 _clickUser.reset();
31 _clickDB.reset();
32
33 g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr);
34 _dbus.reset();
35 })
36 , _registry(registry)
37// _manager(nullptr)
38{
39 auto cancel = thread.getCancellable();
40 _dbus = thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() {
41 return std::shared_ptr<GDBusConnection>(g_bus_get_sync(G_BUS_TYPE_SESSION, cancel.get(), nullptr),
42 [](GDBusConnection* bus) { g_clear_object(&bus); });
43 });
44}
45
46void Registry::Impl::initClick()
47{
48 if (_clickDB && _clickUser)
49 {
50 return;
51 }
52
53 auto init = thread.executeOnThread<bool>([this]() {
54 GError* error = nullptr;
55
56 if (!_clickDB)
57 {
58 _clickDB = std::shared_ptr<ClickDB>(click_db_new(), [](ClickDB* db) { g_clear_object(&db); });
59 /* If TEST_CLICK_DB is unset, this reads the system database. */
60 click_db_read(_clickDB.get(), g_getenv("TEST_CLICK_DB"), &error);
61
62 if (error != nullptr)
63 {
64 auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); });
65 throw std::runtime_error(perror->message);
66 }
67 }
68
69 if (!_clickUser)
70 {
71 _clickUser =
72 std::shared_ptr<ClickUser>(click_user_new_for_user(_clickDB.get(), g_getenv("TEST_CLICK_USER"), &error),
73 [](ClickUser* user) { g_clear_object(&user); });
74
75 if (error != nullptr)
76 {
77 auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); });
78 throw std::runtime_error(perror->message);
79 }
80 }
81
82 return true;
83 });
84
85 if (!init)
86 {
87 throw std::runtime_error("Unable to initialize the Click Database");
88 }
89}
90
91std::shared_ptr<JsonObject> Registry::Impl::getClickManifest(const std::string& package)
92{
93 initClick();
94
95 auto retval = thread.executeOnThread<std::shared_ptr<JsonObject>>([this, package]() {
96 GError* error = nullptr;
97 auto retval = std::shared_ptr<JsonObject>(click_user_get_manifest(_clickUser.get(), package.c_str(), &error),
98 [](JsonObject* obj) {
99 if (obj != nullptr)
100 {
101 json_object_unref(obj);
102 }
103 });
104
105 if (error != nullptr)
106 {
107 auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); });
108 throw std::runtime_error(perror->message);
109 }
110
111 return retval;
112 });
113
114 if (!retval)
115 throw std::runtime_error("Unable to get Click manifest for package: " + package);
116
117 return retval;
118}
119
120std::list<AppID::Package> Registry::Impl::getClickPackages()
121{
122 initClick();
123
124 return thread.executeOnThread<std::list<AppID::Package>>([this]() {
125 GError* error = nullptr;
126 GList* pkgs = click_db_get_packages(_clickDB.get(), FALSE, &error);
127
128 if (error != nullptr)
129 {
130 auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); });
131 throw std::runtime_error(perror->message);
132 }
133
134 std::list<AppID::Package> list;
135 for (GList* item = pkgs; item != NULL; item = g_list_next(item))
136 {
137 list.emplace_back(AppID::Package::from_raw((gchar*)item->data));
138 }
139
140 g_list_free_full(pkgs, g_free);
141 return list;
142 });
143}
144
145std::string Registry::Impl::getClickDir(const std::string& package)
146{
147 initClick();
148
149 return thread.executeOnThread<std::string>([this, package]() {
150 GError* error = nullptr;
151 auto dir = click_user_get_path(_clickUser.get(), package.c_str(), &error);
152
153 if (error != nullptr)
154 {
155 auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); });
156 throw std::runtime_error(perror->message);
157 }
158
159 std::string cppdir(dir);
160 g_free(dir);
161 return cppdir;
162 });
163}
164
165#if 0
166void
167Registry::Impl::setManager (Registry::Manager* manager)
168{
169 if (_manager != nullptr)
170 {
171 throw std::runtime_error("Already have a manager and trying to set another");
172 }
173
174 _manager = manager;
175}
176
177void
178Registry::Impl::clearManager ()
179{
180 _manager = nullptr;
181}
182#endif
183
184}; // namespace app_launch
185}; // namespace ubuntu
0186
=== added file 'libubuntu-app-launch/registry-impl.h'
--- libubuntu-app-launch/registry-impl.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/registry-impl.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,69 @@
1/*
2 * Copyright © 2016 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 "registry.h"
21#include "glib-thread.h"
22
23#include <json-glib/json-glib.h>
24#include <click.h>
25#include <gio/gio.h>
26
27#pragma once
28
29namespace ubuntu
30{
31namespace app_launch
32{
33
34class Registry::Impl
35{
36public:
37 Impl(Registry* registry);
38 virtual ~Impl()
39 {
40 thread.quit();
41 }
42
43 std::shared_ptr<JsonObject> getClickManifest(const std::string& package);
44 std::list<AppID::Package> getClickPackages();
45 std::string getClickDir(const std::string& package);
46
47#if 0
48 void setManager (Registry::Manager* manager);
49 void clearManager ();
50#endif
51
52 GLib::ContextThread thread;
53
54private:
55 Registry* _registry;
56#if 0
57 Registry::Manager* _manager;
58#endif
59
60 std::shared_ptr<ClickDB> _clickDB;
61 std::shared_ptr<ClickUser> _clickUser;
62
63 std::shared_ptr<GDBusConnection> _dbus;
64
65 void initClick();
66};
67
68}; // namespace app_launch
69}; // namespace ubuntu
070
=== added file 'libubuntu-app-launch/registry.cpp'
--- libubuntu-app-launch/registry.cpp 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/registry.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,99 @@
1/*
2 * Copyright © 2016 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 "registry.h"
21#include "registry-impl.h"
22
23#include "application-impl-click.h"
24#include "application-impl-legacy.h"
25#include "application-impl-libertine.h"
26
27#include "helper-impl-click.h"
28
29namespace ubuntu
30{
31namespace app_launch
32{
33
34Registry::Registry()
35{
36 impl = std::unique_ptr<Impl>(new Impl(this));
37}
38
39Registry::~Registry()
40{
41}
42
43std::list<std::shared_ptr<Application>> Registry::runningApps(std::shared_ptr<Registry> connection)
44{
45 return connection->impl->thread.executeOnThread<std::list<std::shared_ptr<Application>>>(
46 [connection]() -> std::list<std::shared_ptr<Application>> {
47 auto strv = ubuntu_app_launch_list_running_apps();
48 if (strv == nullptr)
49 {
50 return {};
51 }
52
53 std::list<std::shared_ptr<Application>> list;
54 for (int i = 0; strv[i] != nullptr; i++)
55 {
56 auto appid = AppID::find(strv[i]);
57 auto app = Application::create(appid, connection);
58 list.push_back(app);
59 }
60
61 g_strfreev(strv);
62
63 return list;
64 });
65}
66
67std::list<std::shared_ptr<Application>> Registry::installedApps(std::shared_ptr<Registry> connection)
68{
69 std::list<std::shared_ptr<Application>> list;
70
71 list.splice(list.begin(), app_impls::Click::list(connection));
72 list.splice(list.begin(), app_impls::Legacy::list(connection));
73 list.splice(list.begin(), app_impls::Libertine::list(connection));
74
75 return list;
76}
77
78std::list<std::shared_ptr<Helper>> Registry::runningHelpers(Helper::Type type, std::shared_ptr<Registry> connection)
79{
80 std::list<std::shared_ptr<Helper>> list;
81
82 list.splice(list.begin(), helper_impls::Click::running(type, connection));
83
84 return list;
85}
86
87std::shared_ptr<Registry> defaultRegistry;
88std::shared_ptr<Registry> Registry::getDefault()
89{
90 if (!defaultRegistry)
91 {
92 defaultRegistry = std::make_shared<Registry>();
93 }
94
95 return defaultRegistry;
96}
97
98}; // namespace app_launch
99}; // namespace ubuntu
0100
=== added file 'libubuntu-app-launch/registry.h'
--- libubuntu-app-launch/registry.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/registry.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,91 @@
1/*
2 * Copyright © 2016 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 <core/signal.h>
21#include <functional>
22#include <list>
23#include <memory>
24
25#include "application.h"
26#include "helper.h"
27
28#pragma once
29#pragma GCC visibility push(default)
30
31namespace ubuntu
32{
33namespace app_launch
34{
35
36class Registry
37{
38public:
39 enum class FailureType
40 {
41 CRASH,
42 START_FAILURE,
43 };
44
45 Registry();
46 virtual ~Registry();
47
48 /* Lots of application lists */
49 static std::list<std::shared_ptr<Application>> runningApps(std::shared_ptr<Registry> registry = getDefault());
50 static std::list<std::shared_ptr<Application>> installedApps(std::shared_ptr<Registry> registry = getDefault());
51
52#if 0 /* TODO -- In next MR */
53 /* Signals to discover what is happening to apps */
54 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStarted;
55 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStopped;
56 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> appFailed;
57 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appPaused;
58 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appResumed;
59
60 /* The Application Manager, almost always if you're not Unity8, don't
61 use this API. Testing is a special case. */
62 class Manager
63 {
64 virtual bool focusRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0;
65 virtual bool startingRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0;
66
67 protected:
68 Manager() = default;
69 };
70
71 void setManager (Manager* manager);
72 void clearManager ();
73#endif
74
75 /* Helper Lists */
76 static std::list<std::shared_ptr<Helper>> runningHelpers(Helper::Type type,
77 std::shared_ptr<Registry> registry = getDefault());
78
79 /* Default Junk */
80 static std::shared_ptr<Registry> getDefault();
81 static void clearDefault();
82
83 /* Hide our implementation */
84 class Impl;
85 std::unique_ptr<Impl> impl;
86};
87
88}; // namespace app_launch
89}; // namespace ubuntu
90
91#pragma GCC visibility pop
092
=== added file 'libubuntu-app-launch/type-tagger.h'
--- libubuntu-app-launch/type-tagger.h 1970-01-01 00:00:00 +0000
+++ libubuntu-app-launch/type-tagger.h 2016-03-04 11:34:19 +0000
@@ -0,0 +1,37 @@
1#pragma once
2
3namespace ubuntu
4{
5namespace app_launch
6{
7
8template <typename Tag, typename T>
9class TypeTagger
10{
11public:
12 static TypeTagger<Tag, T> from_raw(const T& value)
13 {
14 return TypeTagger<Tag, T>(value);
15 }
16 const T& value() const
17 {
18 return _value;
19 }
20 operator T() const
21 {
22 return _value;
23 }
24 ~TypeTagger()
25 {
26 }
27
28private:
29 TypeTagger(const T& value)
30 : _value(value)
31 {
32 }
33 T _value;
34};
35
36}; // namespace app_launch
37}; // namespace ubuntu
038
=== modified file 'libubuntu-app-launch/ubuntu-app-launch.c'
--- libubuntu-app-launch/ubuntu-app-launch.c 2015-08-17 21:38:29 +0000
+++ libubuntu-app-launch/ubuntu-app-launch.c 2016-03-04 11:34:19 +0000
@@ -38,7 +38,6 @@
3838
39static void apps_for_job (GDBusConnection * con, const gchar * name, GArray * apps, gboolean truncate_legacy);39static void apps_for_job (GDBusConnection * con, const gchar * name, GArray * apps, gboolean truncate_legacy);
40static void free_helper (gpointer value);40static void free_helper (gpointer value);
41static GList * pids_for_appid (const gchar * appid);
42int kill (pid_t pid, int signal);41int kill (pid_t pid, int signal);
43static gchar * escape_dbus_string (const gchar * input);42static gchar * escape_dbus_string (const gchar * input);
4443
@@ -588,7 +587,7 @@
588587
589 do {588 do {
590 hash_table_size = g_hash_table_size(pidssignaled);589 hash_table_size = g_hash_table_size(pidssignaled);
591 GList * pidlist = pids_for_appid(appid);590 GList * pidlist = ubuntu_app_launch_get_pids(appid);
592 GList * iter;591 GList * iter;
593592
594 if (pidlist == NULL) {593 if (pidlist == NULL) {
@@ -1506,9 +1505,10 @@
1506/* Get the PIDs for an AppID. If it's click or legacy single instance that's1505/* Get the PIDs for an AppID. If it's click or legacy single instance that's
1507 a simple call to the helper. But if it's not, we have to make a call for1506 a simple call to the helper. But if it's not, we have to make a call for
1508 each instance of the app that we have running. */1507 each instance of the app that we have running. */
1509static GList *1508GList *
1510pids_for_appid (const gchar * appid)1509ubuntu_app_launch_get_pids (const gchar * appid)
1511{1510{
1511 g_return_val_if_fail(appid != NULL, NULL);
1512 ual_tracepoint(pids_list_start, appid);1512 ual_tracepoint(pids_list_start, appid);
15131513
1514 GDBusConnection * cgmanager = cgroup_manager_connection();1514 GDBusConnection * cgmanager = cgroup_manager_connection();
@@ -1522,7 +1522,7 @@
15221522
1523 ual_tracepoint(pids_list_finished, appid, g_list_length(pids));1523 ual_tracepoint(pids_list_finished, appid, g_list_length(pids));
1524 return pids;1524 return pids;
1525 } else if (!is_libertine(appid) && legacy_single_instance(appid)) {1525 } else if (is_libertine(appid) || legacy_single_instance(appid)) {
1526 gchar * jobname = g_strdup_printf("%s-", appid);1526 gchar * jobname = g_strdup_printf("%s-", appid);
1527 GList * pids = pids_from_cgroup(cgmanager, "application-legacy", jobname);1527 GList * pids = pids_from_cgroup(cgmanager, "application-legacy", jobname);
1528 g_free(jobname);1528 g_free(jobname);
@@ -1572,7 +1572,7 @@
1572 return FALSE;1572 return FALSE;
1573 }1573 }
15741574
1575 GList * pidlist = pids_for_appid(appid);1575 GList * pidlist = ubuntu_app_launch_get_pids(appid);
1576 GList * head;1576 GList * head;
15771577
1578 for (head = pidlist; head != NULL; head = g_list_next(head)) {1578 for (head = pidlist; head != NULL; head = g_list_next(head)) {
@@ -2403,7 +2403,7 @@
2403 const gchar * job_name = g_getenv("UPSTART_JOB");2403 const gchar * job_name = g_getenv("UPSTART_JOB");
2404 const gchar * instance_name = g_getenv("UPSTART_INSTANCE");2404 const gchar * instance_name = g_getenv("UPSTART_INSTANCE");
2405 const gchar * demangler = g_getenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME");2405 const gchar * demangler = g_getenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
2406 g_return_if_fail(job_name != NULL);2406 g_return_val_if_fail(job_name != NULL, FALSE);
24072407
2408 GError * error = NULL;2408 GError * error = NULL;
2409 GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);2409 GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
24102410
=== modified file 'libubuntu-app-launch/ubuntu-app-launch.h'
--- libubuntu-app-launch/ubuntu-app-launch.h 2015-08-10 14:19:05 +0000
+++ libubuntu-app-launch/ubuntu-app-launch.h 2016-03-04 11:34:19 +0000
@@ -388,6 +388,18 @@
388GPid ubuntu_app_launch_get_primary_pid (const gchar * appid);388GPid ubuntu_app_launch_get_primary_pid (const gchar * appid);
389389
390/**390/**
391 * ubuntu_app_launch_get_pids:
392 * @appid: ID of the application to look for
393 *
394 * Checks to see if an application is running and returns
395 * the PIDs associated with it.
396 *
397 * Return Value: (transfer full) (element-type GLib.Pid): A list
398 * of PIDs associated with @appid, empty if not running.
399 */
400GList * ubuntu_app_launch_get_pids (const gchar * appid);
401
402/**
391 * ubuntu_app_launch_pid_in_app_id:403 * ubuntu_app_launch_pid_in_app_id:
392 * @pid: Process ID to check on404 * @pid: Process ID to check on
393 * @appid: ID of the application to look in405 * @appid: ID of the application to look in
394406
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2015-06-26 17:26:50 +0000
+++ tests/CMakeLists.txt 2016-03-04 11:34:19 +0000
@@ -42,6 +42,11 @@
42 mir-mock.cpp)42 mir-mock.cpp)
43target_link_libraries (libual-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)43target_link_libraries (libual-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
4444
45add_executable (libual-cpp-test
46 libual-cpp-test.cc
47 mir-mock.cpp)
48target_link_libraries (libual-cpp-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
49
45add_executable (data-spew50add_executable (data-spew
46 data-spew.c)51 data-spew.c)
47target_link_libraries (data-spew ${GLIB2_LIBRARIES})52target_link_libraries (data-spew ${GLIB2_LIBRARIES})
@@ -50,6 +55,16 @@
50 socket-tool.c)55 socket-tool.c)
5156
52add_test (NAME libual-test COMMAND libual-test)57add_test (NAME libual-test COMMAND libual-test)
58add_test (NAME libual-cpp-test COMMAND libual-cpp-test)
59
60# Application Info Desktop
61
62add_executable (application-info-desktop-test
63 application-info-desktop.cpp
64 ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/application-info-desktop.cpp)
65target_link_libraries (application-info-desktop-test gtest ${GTEST_LIBS} ubuntu-launcher)
66
67add_test (NAME application-info-desktop-test COMMAND application-info-desktop-test)
5368
54# Failure Test69# Failure Test
5570
@@ -101,3 +116,8 @@
101configure_file ("xmir-helper-test.in" "${CMAKE_CURRENT_BINARY_DIR}/xmir-helper-test" @ONLY)116configure_file ("xmir-helper-test.in" "${CMAKE_CURRENT_BINARY_DIR}/xmir-helper-test" @ONLY)
102add_test (xmir-helper-test xmir-helper-test)117add_test (xmir-helper-test xmir-helper-test)
103118
119# Formatted code
120
121add_custom_target(format-tests
122 COMMAND clang-format -i -style=file libual-cpp-test.cc
123)
104124
=== added file 'tests/application-info-desktop.cpp'
--- tests/application-info-desktop.cpp 1970-01-01 00:00:00 +0000
+++ tests/application-info-desktop.cpp 2016-03-04 11:34:19 +0000
@@ -0,0 +1,148 @@
1/*
2 * Copyright © 2016 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 "application-info-desktop.h"
21
22#include <gtest/gtest.h>
23
24class ApplicationInfoDesktop : public ::testing::Test
25{
26 virtual void SetUp()
27 {
28 }
29
30 virtual void TearDown()
31 {
32 }
33};
34
35#define DESKTOP "Desktop Entry"
36
37TEST_F(ApplicationInfoDesktop, DefaultState)
38{
39 auto keyfile = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free);
40 g_key_file_set_string(keyfile.get(), DESKTOP, "Name", "Foo App");
41 g_key_file_set_string(keyfile.get(), DESKTOP, "Exec", "foo");
42 g_key_file_set_string(keyfile.get(), DESKTOP, "Icon", "foo.png");
43
44 auto appinfo = ubuntu::app_launch::app_info::Desktop(keyfile, "/");
45
46 EXPECT_EQ("Foo App", appinfo.name().value());
47 EXPECT_EQ("", appinfo.description().value());
48 EXPECT_EQ("/foo.png", appinfo.iconPath().value());
49
50 EXPECT_EQ("", appinfo.splash().title.value());
51 EXPECT_EQ("", appinfo.splash().image.value());
52 EXPECT_EQ("", appinfo.splash().backgroundColor.value());
53 EXPECT_EQ("", appinfo.splash().headerColor.value());
54 EXPECT_EQ("", appinfo.splash().footerColor.value());
55 EXPECT_FALSE( appinfo.splash().showHeader.value());
56
57 EXPECT_TRUE(appinfo.supportedOrientations().portrait);
58 EXPECT_TRUE(appinfo.supportedOrientations().landscape);
59 EXPECT_TRUE(appinfo.supportedOrientations().invertedPortrait);
60 EXPECT_TRUE(appinfo.supportedOrientations().invertedLandscape);
61
62 EXPECT_FALSE(appinfo.rotatesWindowContents().value());
63
64 EXPECT_FALSE(appinfo.supportsUbuntuLifecycle().value());
65}
66
67TEST_F(ApplicationInfoDesktop, KeyfileErrors)
68{
69 EXPECT_THROW(ubuntu::app_launch::app_info::Desktop({}, "/"), std::runtime_error);
70
71 auto noname = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free);
72 g_key_file_set_string(noname.get(), DESKTOP, "Comment", "This is a comment");
73 g_key_file_set_string(noname.get(), DESKTOP, "Exec", "foo");
74 g_key_file_set_string(noname.get(), DESKTOP, "Icon", "foo.png");
75
76 EXPECT_THROW(ubuntu::app_launch::app_info::Desktop(noname, "/"), std::runtime_error);
77
78 auto noicon = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free);
79 g_key_file_set_string(noicon.get(), DESKTOP, "Name", "Foo App");
80 g_key_file_set_string(noicon.get(), DESKTOP, "Comment", "This is a comment");
81 g_key_file_set_string(noicon.get(), DESKTOP, "Exec", "foo");
82
83 EXPECT_THROW(ubuntu::app_launch::app_info::Desktop(noicon, "/"), std::runtime_error);
84}
85
86TEST_F(ApplicationInfoDesktop, Orientations)
87{
88 ubuntu::app_launch::Application::Info::Orientations defaultOrientations =
89 {
90portrait:
91 true,
92landscape:
93 true,
94invertedPortrait:
95 true,
96invertedLandscape:
97 true
98 };
99
100 auto keyfile = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free);
101 g_key_file_set_string(keyfile.get(), DESKTOP, "Name", "Foo App");
102 g_key_file_set_string(keyfile.get(), DESKTOP, "Exec", "foo");
103 g_key_file_set_string(keyfile.get(), DESKTOP, "Icon", "foo.png");
104
105 EXPECT_EQ(defaultOrientations, ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
106
107 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "this should not parse");
108 EXPECT_EQ(defaultOrientations, ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
109
110 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "this;should;not;parse;");
111 EXPECT_EQ(defaultOrientations, ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
112
113 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "portrait;");
114 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: false, invertedPortrait: false, invertedLandscape: false}),
115 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
116
117 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "landscape;portrait;");
118 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: false, invertedLandscape: false}),
119 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
120
121 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "landscape ; portrait; invertedPortrait");
122 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: true, invertedLandscape: false}),
123 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
124
125 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "portrait;landscape;");
126 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: false, invertedLandscape: false}),
127 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
128
129 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "portrait;landscape;invertedportrait;invertedlandscape;");
130 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: true, invertedLandscape: true}),
131 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
132
133 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "PORTRAIT;");
134 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: false, invertedPortrait: false, invertedLandscape: false}),
135 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
136
137 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "pOrTraIt;lANDscApE;inVErtEDpORtrAit;iNVErtEDLAnDsCapE;");
138 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: true, invertedLandscape: true}),
139 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
140
141 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "primary;");
142 EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: false, landscape: false, invertedPortrait: false, invertedLandscape: false}),
143 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
144
145 g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "foobar;primary;");
146 EXPECT_EQ(defaultOrientations,
147 ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations());
148}
0149
=== modified file 'tests/click-app-dir/application.desktop'
--- tests/click-app-dir/application.desktop 2013-09-24 03:39:27 +0000
+++ tests/click-app-dir/application.desktop 2016-03-04 11:34:19 +0000
@@ -1,4 +1,6 @@
1[Desktop Entry]1[Desktop Entry]
2Version=1.0
2Name=Application3Name=Application
3Type=Application4Type=Application
4Exec=foo5Exec=grep
6Icon=foo
57
=== modified file 'tests/exec-util-test.cc'
--- tests/exec-util-test.cc 2015-08-11 19:11:15 +0000
+++ tests/exec-util-test.cc 2016-03-04 11:34:19 +0000
@@ -172,7 +172,7 @@
172 {"APP_DIR", [](const gchar * value) {172 {"APP_DIR", [](const gchar * value) {
173 EXPECT_STREQ(APP_DIR, value); }},173 EXPECT_STREQ(APP_DIR, value); }},
174 {"APP_EXEC", [](const gchar * value) {174 {"APP_EXEC", [](const gchar * value) {
175 EXPECT_STREQ("foo", value); }},175 EXPECT_STREQ("grep", value); }},
176 {"APP_ID", [](const gchar * value) {176 {"APP_ID", [](const gchar * value) {
177 EXPECT_STREQ("com.test.good_application_1.2.3", value); }},177 EXPECT_STREQ("com.test.good_application_1.2.3", value); }},
178 {"APP_LAUNCHER_PID", [](const gchar * value) {178 {"APP_LAUNCHER_PID", [](const gchar * value) {
179179
=== modified file 'tests/helper-handshake-test.cc'
--- tests/helper-handshake-test.cc 2014-04-30 16:18:29 +0000
+++ tests/helper-handshake-test.cc 2016-03-04 11:34:19 +0000
@@ -103,6 +103,7 @@
103{103{
104 bool * reached = static_cast<bool *>(user_data);104 bool * reached = static_cast<bool *>(user_data);
105 *reached = true;105 *reached = true;
106 return true;
106}107}
107108
108TEST_F(HelperHandshakeTest, HandshakeTimeout)109TEST_F(HelperHandshakeTest, HandshakeTimeout)
@@ -114,6 +115,8 @@
114115
115 starting_handshake_wait(handshake);116 starting_handshake_wait(handshake);
116117
118 g_source_remove(outertimeout);
119
117 ASSERT_FALSE(timeout_reached);120 ASSERT_FALSE(timeout_reached);
118121
119 return;122 return;
120123
=== modified file 'tests/helper-test.cc'
--- tests/helper-test.cc 2014-11-11 22:05:30 +0000
+++ tests/helper-test.cc 2016-03-04 11:34:19 +0000
@@ -297,8 +297,6 @@
297 ASSERT_NE(calls, nullptr);297 ASSERT_NE(calls, nullptr);
298 ASSERT_STREQ("SetEnvList", calls[0].name);298 ASSERT_STREQ("SetEnvList", calls[0].name);
299299
300 unsigned int i;
301
302 bool got_app_isolation = false;300 bool got_app_isolation = false;
303 bool got_cache_home = false;301 bool got_cache_home = false;
304 bool got_config_home = false;302 bool got_config_home = false;
305303
=== modified file 'tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop'
--- tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop 2015-08-06 21:38:31 +0000
+++ tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop 2016-03-04 11:34:19 +0000
@@ -2,3 +2,4 @@
2Name=Test2Name=Test
3Type=Application3Type=Application
4Exec=test4Exec=test
5Icon=test
56
=== added file 'tests/libual-cpp-test.cc'
--- tests/libual-cpp-test.cc 1970-01-01 00:00:00 +0000
+++ tests/libual-cpp-test.cc 2016-03-04 11:34:19 +0000
@@ -0,0 +1,1599 @@
1/*
2 * Copyright 2013 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 <future>
21#include <thread>
22
23#include <gtest/gtest.h>
24#include <gio/gio.h>
25#include <zeitgeist.h>
26#include "mir-mock.h"
27
28#include "registry.h"
29#include "application.h"
30#include "helper.h"
31
32extern "C" {
33#include "ubuntu-app-launch.h"
34#include "libdbustest/dbus-test.h"
35#include <fcntl.h>
36}
37
38class LibUAL : public ::testing::Test
39{
40protected:
41 DbusTestService* service = NULL;
42 DbusTestDbusMock* mock = NULL;
43 DbusTestDbusMock* cgmock = NULL;
44 GDBusConnection* bus = NULL;
45 std::string last_focus_appid;
46 std::string last_resume_appid;
47 guint resume_timeout = 0;
48 std::shared_ptr<ubuntu::app_launch::Registry> registry;
49
50private:
51 static void focus_cb(const gchar* appid, gpointer user_data)
52 {
53 g_debug("Focus Callback: %s", appid);
54 LibUAL* _this = static_cast<LibUAL*>(user_data);
55 _this->last_focus_appid = appid;
56 }
57
58 static void resume_cb(const gchar* appid, gpointer user_data)
59 {
60 g_debug("Resume Callback: %s", appid);
61 LibUAL* _this = static_cast<LibUAL*>(user_data);
62 _this->last_resume_appid = appid;
63
64 if (_this->resume_timeout > 0)
65 {
66 _this->pause(_this->resume_timeout);
67 }
68 }
69
70protected:
71 /* Useful debugging stuff, but not on by default. You really want to
72 not get all this noise typically */
73 void debugConnection()
74 {
75 if (true)
76 {
77 return;
78 }
79
80 DbusTestBustle* bustle = dbus_test_bustle_new("test.bustle");
81 dbus_test_service_add_task(service, DBUS_TEST_TASK(bustle));
82 g_object_unref(bustle);
83
84 DbusTestProcess* monitor = dbus_test_process_new("dbus-monitor");
85 dbus_test_service_add_task(service, DBUS_TEST_TASK(monitor));
86 g_object_unref(monitor);
87 }
88
89 virtual void SetUp()
90 {
91 /* Click DB test mode */
92 g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
93 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
94
95 gchar* linkfarmpath = g_build_filename(CMAKE_SOURCE_DIR, "link-farm", NULL);
96 g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", linkfarmpath, TRUE);
97 g_free(linkfarmpath);
98
99 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
100 g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE);
101 g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE);
102
103 service = dbus_test_service_new(NULL);
104
105 debugConnection();
106
107 mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");
108
109 DbusTestDbusMockObject* obj =
110 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
111
112 dbus_test_dbus_mock_object_add_method(mock, obj, "EmitEvent", G_VARIANT_TYPE("(sasb)"), NULL, "", NULL);
113
114 dbus_test_dbus_mock_object_add_method(mock, obj, "GetJobByName", G_VARIANT_TYPE("s"), G_VARIANT_TYPE("o"),
115 "if args[0] == 'application-click':\n"
116 " ret = dbus.ObjectPath('/com/test/application_click')\n"
117 "elif args[0] == 'application-legacy':\n"
118 " ret = dbus.ObjectPath('/com/test/application_legacy')\n"
119 "elif args[0] == 'untrusted-helper':\n"
120 " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",
121 NULL);
122
123 dbus_test_dbus_mock_object_add_method(mock, obj, "SetEnv", G_VARIANT_TYPE("(assb)"), NULL, "", NULL);
124
125 /* Click App */
126 DbusTestDbusMockObject* jobobj =
127 dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
128
129 dbus_test_dbus_mock_object_add_method(
130 mock, jobobj, "Start", G_VARIANT_TYPE("(asb)"), NULL,
131 "if args[0][0] == 'APP_ID=com.test.good_application_1.2.3':"
132 " raise dbus.exceptions.DBusException('Foo running', name='com.ubuntu.Upstart0_6.Error.AlreadyStarted')",
133 NULL);
134
135 dbus_test_dbus_mock_object_add_method(mock, jobobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
136
137 dbus_test_dbus_mock_object_add_method(mock, jobobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"),
138 "ret = [ dbus.ObjectPath('/com/test/app_instance') ]", NULL);
139
140 DbusTestDbusMockObject* instobj =
141 dbus_test_dbus_mock_get_object(mock, "/com/test/app_instance", "com.ubuntu.Upstart0_6.Instance", NULL);
142 dbus_test_dbus_mock_object_add_property(mock, instobj, "name", G_VARIANT_TYPE_STRING,
143 g_variant_new_string("com.test.good_application_1.2.3"), NULL);
144 gchar* process_var = g_strdup_printf("[('main', %d)]", getpid());
145 dbus_test_dbus_mock_object_add_property(mock, instobj, "processes", G_VARIANT_TYPE("a(si)"),
146 g_variant_new_parsed(process_var), NULL);
147 g_free(process_var);
148
149 /* Legacy App */
150 DbusTestDbusMockObject* ljobobj =
151 dbus_test_dbus_mock_get_object(mock, "/com/test/application_legacy", "com.ubuntu.Upstart0_6.Job", NULL);
152
153 dbus_test_dbus_mock_object_add_method(mock, ljobobj, "Start", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
154
155 dbus_test_dbus_mock_object_add_method(mock, ljobobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
156
157 dbus_test_dbus_mock_object_add_method(mock, ljobobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"),
158 "ret = [ dbus.ObjectPath('/com/test/legacy_app_instance') ]", NULL);
159
160 DbusTestDbusMockObject* linstobj = dbus_test_dbus_mock_get_object(mock, "/com/test/legacy_app_instance",
161 "com.ubuntu.Upstart0_6.Instance", NULL);
162 dbus_test_dbus_mock_object_add_property(mock, linstobj, "name", G_VARIANT_TYPE_STRING,
163 g_variant_new_string("multiple-2342345"), NULL);
164 dbus_test_dbus_mock_object_add_property(mock, linstobj, "processes", G_VARIANT_TYPE("a(si)"),
165 g_variant_new_parsed("[('main', 5678)]"), NULL);
166
167 /* Untrusted Helper */
168 DbusTestDbusMockObject* uhelperobj =
169 dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
170
171 dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "Start", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
172
173 dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
174
175 dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"),
176 "ret = [ dbus.ObjectPath('/com/test/untrusted/helper/instance'), "
177 "dbus.ObjectPath('/com/test/untrusted/helper/multi_instance') ]",
178 NULL);
179
180 DbusTestDbusMockObject* uhelperinstance = dbus_test_dbus_mock_get_object(
181 mock, "/com/test/untrusted/helper/instance", "com.ubuntu.Upstart0_6.Instance", NULL);
182 dbus_test_dbus_mock_object_add_property(mock, uhelperinstance, "name", G_VARIANT_TYPE_STRING,
183 g_variant_new_string("untrusted-type::com.foo_bar_43.23.12"), NULL);
184
185 DbusTestDbusMockObject* unhelpermulti = dbus_test_dbus_mock_get_object(
186 mock, "/com/test/untrusted/helper/multi_instance", "com.ubuntu.Upstart0_6.Instance", NULL);
187 dbus_test_dbus_mock_object_add_property(
188 mock, unhelpermulti, "name", G_VARIANT_TYPE_STRING,
189 g_variant_new_string("untrusted-type:24034582324132:com.bar_foo_8432.13.1"), NULL);
190
191 /* Create the cgroup manager mock */
192 cgmock = dbus_test_dbus_mock_new("org.test.cgmock");
193 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME", "org.test.cgmock", TRUE);
194
195 DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock, "/org/linuxcontainers/cgmanager",
196 "org.linuxcontainers.cgmanager0_0", NULL);
197 dbus_test_dbus_mock_object_add_method(cgmock, cgobject, "GetTasksRecursive", G_VARIANT_TYPE("(ss)"),
198 G_VARIANT_TYPE("ai"), "ret = [100, 200, 300]", NULL);
199
200 /* Put it together */
201 dbus_test_service_add_task(service, DBUS_TEST_TASK(mock));
202 dbus_test_service_add_task(service, DBUS_TEST_TASK(cgmock));
203 dbus_test_service_start_tasks(service);
204
205 bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
206 g_dbus_connection_set_exit_on_close(bus, FALSE);
207 g_object_add_weak_pointer(G_OBJECT(bus), (gpointer*)&bus);
208
209 /* Make sure we pretend the CG manager is just on our bus */
210 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);
211
212 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this));
213 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this));
214
215 registry = std::make_shared<ubuntu::app_launch::Registry>();
216 }
217
218 virtual void TearDown()
219 {
220 registry.reset();
221
222 ubuntu_app_launch_observer_delete_app_focus(focus_cb, this);
223 ubuntu_app_launch_observer_delete_app_resume(resume_cb, this);
224
225 g_clear_object(&mock);
226 g_clear_object(&cgmock);
227 g_clear_object(&service);
228
229 g_object_unref(bus);
230
231 unsigned int cleartry = 0;
232 while (bus != NULL && cleartry < 100)
233 {
234 pause(100);
235 cleartry++;
236 }
237 ASSERT_EQ(nullptr, bus);
238 }
239
240 GVariant* find_env(GVariant* env_array, const gchar* var)
241 {
242 unsigned int i;
243 GVariant* retval = nullptr;
244
245 for (i = 0; i < g_variant_n_children(env_array); i++)
246 {
247 GVariant* child = g_variant_get_child_value(env_array, i);
248 const gchar* envvar = g_variant_get_string(child, nullptr);
249
250 if (g_str_has_prefix(envvar, var))
251 {
252 if (retval != nullptr)
253 {
254 g_warning("Found the env var more than once!");
255 g_variant_unref(retval);
256 return nullptr;
257 }
258
259 retval = child;
260 }
261 else
262 {
263 g_variant_unref(child);
264 }
265 }
266
267 if (!retval)
268 {
269 gchar* envstr = g_variant_print(env_array, FALSE);
270 g_warning("Unable to find '%s' in '%s'", var, envstr);
271 g_free(envstr);
272 }
273
274 return retval;
275 }
276
277 bool check_env(GVariant* env_array, const gchar* var, const gchar* value)
278 {
279 bool found = false;
280 GVariant* val = find_env(env_array, var);
281 if (val == nullptr)
282 {
283 return false;
284 }
285
286 const gchar* envvar = g_variant_get_string(val, nullptr);
287
288 gchar* combined = g_strdup_printf("%s=%s", var, value);
289 if (g_strcmp0(envvar, combined) == 0)
290 {
291 found = true;
292 }
293
294 g_variant_unref(val);
295
296 return found;
297 }
298
299 void pause(guint time = 0)
300 {
301 if (time > 0)
302 {
303 GMainLoop* mainloop = g_main_loop_new(NULL, FALSE);
304
305 g_timeout_add(time,
306 [](gpointer pmainloop) -> gboolean
307 {
308 g_main_loop_quit(static_cast<GMainLoop*>(pmainloop));
309 return G_SOURCE_REMOVE;
310 },
311 mainloop);
312
313 g_main_loop_run(mainloop);
314
315 g_main_loop_unref(mainloop);
316 }
317
318 while (g_main_pending())
319 {
320 g_main_iteration(TRUE);
321 }
322 }
323};
324
325TEST_F(LibUAL, DISABLED_StartApplication)
326{
327 DbusTestDbusMockObject* obj =
328 dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
329
330 /* Basic make sure we can send the event */
331 auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3");
332 auto app = ubuntu::app_launch::Application::create(appid, registry);
333 app->launch();
334
335 EXPECT_EQ(1, dbus_test_dbus_mock_object_check_method_call(mock, obj, "Start", NULL, NULL));
336
337 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
338
339 /* Now look at the details of the call */
340 app->launch();
341
342 guint len = 0;
343 const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
344 EXPECT_NE(nullptr, calls);
345 EXPECT_EQ(1, len);
346
347 EXPECT_STREQ("Start", calls->name);
348 EXPECT_EQ(2, g_variant_n_children(calls->params));
349
350 GVariant* block = g_variant_get_child_value(calls->params, 1);
351 EXPECT_TRUE(g_variant_get_boolean(block));
352 g_variant_unref(block);
353
354 GVariant* env = g_variant_get_child_value(calls->params, 0);
355 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
356 g_variant_unref(env);
357
358 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
359
360 /* Let's pass some URLs */
361 std::vector<ubuntu::app_launch::Application::URL> urls{
362 ubuntu::app_launch::Application::URL::from_raw("http://ubuntu.com/"),
363 ubuntu::app_launch::Application::URL::from_raw("https://ubuntu.com/"),
364 ubuntu::app_launch::Application::URL::from_raw("file:///home/phablet/test.txt")};
365
366 app->launch(urls);
367
368 len = 0;
369 calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
370 EXPECT_NE(nullptr, calls);
371 EXPECT_EQ(1, len);
372
373 env = g_variant_get_child_value(calls->params, 0);
374 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
375 EXPECT_TRUE(
376 check_env(env, "APP_URIS", "'http://ubuntu.com/' 'https://ubuntu.com/' 'file:///home/phablet/test.txt'"));
377 g_variant_unref(env);
378
379 return;
380}
381
382TEST_F(LibUAL, DISABLED_StartApplicationTest)
383{
384 DbusTestDbusMockObject* obj =
385 dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
386
387 /* Basic make sure we can send the event */
388 auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3");
389 auto app = ubuntu::app_launch::Application::create(appid, registry);
390 app->launchTest();
391
392 guint len = 0;
393 const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
394 EXPECT_NE(nullptr, calls);
395 EXPECT_EQ(1, len);
396
397 EXPECT_STREQ("Start", calls->name);
398 EXPECT_EQ(2, g_variant_n_children(calls->params));
399
400 GVariant* block = g_variant_get_child_value(calls->params, 1);
401 EXPECT_TRUE(g_variant_get_boolean(block));
402 g_variant_unref(block);
403
404 GVariant* env = g_variant_get_child_value(calls->params, 0);
405 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
406 EXPECT_TRUE(check_env(env, "QT_LOAD_TESTABILITY", "1"));
407 g_variant_unref(env);
408}
409
410TEST_F(LibUAL, DISABLED_StopApplication)
411{
412 DbusTestDbusMockObject* obj =
413 dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
414
415 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
416 auto app = ubuntu::app_launch::Application::create(appid, registry);
417
418 ASSERT_TRUE(app->hasInstances());
419 EXPECT_EQ(1, app->instances().size());
420
421 app->instances()[0]->stop();
422
423 ASSERT_EQ(dbus_test_dbus_mock_object_check_method_call(mock, obj, "Stop", NULL, NULL), 1);
424}
425
426/* NOTE: The fact that there is 'libertine-data' in these strings is because
427 we're using one CACHE_HOME for this test suite and the libertine functions
428 need to pull things from there, where these are only comparisons. It's just
429 what value is in the environment variable */
430TEST_F(LibUAL, DISABLED_ApplicationLog)
431{
432 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
433 auto app = ubuntu::app_launch::Application::create(appid, registry);
434
435 EXPECT_EQ(
436 std::string(CMAKE_SOURCE_DIR "/libertine-data/upstart/application-click-com.test.good_application_1.2.3.log"),
437 app->instances()[0]->logPath());
438
439 appid = ubuntu::app_launch::AppID::find("single");
440 app = ubuntu::app_launch::Application::create(appid, registry);
441
442 EXPECT_EQ(std::string(CMAKE_SOURCE_DIR "/libertine-data/upstart/application-legacy-single-.log"),
443 app->instances()[0]->logPath());
444
445 appid = ubuntu::app_launch::AppID::find("multiple");
446 app = ubuntu::app_launch::Application::create(appid, registry);
447
448 EXPECT_EQ(std::string(CMAKE_SOURCE_DIR "/libertine-data/upstart/application-legacy-multiple-2342345.log"),
449 app->instances()[0]->logPath());
450}
451
452TEST_F(LibUAL, DISABLED_ApplicationPid)
453{
454 /* Check bad params */
455 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
456 auto app = ubuntu::app_launch::Application::create(appid, registry);
457
458 EXPECT_FALSE(app->instances()[0]->hasPid(0));
459
460 /* Check primary pid, which comes from Upstart */
461 EXPECT_EQ(getpid(), app->instances()[0]->primaryPid());
462
463 auto multiappid = ubuntu::app_launch::AppID::find("multiple");
464 auto multiapp = ubuntu::app_launch::Application::create(multiappid, registry);
465 EXPECT_EQ(5678, multiapp->instances()[0]->primaryPid());
466
467 /* Look at the full PID list from CG Manager */
468 DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock, "/org/linuxcontainers/cgmanager",
469 "org.linuxcontainers.cgmanager0_0", NULL);
470 const DbusTestDbusMockCall* calls = NULL;
471 guint len = 0;
472
473 /* Click in the set */
474 EXPECT_TRUE(app->instances()[0]->hasPid(100));
475 calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL);
476 EXPECT_EQ(1, len);
477 EXPECT_STREQ("GetTasksRecursive", calls->name);
478 EXPECT_TRUE(g_variant_equal(
479 calls->params, g_variant_new("(ss)", "freezer", "upstart/application-click-com.test.good_application_1.2.3")));
480 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL));
481
482 /* Click out of the set */
483 EXPECT_FALSE(app->instances()[0]->hasPid(101));
484 calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL);
485 EXPECT_EQ(1, len);
486 EXPECT_STREQ("GetTasksRecursive", calls->name);
487 EXPECT_TRUE(g_variant_equal(
488 calls->params, g_variant_new("(ss)", "freezer", "upstart/application-click-com.test.good_application_1.2.3")));
489 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL));
490
491 /* Legacy Single Instance */
492 auto singleappid = ubuntu::app_launch::AppID::find("single");
493 auto singleapp = ubuntu::app_launch::Application::create(singleappid, registry);
494
495 EXPECT_TRUE(singleapp->instances()[0]->hasPid(100));
496
497 calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL);
498 EXPECT_EQ(1, len);
499 EXPECT_STREQ("GetTasksRecursive", calls->name);
500 EXPECT_TRUE(g_variant_equal(calls->params, g_variant_new("(ss)", "freezer", "upstart/application-legacy-single-")));
501 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL));
502
503 /* Legacy Multi Instance */
504 EXPECT_TRUE(multiapp->instances()[0]->hasPid(100));
505 calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL);
506 EXPECT_EQ(1, len);
507 EXPECT_STREQ("GetTasksRecursive", calls->name);
508 EXPECT_TRUE(g_variant_equal(calls->params,
509 g_variant_new("(ss)", "freezer", "upstart/application-legacy-multiple-2342345")));
510 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL));
511}
512
513TEST_F(LibUAL, DISABLED_ApplicationId)
514{
515 g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
516 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
517
518 /* Test with current-user-version, should return the version in the manifest */
519 EXPECT_EQ("com.test.good_application_1.2.3",
520 (std::string)ubuntu::app_launch::AppID::discover("com.test.good", "application"));
521
522 /* Test with version specified, shouldn't even read the manifest */
523 EXPECT_EQ("com.test.good_application_1.2.4",
524 (std::string)ubuntu::app_launch::AppID::discover("com.test.good", "application", "1.2.4"));
525
526 /* Test with out a version or app, should return the version in the manifest */
527 EXPECT_EQ("com.test.good_application_1.2.3", (std::string)ubuntu::app_launch::AppID::discover(
528 "com.test.good", "first-listed-app", "current-user-version"));
529
530 /* Make sure we can select the app from a list correctly */
531 EXPECT_EQ("com.test.multiple_first_1.2.3",
532 (std::string)ubuntu::app_launch::AppID::discover(
533 "com.test.multiple", ubuntu::app_launch::AppID::ApplicationWildcard::FIRST_LISTED));
534 EXPECT_EQ("com.test.multiple_first_1.2.3", (std::string)ubuntu::app_launch::AppID::discover("com.test.multiple"));
535 EXPECT_EQ("com.test.multiple_fifth_1.2.3",
536 (std::string)ubuntu::app_launch::AppID::discover(
537 "com.test.multiple", ubuntu::app_launch::AppID::ApplicationWildcard::LAST_LISTED));
538 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover(
539 "com.test.multiple", ubuntu::app_launch::AppID::ApplicationWildcard::ONLY_LISTED));
540 EXPECT_EQ("com.test.good_application_1.2.3",
541 (std::string)ubuntu::app_launch::AppID::discover(
542 "com.test.good", ubuntu::app_launch::AppID::ApplicationWildcard::ONLY_LISTED));
543
544 /* A bunch that should be NULL */
545 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-hooks"));
546 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-json"));
547 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-object"));
548 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-version"));
549
550 /* Libertine tests */
551 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("container-name"));
552 EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("container-name", "not-exist"));
553 EXPECT_EQ("container-name_test_0.0", (std::string)ubuntu::app_launch::AppID::discover("container-name", "test"));
554 EXPECT_EQ("container-name_user-app_0.0",
555 (std::string)ubuntu::app_launch::AppID::discover("container-name", "user-app"));
556}
557
558TEST_F(LibUAL, AppIdParse)
559{
560 EXPECT_FALSE(ubuntu::app_launch::AppID::parse("com.ubuntu.test_test_123").empty());
561 EXPECT_FALSE(ubuntu::app_launch::AppID::find("inkscape").empty());
562
563 auto id = ubuntu::app_launch::AppID::parse("com.ubuntu.test_test_123");
564
565 ASSERT_FALSE(id.empty());
566 EXPECT_EQ("com.ubuntu.test", id.package.value());
567 EXPECT_EQ("test", id.appname.value());
568 EXPECT_EQ("123", id.version.value());
569
570 return;
571}
572
573TEST_F(LibUAL, DISABLED_ApplicationList)
574{
575 auto apps = ubuntu::app_launch::Registry::runningApps(registry);
576
577 ASSERT_EQ(2, apps.size());
578
579 apps.sort([](const std::shared_ptr<ubuntu::app_launch::Application>& a,
580 const std::shared_ptr<ubuntu::app_launch::Application>& b)
581 {
582 std::string sa = a->appId();
583 std::string sb = b->appId();
584
585 return sa < sb;
586 });
587
588 EXPECT_EQ("com.test.good_application_1.2.3", (std::string)apps.front()->appId());
589 EXPECT_EQ("multiple", (std::string)apps.back()->appId());
590}
591
592typedef struct
593{
594 unsigned int count;
595 const gchar* name;
596} observer_data_t;
597
598static void observer_cb(const gchar* appid, gpointer user_data)
599{
600 observer_data_t* data = (observer_data_t*)user_data;
601
602 if (data->name == NULL)
603 {
604 data->count++;
605 }
606 else if (g_strcmp0(data->name, appid) == 0)
607 {
608 data->count++;
609 }
610}
611
612TEST_F(LibUAL, StartStopObserver)
613{
614 observer_data_t start_data = {.count = 0, .name = nullptr};
615 observer_data_t stop_data = {.count = 0, .name = nullptr};
616
617 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_started(observer_cb, &start_data));
618 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_stop(observer_cb, &stop_data));
619
620 DbusTestDbusMockObject* obj =
621 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
622
623 /* Basic start */
624 dbus_test_dbus_mock_object_emit_signal(
625 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
626 g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
627 NULL);
628
629 g_usleep(100000);
630 while (g_main_pending())
631 {
632 g_main_iteration(TRUE);
633 }
634
635 ASSERT_EQ(start_data.count, 1);
636
637 /* Basic stop */
638 dbus_test_dbus_mock_object_emit_signal(
639 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
640 g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
641 NULL);
642
643 g_usleep(100000);
644 while (g_main_pending())
645 {
646 g_main_iteration(TRUE);
647 }
648
649 ASSERT_EQ(stop_data.count, 1);
650
651 /* Start legacy */
652 start_data.count = 0;
653 start_data.name = "multiple";
654
655 dbus_test_dbus_mock_object_emit_signal(
656 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
657 g_variant_new_parsed("('started', ['JOB=application-legacy', 'INSTANCE=multiple-234235'])"), NULL);
658
659 g_usleep(100000);
660 while (g_main_pending())
661 {
662 g_main_iteration(TRUE);
663 }
664
665 ASSERT_EQ(start_data.count, 1);
666
667 /* Legacy stop */
668 stop_data.count = 0;
669 stop_data.name = "bar";
670
671 dbus_test_dbus_mock_object_emit_signal(
672 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
673 g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=bar-9344321'])"), NULL);
674
675 g_usleep(100000);
676 while (g_main_pending())
677 {
678 g_main_iteration(TRUE);
679 }
680
681 ASSERT_EQ(stop_data.count, 1);
682
683 /* Test Noise Start */
684 start_data.count = 0;
685 start_data.name = "com.test.good_application_1.2.3";
686 stop_data.count = 0;
687 stop_data.name = "com.test.good_application_1.2.3";
688
689 /* A full lifecycle */
690 dbus_test_dbus_mock_object_emit_signal(
691 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
692 g_variant_new_parsed("('starting', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
693 NULL);
694 dbus_test_dbus_mock_object_emit_signal(
695 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
696 g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
697 NULL);
698 dbus_test_dbus_mock_object_emit_signal(
699 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
700 g_variant_new_parsed("('stopping', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
701 NULL);
702 dbus_test_dbus_mock_object_emit_signal(
703 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
704 g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
705 NULL);
706
707 g_usleep(100000);
708 while (g_main_pending())
709 {
710 g_main_iteration(TRUE);
711 }
712
713 /* Ensure we just signaled once for each */
714 ASSERT_EQ(start_data.count, 1);
715 ASSERT_EQ(stop_data.count, 1);
716
717 /* Remove */
718 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_started(observer_cb, &start_data));
719 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_stop(observer_cb, &stop_data));
720}
721
722static GDBusMessage* filter_starting(GDBusConnection* conn,
723 GDBusMessage* message,
724 gboolean incomming,
725 gpointer user_data)
726{
727 if (g_strcmp0(g_dbus_message_get_member(message), "UnityStartingSignal") == 0)
728 {
729 unsigned int* count = static_cast<unsigned int*>(user_data);
730 (*count)++;
731 g_object_unref(message);
732 return NULL;
733 }
734
735 return message;
736}
737
738static void starting_observer(const gchar* appid, gpointer user_data)
739{
740 std::string* last = static_cast<std::string*>(user_data);
741 *last = appid;
742 return;
743}
744
745TEST_F(LibUAL, StartingResponses)
746{
747 std::string last_observer;
748 unsigned int starting_count = 0;
749 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
750 guint filter = g_dbus_connection_add_filter(session, filter_starting, &starting_count, NULL);
751
752 EXPECT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_observer, &last_observer));
753
754 g_dbus_connection_emit_signal(session, NULL, /* destination */
755 "/", /* path */
756 "com.canonical.UbuntuAppLaunch", /* interface */
757 "UnityStartingBroadcast", /* signal */
758 g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */
759 NULL);
760
761 pause(100);
762
763 EXPECT_EQ("com.test.good_application_1.2.3", last_observer);
764 EXPECT_EQ(1, starting_count);
765
766 EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_starting(starting_observer, &last_observer));
767
768 g_dbus_connection_remove_filter(session, filter);
769 g_object_unref(session);
770}
771
772TEST_F(LibUAL, AppIdTest)
773{
774 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
775 auto app = ubuntu::app_launch::Application::create(appid, registry);
776 app->launch();
777
778 pause(50); /* Ensure all the events come through */
779 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
780 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
781}
782
783GDBusMessage* filter_func_good(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)
784{
785 if (!incomming)
786 {
787 return message;
788 }
789
790 if (g_strcmp0(g_dbus_message_get_path(message), (gchar*)user_data) == 0)
791 {
792 GDBusMessage* reply = g_dbus_message_new_method_reply(message);
793 g_dbus_connection_send_message(conn, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
794 g_object_unref(message);
795 return NULL;
796 }
797
798 return message;
799}
800
801TEST_F(LibUAL, UrlSendTest)
802{
803 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
804 guint filter = g_dbus_connection_add_filter(session, filter_func_good,
805 (gpointer) "/com_2etest_2egood_5fapplication_5f1_2e2_2e3", NULL);
806
807 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
808 auto app = ubuntu::app_launch::Application::create(appid, registry);
809 std::vector<ubuntu::app_launch::Application::URL> uris = {
810 ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")};
811
812 app->launch(uris);
813
814 pause(100); /* Ensure all the events come through */
815
816 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
817 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
818
819 g_dbus_connection_remove_filter(session, filter);
820
821 /* Send multiple resume responses to ensure we unsubscribe */
822 /* Multiple to increase our chance of hitting a bad free in the middle,
823 fun with async! */
824 int i;
825 for (i = 0; i < 5; i++)
826 {
827 g_dbus_connection_emit_signal(session, NULL, /* destination */
828 "/", /* path */
829 "com.canonical.UbuntuAppLaunch", /* interface */
830 "UnityResumeResponse", /* signal */
831 g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */
832 NULL);
833
834 pause(50); /* Ensure all the events come through */
835 }
836
837 g_object_unref(session);
838}
839
840TEST_F(LibUAL, UrlSendNoObjectTest)
841{
842 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
843 auto app = ubuntu::app_launch::Application::create(appid, registry);
844 std::vector<ubuntu::app_launch::Application::URL> uris = {
845 ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")};
846
847 app->launch(uris);
848
849 pause(100); /* Ensure all the events come through */
850
851 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
852 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
853}
854
855TEST_F(LibUAL, UnityTimeoutTest)
856{
857 this->resume_timeout = 100;
858
859 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
860 auto app = ubuntu::app_launch::Application::create(appid, registry);
861
862 app->launch();
863
864 pause(1000); /* Ensure all the events come through */
865 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
866 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
867}
868
869TEST_F(LibUAL, UnityTimeoutUriTest)
870{
871 this->resume_timeout = 200;
872
873 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
874 auto app = ubuntu::app_launch::Application::create(appid, registry);
875 std::vector<ubuntu::app_launch::Application::URL> uris = {
876 ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")};
877
878 app->launch(uris);
879
880 pause(1000); /* Ensure all the events come through */
881 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
882 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
883}
884
885GDBusMessage* filter_respawn(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)
886{
887 if (g_strcmp0(g_dbus_message_get_member(message), "UnityResumeResponse") == 0)
888 {
889 g_object_unref(message);
890 return NULL;
891 }
892
893 return message;
894}
895
896TEST_F(LibUAL, UnityLostTest)
897{
898 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
899 guint filter = g_dbus_connection_add_filter(session, filter_respawn, NULL, NULL);
900
901 guint start = g_get_monotonic_time();
902
903 auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3");
904 auto app = ubuntu::app_launch::Application::create(appid, registry);
905 std::vector<ubuntu::app_launch::Application::URL> uris = {
906 ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")};
907
908 app->launch(uris);
909
910 guint end = g_get_monotonic_time();
911
912 g_debug("Start call time: %d ms", (end - start) / 1000);
913 EXPECT_LT(end - start, 2000 * 1000);
914
915 pause(1000); /* Ensure all the events come through */
916
917 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
918 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
919
920 g_dbus_connection_remove_filter(session, filter);
921 g_object_unref(session);
922}
923
924TEST_F(LibUAL, LegacySingleInstance)
925{
926 DbusTestDbusMockObject* obj =
927 dbus_test_dbus_mock_get_object(mock, "/com/test/application_legacy", "com.ubuntu.Upstart0_6.Job", NULL);
928
929 /* Check for a single-instance app */
930 auto singleappid = ubuntu::app_launch::AppID::find("single");
931 auto singleapp = ubuntu::app_launch::Application::create(singleappid, registry);
932
933 singleapp->launch();
934
935 guint len = 0;
936 const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
937 EXPECT_NE(nullptr, calls);
938 EXPECT_EQ(1, len);
939
940 EXPECT_STREQ("Start", calls->name);
941 EXPECT_EQ(2, g_variant_n_children(calls->params));
942
943 GVariant* block = g_variant_get_child_value(calls->params, 1);
944 EXPECT_TRUE(g_variant_get_boolean(block));
945 g_variant_unref(block);
946
947 GVariant* env = g_variant_get_child_value(calls->params, 0);
948 EXPECT_TRUE(check_env(env, "APP_ID", "single"));
949 EXPECT_TRUE(check_env(env, "INSTANCE_ID", ""));
950 g_variant_unref(env);
951
952 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
953
954 /* Check for a multi-instance app */
955 auto multipleappid = ubuntu::app_launch::AppID::find("multiple");
956 auto multipleapp = ubuntu::app_launch::Application::create(multipleappid, registry);
957
958 multipleapp->launch();
959
960 len = 0;
961 calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
962 EXPECT_NE(nullptr, calls);
963 EXPECT_EQ(1, len);
964
965 EXPECT_STREQ("Start", calls->name);
966 EXPECT_EQ(2, g_variant_n_children(calls->params));
967
968 block = g_variant_get_child_value(calls->params, 1);
969 EXPECT_TRUE(g_variant_get_boolean(block));
970 g_variant_unref(block);
971
972 env = g_variant_get_child_value(calls->params, 0);
973 EXPECT_TRUE(check_env(env, "APP_ID", "multiple"));
974 EXPECT_FALSE(check_env(env, "INSTANCE_ID", ""));
975 g_variant_unref(env);
976}
977
978static void failed_observer(const gchar* appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)
979{
980 if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH)
981 {
982 std::string* last = static_cast<std::string*>(user_data);
983 *last = appid;
984 }
985 return;
986}
987
988TEST_F(LibUAL, FailingObserver)
989{
990 std::string last_observer;
991 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
992
993 EXPECT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));
994
995 g_dbus_connection_emit_signal(
996 session, NULL, /* destination */
997 "/", /* path */
998 "com.canonical.UbuntuAppLaunch", /* interface */
999 "ApplicationFailed", /* signal */
1000 g_variant_new("(ss)", "com.test.good_application_1.2.3", "crash"), /* params, the same */
1001 NULL);
1002
1003 pause(100);
1004
1005 EXPECT_EQ("com.test.good_application_1.2.3", last_observer);
1006
1007 last_observer.clear();
1008
1009 g_dbus_connection_emit_signal(
1010 session, NULL, /* destination */
1011 "/", /* path */
1012 "com.canonical.UbuntuAppLaunch", /* interface */
1013 "ApplicationFailed", /* signal */
1014 g_variant_new("(ss)", "com.test.good_application_1.2.3", "blahblah"), /* params, the same */
1015 NULL);
1016
1017 pause(100);
1018
1019 EXPECT_EQ("com.test.good_application_1.2.3", last_observer);
1020
1021 last_observer.clear();
1022
1023 g_dbus_connection_emit_signal(
1024 session, NULL, /* destination */
1025 "/", /* path */
1026 "com.canonical.UbuntuAppLaunch", /* interface */
1027 "ApplicationFailed", /* signal */
1028 g_variant_new("(ss)", "com.test.good_application_1.2.3", "start-failure"), /* params, the same */
1029 NULL);
1030
1031 pause(100);
1032
1033 EXPECT_TRUE(last_observer.empty());
1034
1035 EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));
1036
1037 g_object_unref(session);
1038}
1039
1040TEST_F(LibUAL, StartHelper)
1041{
1042 DbusTestDbusMockObject* obj =
1043 dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
1044
1045 auto untrusted = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type");
1046
1047 /* Basic make sure we can send the event */
1048 auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3");
1049 auto helper = ubuntu::app_launch::Helper::create(untrusted, appid, registry);
1050
1051 helper->launch();
1052
1053 EXPECT_EQ(1, dbus_test_dbus_mock_object_check_method_call(mock, obj, "Start", NULL, NULL));
1054
1055 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
1056
1057 /* Now check a multi out */
1058 helper->launch();
1059
1060 guint len = 0;
1061 auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1062 EXPECT_NE(nullptr, calls);
1063 EXPECT_EQ(1, len);
1064
1065 EXPECT_STREQ("Start", calls->name);
1066 EXPECT_EQ(2, g_variant_n_children(calls->params));
1067
1068 auto block = g_variant_get_child_value(calls->params, 1);
1069 EXPECT_TRUE(g_variant_get_boolean(block));
1070 g_variant_unref(block);
1071
1072 auto env = g_variant_get_child_value(calls->params, 0);
1073 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
1074 EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type"));
1075 g_variant_unref(env);
1076
1077 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
1078
1079 /* Let's pass some URLs */
1080 std::vector<ubuntu::app_launch::Helper::URL> urls = {
1081 ubuntu::app_launch::Helper::URL::from_raw("http://ubuntu.com/"),
1082 ubuntu::app_launch::Helper::URL::from_raw("https://ubuntu.com/"),
1083 ubuntu::app_launch::Helper::URL::from_raw("file:///home/phablet/test.txt")};
1084 helper->launch(urls);
1085
1086 len = 0;
1087 calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1088 EXPECT_NE(nullptr, calls);
1089 EXPECT_EQ(1, len);
1090
1091 env = g_variant_get_child_value(calls->params, 0);
1092 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
1093 EXPECT_TRUE(
1094 check_env(env, "APP_URIS", "'http://ubuntu.com/' 'https://ubuntu.com/' 'file:///home/phablet/test.txt'"));
1095 EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type"));
1096 EXPECT_FALSE(check_env(env, "INSTANCE_ID", NULL));
1097 g_variant_unref(env);
1098
1099 return;
1100}
1101
1102TEST_F(LibUAL, StopHelper)
1103{
1104 DbusTestDbusMockObject* obj =
1105 dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
1106
1107 /* Multi helper */
1108 auto untrusted = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type");
1109
1110 auto appid = ubuntu::app_launch::AppID::parse("com.bar_foo_8432.13.1");
1111 auto helper = ubuntu::app_launch::Helper::create(untrusted, appid, registry);
1112
1113 ASSERT_TRUE(helper->hasInstances());
1114
1115 auto instances = helper->instances();
1116
1117 EXPECT_EQ(1, instances.size());
1118
1119 instances[0]->stop();
1120
1121 ASSERT_EQ(dbus_test_dbus_mock_object_check_method_call(mock, obj, "Stop", NULL, NULL), 1);
1122
1123 guint len = 0;
1124 auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Stop", &len, NULL);
1125 EXPECT_NE(nullptr, calls);
1126 EXPECT_EQ(1, len);
1127
1128 EXPECT_STREQ("Stop", calls->name);
1129 EXPECT_EQ(2, g_variant_n_children(calls->params));
1130
1131 auto block = g_variant_get_child_value(calls->params, 1);
1132 EXPECT_TRUE(g_variant_get_boolean(block));
1133 g_variant_unref(block);
1134
1135 auto env = g_variant_get_child_value(calls->params, 0);
1136 EXPECT_TRUE(check_env(env, "APP_ID", "com.bar_foo_8432.13.1"));
1137 EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type"));
1138 EXPECT_TRUE(check_env(env, "INSTANCE_ID", "24034582324132"));
1139 g_variant_unref(env);
1140
1141 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
1142
1143 return;
1144}
1145
1146TEST_F(LibUAL, HelperList)
1147{
1148 auto nothelper = ubuntu::app_launch::Helper::Type::from_raw("not-a-type");
1149 auto notlist = ubuntu::app_launch::Registry::runningHelpers(nothelper, registry);
1150
1151 EXPECT_EQ(0, notlist.size());
1152
1153 auto goodhelper = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type");
1154 auto goodlist = ubuntu::app_launch::Registry::runningHelpers(goodhelper, registry);
1155
1156 EXPECT_EQ(2, goodlist.size());
1157
1158 goodlist.sort(
1159 [](const std::shared_ptr<ubuntu::app_launch::Helper>& a, const std::shared_ptr<ubuntu::app_launch::Helper>& b)
1160 {
1161 std::string sa = a->appId();
1162 std::string sb = b->appId();
1163
1164 return sa < sb;
1165 });
1166
1167 EXPECT_EQ("com.bar_foo_8432.13.1", (std::string)goodlist.front()->appId());
1168 EXPECT_EQ("com.foo_bar_43.23.12", (std::string)goodlist.back()->appId());
1169
1170 EXPECT_TRUE(goodlist.front()->hasInstances());
1171 EXPECT_TRUE(goodlist.back()->hasInstances());
1172
1173 EXPECT_EQ(1, goodlist.front()->instances().size());
1174 EXPECT_EQ(1, goodlist.back()->instances().size());
1175
1176 EXPECT_TRUE(goodlist.front()->instances()[0]->isRunning());
1177 EXPECT_TRUE(goodlist.back()->instances()[0]->isRunning());
1178}
1179
1180typedef struct
1181{
1182 unsigned int count;
1183 const gchar* appid;
1184 const gchar* type;
1185 const gchar* instance;
1186} helper_observer_data_t;
1187
1188static void helper_observer_cb(const gchar* appid, const gchar* instance, const gchar* type, gpointer user_data)
1189{
1190 helper_observer_data_t* data = (helper_observer_data_t*)user_data;
1191
1192 if (g_strcmp0(data->appid, appid) == 0 && g_strcmp0(data->type, type) == 0 &&
1193 g_strcmp0(data->instance, instance) == 0)
1194 {
1195 data->count++;
1196 }
1197}
1198
1199TEST_F(LibUAL, StartStopHelperObserver)
1200{
1201 helper_observer_data_t start_data = {
1202 .count = 0, .appid = "com.foo_foo_1.2.3", .type = "my-type-is-scorpio", .instance = nullptr};
1203 helper_observer_data_t stop_data = {
1204 .count = 0, .appid = "com.bar_bar_44.32", .type = "my-type-is-libra", .instance = "1234"};
1205
1206 ASSERT_TRUE(ubuntu_app_launch_observer_add_helper_started(helper_observer_cb, "my-type-is-scorpio", &start_data));
1207 ASSERT_TRUE(ubuntu_app_launch_observer_add_helper_stop(helper_observer_cb, "my-type-is-libra", &stop_data));
1208
1209 DbusTestDbusMockObject* obj =
1210 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
1211
1212 /* Basic start */
1213 dbus_test_dbus_mock_object_emit_signal(
1214 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
1215 g_variant_new_parsed("('started', ['JOB=untrusted-helper', 'INSTANCE=my-type-is-scorpio::com.foo_foo_1.2.3'])"),
1216 NULL);
1217
1218 g_usleep(100000);
1219 while (g_main_pending())
1220 {
1221 g_main_iteration(TRUE);
1222 }
1223
1224 ASSERT_EQ(start_data.count, 1);
1225
1226 /* Basic stop */
1227 dbus_test_dbus_mock_object_emit_signal(
1228 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
1229 g_variant_new_parsed(
1230 "('stopped', ['JOB=untrusted-helper', 'INSTANCE=my-type-is-libra:1234:com.bar_bar_44.32'])"),
1231 NULL);
1232
1233 g_usleep(100000);
1234 while (g_main_pending())
1235 {
1236 g_main_iteration(TRUE);
1237 }
1238
1239 ASSERT_EQ(stop_data.count, 1);
1240
1241 /* Remove */
1242 ASSERT_TRUE(
1243 ubuntu_app_launch_observer_delete_helper_started(helper_observer_cb, "my-type-is-scorpio", &start_data));
1244 ASSERT_TRUE(ubuntu_app_launch_observer_delete_helper_stop(helper_observer_cb, "my-type-is-libra", &stop_data));
1245}
1246
1247gboolean datain(GIOChannel* source, GIOCondition cond, gpointer data)
1248{
1249 gsize* datacnt = static_cast<gsize*>(data);
1250 gchar* str = NULL;
1251 gsize len = 0;
1252 GError* error = NULL;
1253
1254 g_io_channel_read_line(source, &str, &len, NULL, &error);
1255 g_free(str);
1256
1257 if (error != NULL)
1258 {
1259 g_warning("Unable to read from channel: %s", error->message);
1260 g_error_free(error);
1261 }
1262
1263 *datacnt += len;
1264
1265 return TRUE;
1266}
1267
1268static void signal_increment(GDBusConnection* connection,
1269 const gchar* sender,
1270 const gchar* path,
1271 const gchar* interface,
1272 const gchar* signal,
1273 GVariant* params,
1274 gpointer user_data)
1275{
1276 guint* count = (guint*)user_data;
1277 g_debug("Count incremented to: %d", *count + 1);
1278 *count = *count + 1;
1279}
1280
1281TEST_F(LibUAL, PauseResume)
1282{
1283 g_setenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH", CMAKE_BINARY_DIR "/libual-proc", 1);
1284
1285 /* Setup some spew */
1286 GPid spewpid = 0;
1287 gint spewstdout = 0;
1288 const gchar* spewline[] = {SPEW_UTILITY, NULL};
1289 ASSERT_TRUE(g_spawn_async_with_pipes(NULL, (gchar**)spewline, NULL, /* environment */
1290 G_SPAWN_DEFAULT, NULL, NULL, /* child setup */
1291 &spewpid, NULL, /* stdin */
1292 &spewstdout, NULL, /* stderr */
1293 NULL)); /* error */
1294
1295 gsize datacnt = 0;
1296 GIOChannel* spewoutchan = g_io_channel_unix_new(spewstdout);
1297 g_io_channel_set_flags(spewoutchan, G_IO_FLAG_NONBLOCK, NULL);
1298 g_io_add_watch(spewoutchan, G_IO_IN, datain, &datacnt);
1299
1300 /* Setup our OOM adjust file */
1301 gchar* procdir = g_strdup_printf(CMAKE_BINARY_DIR "/libual-proc/%d", spewpid);
1302 ASSERT_EQ(0, g_mkdir_with_parents(procdir, 0700));
1303 gchar* oomadjfile = g_strdup_printf("%s/oom_score_adj", procdir);
1304 g_free(procdir);
1305 ASSERT_TRUE(g_file_set_contents(oomadjfile, "0", -1, NULL));
1306
1307 /* Setup the cgroup */
1308 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME", "org.test.cgmock2", TRUE);
1309 DbusTestDbusMock* cgmock2 = dbus_test_dbus_mock_new("org.test.cgmock2");
1310 DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock2, "/org/linuxcontainers/cgmanager",
1311 "org.linuxcontainers.cgmanager0_0", NULL);
1312 gchar* pypids = g_strdup_printf("ret = [%d]", spewpid);
1313 dbus_test_dbus_mock_object_add_method(cgmock, cgobject, "GetTasksRecursive", G_VARIANT_TYPE("(ss)"),
1314 G_VARIANT_TYPE("ai"), pypids, NULL);
1315 g_free(pypids);
1316
1317 dbus_test_service_add_task(service, DBUS_TEST_TASK(cgmock2));
1318 dbus_test_task_run(DBUS_TEST_TASK(cgmock2));
1319 g_object_unref(G_OBJECT(cgmock2));
1320
1321 /* Setup ZG Mock */
1322 DbusTestDbusMock* zgmock = dbus_test_dbus_mock_new("org.gnome.zeitgeist.Engine");
1323 DbusTestDbusMockObject* zgobj =
1324 dbus_test_dbus_mock_get_object(zgmock, "/org/gnome/zeitgeist/log/activity", "org.gnome.zeitgeist.Log", NULL);
1325
1326 dbus_test_dbus_mock_object_add_method(zgmock, zgobj, "InsertEvents", G_VARIANT_TYPE("a(asaasay)"),
1327 G_VARIANT_TYPE("au"), "ret = [ 0 ]", NULL);
1328
1329 dbus_test_service_add_task(service, DBUS_TEST_TASK(zgmock));
1330 dbus_test_task_run(DBUS_TEST_TASK(zgmock));
1331 g_object_unref(G_OBJECT(zgmock));
1332
1333 /* Give things a chance to start */
1334 do
1335 {
1336 g_debug("Giving mocks a chance to start");
1337 pause(200);
1338 } while (dbus_test_task_get_state(DBUS_TEST_TASK(cgmock2)) != DBUS_TEST_TASK_STATE_RUNNING &&
1339 dbus_test_task_get_state(DBUS_TEST_TASK(zgmock)) != DBUS_TEST_TASK_STATE_RUNNING);
1340
1341 /* Setup signal handling */
1342 guint paused_count = 0;
1343 guint resumed_count = 0;
1344 guint paused_signal =
1345 g_dbus_connection_signal_subscribe(bus, nullptr, "com.canonical.UbuntuAppLaunch", "ApplicationPaused", "/",
1346 nullptr, G_DBUS_SIGNAL_FLAGS_NONE, signal_increment, &paused_count, nullptr);
1347 guint resumed_signal = g_dbus_connection_signal_subscribe(
1348 bus, nullptr, "com.canonical.UbuntuAppLaunch", "ApplicationResumed", "/", nullptr, G_DBUS_SIGNAL_FLAGS_NONE,
1349 signal_increment, &resumed_count, nullptr);
1350
1351 /* Test it */
1352 EXPECT_NE(0, datacnt);
1353 paused_count = 0;
1354
1355 /* Pause the app */
1356 EXPECT_TRUE(ubuntu_app_launch_pause_application("com.test.good_application_1.2.3"));
1357
1358 pause(0); /* Flush queued events */
1359 datacnt = 0; /* clear it */
1360
1361 pause(200);
1362
1363 /* Check data coming out */
1364 EXPECT_EQ(1, paused_count);
1365 EXPECT_EQ(0, datacnt);
1366
1367 /* Check to make sure we sent the event to ZG */
1368 guint numcalls = 0;
1369 const DbusTestDbusMockCall* calls =
1370 dbus_test_dbus_mock_object_get_method_calls(zgmock, zgobj, "InsertEvents", &numcalls, NULL);
1371
1372 EXPECT_NE(nullptr, calls);
1373 EXPECT_EQ(1, numcalls);
1374
1375 dbus_test_dbus_mock_object_clear_method_calls(zgmock, zgobj, NULL);
1376
1377 /* Check to ensure we set the OOM score */
1378 gchar* pauseoomscore = NULL;
1379 ASSERT_TRUE(g_file_get_contents(oomadjfile, &pauseoomscore, NULL, NULL));
1380 EXPECT_STREQ("900", pauseoomscore);
1381 g_free(pauseoomscore);
1382 resumed_count = 0;
1383
1384 /* Now Resume the App */
1385 EXPECT_TRUE(ubuntu_app_launch_resume_application("com.test.good_application_1.2.3"));
1386
1387 pause(200);
1388
1389 EXPECT_NE(0, datacnt);
1390 EXPECT_EQ(1, resumed_count);
1391
1392 /* Check to make sure we sent the event to ZG */
1393 numcalls = 0;
1394 calls = dbus_test_dbus_mock_object_get_method_calls(zgmock, zgobj, "InsertEvents", &numcalls, NULL);
1395
1396 EXPECT_NE(nullptr, calls);
1397 EXPECT_EQ(1, numcalls);
1398
1399 /* Check to ensure we set the OOM score */
1400 gchar* resumeoomscore = NULL;
1401 ASSERT_TRUE(g_file_get_contents(oomadjfile, &resumeoomscore, NULL, NULL));
1402 EXPECT_STREQ("100", resumeoomscore);
1403 g_free(resumeoomscore);
1404
1405 /* Clean up */
1406 gchar* killstr = g_strdup_printf("kill -9 %d", spewpid);
1407 ASSERT_TRUE(g_spawn_command_line_sync(killstr, NULL, NULL, NULL, NULL));
1408 g_free(killstr);
1409
1410 g_io_channel_unref(spewoutchan);
1411
1412 g_spawn_command_line_sync("rm -rf " CMAKE_BINARY_DIR "/libual-proc", NULL, NULL, NULL, NULL);
1413
1414 g_dbus_connection_signal_unsubscribe(bus, paused_signal);
1415 g_dbus_connection_signal_unsubscribe(bus, resumed_signal);
1416
1417 /* Kill ZG default instance :-( */
1418 ZeitgeistLog* log = zeitgeist_log_get_default();
1419 g_object_unref(log);
1420 g_object_unref(log);
1421
1422 g_free(oomadjfile);
1423}
1424
1425TEST_F(LibUAL, StartSessionHelper)
1426{
1427 DbusTestDbusMockObject* obj =
1428 dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
1429 MirConnection* conn = mir_connect_sync("libual-test", "start-session-helper"); // Mocked, doesn't need cleaning up
1430 MirPromptSession* msession = mir_connection_create_prompt_session_sync(conn, 5, nullptr, nullptr);
1431
1432 /* Building a temporary file and making an FD for it */
1433 const char* filedata = "This is some data that we should get on the other side\n";
1434 ASSERT_TRUE(g_file_set_contents(SESSION_TEMP_FILE, filedata, strlen(filedata), nullptr) == TRUE);
1435 int mirfd = open(SESSION_TEMP_FILE, 0);
1436 mir_mock_set_trusted_fd(mirfd);
1437
1438 /* Basic make sure we can send the event */
1439 auto untrusted = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type");
1440 auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3");
1441 auto helper = ubuntu::app_launch::Helper::create(untrusted, appid, registry);
1442
1443 helper->launch(msession);
1444
1445 guint len = 0;
1446 const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
1447 EXPECT_NE(nullptr, calls);
1448 EXPECT_EQ(1, len);
1449
1450 EXPECT_STREQ("Start", calls->name);
1451 EXPECT_EQ(2, g_variant_n_children(calls->params));
1452
1453 GVariant* block = g_variant_get_child_value(calls->params, 1);
1454 EXPECT_TRUE(g_variant_get_boolean(block));
1455 g_variant_unref(block);
1456
1457 /* Check the environment */
1458 GVariant* env = g_variant_get_child_value(calls->params, 0);
1459 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
1460 EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type"));
1461
1462 GVariant* mnamev = find_env(env, "UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
1463 ASSERT_NE(nullptr, mnamev); /* Have to assert because, eh, GVariant */
1464 EXPECT_STREQ(g_dbus_connection_get_unique_name(bus),
1465 g_variant_get_string(mnamev, nullptr) + strlen("UBUNTU_APP_LAUNCH_DEMANGLE_NAME="));
1466 GVariant* mpathv = find_env(env, "UBUNTU_APP_LAUNCH_DEMANGLE_PATH");
1467 ASSERT_NE(nullptr, mpathv); /* Have to assert because, eh, GVariant */
1468
1469 g_variant_unref(env);
1470
1471 /* Setup environment for call */
1472 const gchar* mname = g_variant_get_string(mnamev, nullptr);
1473 mname += strlen("UBUNTU_APP_LAUNCH_DEMANGLE_NAME=");
1474 g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", mname, TRUE);
1475 g_variant_unref(mnamev);
1476
1477 const gchar* mpath = g_variant_get_string(mpathv, nullptr);
1478 mpath += strlen("UBUNTU_APP_LAUNCH_DEMANGLE_PATH=");
1479 g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_PATH", mpath, TRUE);
1480 g_variant_unref(mpathv);
1481
1482 /* Exec our tool */
1483 std::promise<std::string> outputpromise;
1484 std::thread t(
1485 [&outputpromise]()
1486 {
1487 gchar* socketstdout = nullptr;
1488 GError* error = nullptr;
1489 g_unsetenv("G_MESSAGES_DEBUG");
1490
1491 g_spawn_command_line_sync(SOCKET_DEMANGLER " " SOCKET_TOOL, &socketstdout, nullptr, nullptr, &error);
1492
1493 if (error != nullptr)
1494 {
1495 fprintf(stderr, "Unable to spawn '" SOCKET_DEMANGLER " " SOCKET_TOOL "': %s\n", error->message);
1496 g_error_free(error);
1497 outputpromise.set_value(std::string(""));
1498 }
1499 else
1500 {
1501 outputpromise.set_value(std::string(socketstdout));
1502 g_free(socketstdout);
1503 }
1504 });
1505 t.detach();
1506
1507 auto outputfuture = outputpromise.get_future();
1508 while (outputfuture.wait_for(std::chrono::milliseconds{1}) != std::future_status::ready)
1509 {
1510 pause();
1511 }
1512
1513 ASSERT_STREQ(filedata, outputfuture.get().c_str());
1514
1515 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
1516
1517 return;
1518}
1519
1520TEST_F(LibUAL, SetExec)
1521{
1522 DbusTestDbusMockObject* obj =
1523 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
1524
1525 const char* exec = "lets exec this";
1526
1527 g_setenv("UPSTART_JOB", "fubar", TRUE);
1528 g_unsetenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME");
1529 EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, NULL));
1530
1531 guint len = 0;
1532 const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
1533 ASSERT_NE(nullptr, calls);
1534 EXPECT_EQ(1, len);
1535
1536 gchar* appexecstr = g_strdup_printf("APP_EXEC=%s", exec);
1537 GVariant* appexecenv = g_variant_get_child_value(calls[0].params, 1);
1538 EXPECT_STREQ(appexecstr, g_variant_get_string(appexecenv, nullptr));
1539 g_variant_unref(appexecenv);
1540 g_free(appexecstr);
1541
1542 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
1543
1544 /* Now check for the demangler */
1545 g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", g_dbus_connection_get_unique_name(bus), TRUE);
1546 EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, NULL));
1547
1548 calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL);
1549 ASSERT_NE(nullptr, calls);
1550 EXPECT_EQ(1, len);
1551
1552 gchar* demangleexecstr = g_strdup_printf("APP_EXEC=%s %s", SOCKET_DEMANGLER_INSTALL, exec);
1553 appexecenv = g_variant_get_child_value(calls[0].params, 1);
1554 EXPECT_STREQ(demangleexecstr, g_variant_get_string(appexecenv, nullptr));
1555 g_variant_unref(appexecenv);
1556 g_free(demangleexecstr);
1557
1558 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
1559
1560 /* Now check for the directory */
1561 g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", g_dbus_connection_get_unique_name(bus), TRUE);
1562 EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, "/not/a/real/directory"));
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches