Merge lp:~bregma/ubuntu-app-launch/app-object into lp:ubuntu-app-launch/16.04
- app-object
- Merge into trunk.16.04
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 |
Related bugs: |
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
- 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 supportsUbuntuL
ifecycle - 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
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2014-06-14 16:32:00 +0000 |
3 | +++ .bzrignore 2016-03-04 11:34:19 +0000 |
4 | @@ -5,3 +5,9 @@ |
5 | cmake_install.cmake |
6 | *-trace.[ch] |
7 | cgroup-reap-all |
8 | +libubuntu-app-launch/UbuntuAppLaunch-2.gir |
9 | +libubuntu-app-launch/UbuntuAppLaunch-2.typelib |
10 | +libubuntu-app-launch/libubuntu-app-launch.so.2* |
11 | +libubuntu-app-launch/proxy-socket-demangler.c |
12 | +libubuntu-app-launch/proxy-socket-demangler.h |
13 | +libubuntu-app-launch/ubuntu-app-launch-2.pc |
14 | |
15 | === modified file 'CMakeLists.txt' |
16 | --- CMakeLists.txt 2015-08-11 02:45:49 +0000 |
17 | +++ CMakeLists.txt 2016-03-04 11:34:19 +0000 |
18 | @@ -37,7 +37,8 @@ |
19 | set(ubuntu_app_launch_arch "${UBUNTU_APP_LAUNCH_ARCH}") |
20 | |
21 | # Deprecated needed for g_atexit() in libual |
22 | -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-error=unused-function -Wno-error=deprecated-declarations -std=c99") |
23 | +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-error=unused-function -Wno-error=deprecated-declarations -std=gnu99") |
24 | +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11 -pthread") |
25 | |
26 | enable_testing() |
27 | |
28 | @@ -77,6 +78,9 @@ |
29 | pkg_check_modules(MIR mirclient) |
30 | include_directories(${MIR_INCLUDE_DIRS}) |
31 | |
32 | +pkg_check_modules(LIBERTINE libertine) |
33 | +include_directories(${LIBERTINE_INCLUDE_DIRS}) |
34 | + |
35 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) |
36 | |
37 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") |
38 | |
39 | === added file '_clang-format' |
40 | --- _clang-format 1970-01-01 00:00:00 +0000 |
41 | +++ _clang-format 2016-03-04 11:34:19 +0000 |
42 | @@ -0,0 +1,48 @@ |
43 | +--- |
44 | +AccessModifierOffset: -4 |
45 | +AlignEscapedNewlinesLeft: true |
46 | +AlignTrailingComments: true |
47 | +AllowAllParametersOfDeclarationOnNextLine: true |
48 | +AllowShortFunctionsOnASingleLine: false |
49 | +AllowShortIfStatementsOnASingleLine: false |
50 | +AllowShortLoopsOnASingleLine: false |
51 | +AlwaysBreakBeforeMultilineStrings: true |
52 | +AlwaysBreakTemplateDeclarations: true |
53 | +BinPackParameters: false |
54 | +BreakBeforeBinaryOperators: false |
55 | +BreakBeforeBraces: Allman |
56 | +BreakBeforeTernaryOperators: false |
57 | +BreakConstructorInitializersBeforeComma: true |
58 | +ColumnLimit: 120 |
59 | +ConstructorInitializerAllOnOneLineOrOnePerLine: false |
60 | +ConstructorInitializerIndentWidth: 4 |
61 | +ContinuationIndentWidth: 4 |
62 | +Cpp11BracedListStyle: true |
63 | +DerivePointerBinding: true |
64 | +ExperimentalAutoDetectBinPacking: false |
65 | +IndentCaseLabels: true |
66 | +IndentFunctionDeclarationAfterType: true |
67 | +IndentWidth: 4 |
68 | +Language: Cpp |
69 | +MaxEmptyLinesToKeep: 1 |
70 | +NamespaceIndentation: None |
71 | +ObjCSpaceBeforeProtocolList: false |
72 | +PenaltyBreakBeforeFirstCallParameter: 1 |
73 | +PenaltyBreakComment: 60 |
74 | +PenaltyBreakFirstLessLess: 120 |
75 | +PenaltyBreakString: 1000 |
76 | +PenaltyExcessCharacter: 1000000 |
77 | +PenaltyReturnTypeOnItsOwnLine: 200 |
78 | +PointerBindsToType: true |
79 | +SpaceBeforeAssignmentOperators: true |
80 | +SpaceBeforeParens: ControlStatements |
81 | +SpaceInEmptyParentheses: false |
82 | +SpacesBeforeTrailingComments: 2 |
83 | +SpacesInAngles: false |
84 | +SpacesInCStyleCastParentheses: false |
85 | +SpacesInParentheses: false |
86 | +Standard: Cpp11 |
87 | +TabWidth: 8 |
88 | +UseTab: Never |
89 | +... |
90 | + |
91 | |
92 | === modified file 'debian/changelog' |
93 | --- debian/changelog 2015-08-17 21:38:34 +0000 |
94 | +++ debian/changelog 2016-03-04 11:34:19 +0000 |
95 | @@ -1,3 +1,16 @@ |
96 | +ubuntu-app-launch (0.9~smw2) UNRELEASED; urgency=medium |
97 | + |
98 | + [ Ted Gould ] |
99 | + * Add new C++ API that has a retained object for maintaining |
100 | + connections through multiple calls. Also provides for getting |
101 | + consistent application metadata. |
102 | + |
103 | + [ Stephen M. Webb ] |
104 | + * Fixed a coupla FTBFS and bumped version for Silo 58 testing. |
105 | + * Disabled a bunch of chronically-failing tests. |
106 | + |
107 | + -- Ted Gould <ted@ubuntu.com> Tue, 26 Jan 2016 15:22:06 -0600 |
108 | + |
109 | ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium |
110 | |
111 | [ CI Train Bot ] |
112 | |
113 | === modified file 'debian/control' |
114 | --- debian/control 2015-07-31 00:15:57 +0000 |
115 | +++ debian/control 2016-03-04 11:34:19 +0000 |
116 | @@ -15,10 +15,12 @@ |
117 | libglib2.0-dev, |
118 | libgtest-dev, |
119 | libjson-glib-dev, |
120 | + liblibertine-dev, |
121 | liblttng-ust-dev, |
122 | libmirclient-dev (>= 0.5), |
123 | libnih-dbus-dev, |
124 | libnih-dev, |
125 | + libproperties-cpp-dev, |
126 | libupstart-dev, |
127 | libzeitgeist-2.0-dev, |
128 | gobject-introspection, |
129 | |
130 | === added file 'debian/libubuntu-app-launch2.shlibs' |
131 | --- debian/libubuntu-app-launch2.shlibs 1970-01-01 00:00:00 +0000 |
132 | +++ debian/libubuntu-app-launch2.shlibs 2016-03-04 11:34:19 +0000 |
133 | @@ -0,0 +1,1 @@ |
134 | +libubuntu-app-launch 2 libubuntu-app-launch2 (>= 0.9) |
135 | |
136 | === removed file 'debian/libubuntu-app-launch2.symbols' |
137 | --- debian/libubuntu-app-launch2.symbols 2015-08-17 21:38:34 +0000 |
138 | +++ debian/libubuntu-app-launch2.symbols 1970-01-01 00:00:00 +0000 |
139 | @@ -1,41 +0,0 @@ |
140 | -libubuntu-app-launch.so.2 libubuntu-app-launch2 #MINVER# |
141 | - ubuntu_app_launch_app_id_parse@Base 0.4 |
142 | - ubuntu_app_launch_application_info@Base 0.5+15.10.20150817 |
143 | - ubuntu_app_launch_application_log_path@Base 0.4 |
144 | - ubuntu_app_launch_get_primary_pid@Base 0.4 |
145 | - ubuntu_app_launch_helper_set_exec@Base 0.5+15.10.20150604 |
146 | - ubuntu_app_launch_list_helper_instances@Base 0.4 |
147 | - ubuntu_app_launch_list_helpers@Base 0.4 |
148 | - ubuntu_app_launch_list_running_apps@Base 0.4 |
149 | - ubuntu_app_launch_observer_add_app_failed@Base 0.4 |
150 | - ubuntu_app_launch_observer_add_app_focus@Base 0.4 |
151 | - ubuntu_app_launch_observer_add_app_paused@Base 0.4+15.04.20150305.1 |
152 | - ubuntu_app_launch_observer_add_app_resume@Base 0.4 |
153 | - ubuntu_app_launch_observer_add_app_resumed@Base 0.4+15.04.20150305.1 |
154 | - ubuntu_app_launch_observer_add_app_started@Base 0.4 |
155 | - ubuntu_app_launch_observer_add_app_starting@Base 0.4 |
156 | - ubuntu_app_launch_observer_add_app_stop@Base 0.4 |
157 | - ubuntu_app_launch_observer_add_helper_started@Base 0.4 |
158 | - ubuntu_app_launch_observer_add_helper_stop@Base 0.4 |
159 | - ubuntu_app_launch_observer_delete_app_failed@Base 0.4 |
160 | - ubuntu_app_launch_observer_delete_app_focus@Base 0.4 |
161 | - ubuntu_app_launch_observer_delete_app_paused@Base 0.4+15.04.20150305.1 |
162 | - ubuntu_app_launch_observer_delete_app_resume@Base 0.4 |
163 | - ubuntu_app_launch_observer_delete_app_resumed@Base 0.4+15.04.20150305.1 |
164 | - ubuntu_app_launch_observer_delete_app_started@Base 0.4 |
165 | - ubuntu_app_launch_observer_delete_app_starting@Base 0.4 |
166 | - ubuntu_app_launch_observer_delete_app_stop@Base 0.4 |
167 | - ubuntu_app_launch_observer_delete_helper_started@Base 0.4 |
168 | - ubuntu_app_launch_observer_delete_helper_stop@Base 0.4 |
169 | - ubuntu_app_launch_pause_application@Base 0.4+14.10.20140915.3 |
170 | - ubuntu_app_launch_pid_in_app_id@Base 0.4 |
171 | - ubuntu_app_launch_resume_application@Base 0.4+14.10.20140915.3 |
172 | - ubuntu_app_launch_start_application@Base 0.4 |
173 | - ubuntu_app_launch_start_application_test@Base 0.4 |
174 | - ubuntu_app_launch_start_helper@Base 0.4 |
175 | - ubuntu_app_launch_start_multiple_helper@Base 0.4 |
176 | - ubuntu_app_launch_start_session_helper@Base 0.5+15.10.20150604 |
177 | - ubuntu_app_launch_stop_application@Base 0.4 |
178 | - ubuntu_app_launch_stop_helper@Base 0.4 |
179 | - ubuntu_app_launch_stop_multiple_helper@Base 0.4 |
180 | - ubuntu_app_launch_triplet_to_app_id@Base 0.4 |
181 | |
182 | === modified file 'debian/rules' |
183 | --- debian/rules 2014-11-21 16:43:12 +0000 |
184 | +++ debian/rules 2016-03-04 11:34:19 +0000 |
185 | @@ -1,9 +1,6 @@ |
186 | #!/usr/bin/make -f |
187 | # -*- makefile -*- |
188 | |
189 | -# Error on symbol errors |
190 | -export DPKG_GENSYMBOLS_CHECK_LEVEL=4 |
191 | - |
192 | # Get full logs in tests |
193 | export G_MESSAGES_DEBUG=all |
194 | |
195 | |
196 | === modified file 'helpers-shared.c' |
197 | --- helpers-shared.c 2015-07-15 02:32:54 +0000 |
198 | +++ helpers-shared.c 2016-03-04 11:34:19 +0000 |
199 | @@ -145,6 +145,8 @@ |
200 | cgroup_manager_connection_core_cb(g_dbus_connection_new_for_address_finish, res, (cgm_connection_t *)data); |
201 | } |
202 | |
203 | +G_DEFINE_QUARK(CGMANAGER_CONTEXT, cgmanager_context); |
204 | + |
205 | /* Get the connection to the cgroup manager */ |
206 | GDBusConnection * |
207 | cgroup_manager_connection (void) |
208 | @@ -191,7 +193,9 @@ |
209 | g_main_context_pop_thread_default(context); |
210 | |
211 | if (!use_session_bus && connection.con != NULL) { |
212 | - g_object_set_data(G_OBJECT(connection.con), "cgmanager-context", context); |
213 | + g_object_set_qdata(G_OBJECT(connection.con), |
214 | + cgmanager_context_quark(), |
215 | + context); |
216 | } else { |
217 | g_main_context_unref(context); |
218 | } |
219 | @@ -213,7 +217,7 @@ |
220 | if (cgmanager == NULL) |
221 | return; |
222 | |
223 | - GMainContext * creationcontext = g_object_get_data(G_OBJECT(cgmanager), "cgmanager-context"); |
224 | + GMainContext * creationcontext = g_object_get_qdata(G_OBJECT(cgmanager), cgmanager_context_quark()); |
225 | if (creationcontext == NULL) { |
226 | g_object_unref(cgmanager); |
227 | return; |
228 | @@ -228,6 +232,11 @@ |
229 | } |
230 | |
231 | g_object_unref(cgmanager); |
232 | + |
233 | + while (g_main_context_pending(creationcontext)) { |
234 | + g_main_context_iteration(creationcontext, TRUE /* may block */); |
235 | + } |
236 | + |
237 | g_main_context_unref(creationcontext); |
238 | } |
239 | |
240 | |
241 | === modified file 'libubuntu-app-launch/CMakeLists.txt' |
242 | --- libubuntu-app-launch/CMakeLists.txt 2015-08-11 19:11:15 +0000 |
243 | +++ libubuntu-app-launch/CMakeLists.txt 2016-03-04 11:34:19 +0000 |
244 | @@ -17,6 +17,7 @@ |
245 | add_lttng_gen_tp(NAME ubuntu-app-launch-trace) |
246 | |
247 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") |
248 | +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") |
249 | add_definitions ( -DOOM_HELPER="${pkglibexecdir}/oom-adjust-setuid-helper" -DDEMANGLER_PATH="${pkglibexecdir}/socket-demangler" ) |
250 | add_definitions ( -DLIBERTINE_LAUNCH="${CMAKE_INSTALL_FULL_BINDIR}/libertine-launch" ) |
251 | |
252 | @@ -24,6 +25,28 @@ |
253 | ubuntu-app-launch.h |
254 | ) |
255 | |
256 | +set(LAUNCHER_CPP_HEADERS |
257 | +appid.h |
258 | +application.h |
259 | +helper.h |
260 | +registry.h |
261 | +type-tagger.h |
262 | +) |
263 | + |
264 | +set(LAUNCHER_CPP_SOURCES |
265 | +application.cpp |
266 | +helper.cpp |
267 | +registry.cpp |
268 | +registry-impl.cpp |
269 | +application-impl-base.cpp |
270 | +application-impl-click.cpp |
271 | +application-impl-legacy.cpp |
272 | +application-impl-libertine.cpp |
273 | +application-info-desktop.cpp |
274 | +helper-impl-click.cpp |
275 | +glib-thread.cpp |
276 | +) |
277 | + |
278 | set(LAUNCHER_SOURCES |
279 | ubuntu-app-launch.c |
280 | second-exec-core.c |
281 | @@ -31,6 +54,11 @@ |
282 | desktop-exec.c |
283 | ubuntu-app-launch-trace.c |
284 | app-info.c |
285 | +${LAUNCHER_CPP_SOURCES} |
286 | +) |
287 | + |
288 | +add_custom_target(format |
289 | + COMMAND clang-format -i -style=file ${LAUNCHER_CPP_HEADERS} ${LAUNCHER_CPP_SOURCES} |
290 | ) |
291 | |
292 | set(LAUNCHER_GEN_SOURCES |
293 | @@ -59,6 +87,7 @@ |
294 | ${CLICK_LIBRARIES} |
295 | ${ZEITGEIST_LIBRARIES} |
296 | ${MIR_LIBRARIES} |
297 | + ${LIBERTINE_LIBRARIES} |
298 | helpers |
299 | -Wl,--no-undefined |
300 | ) |
301 | @@ -69,6 +98,11 @@ |
302 | ) |
303 | |
304 | install( |
305 | + FILES ${LAUNCHER_CPP_HEADERS} |
306 | + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libubuntu-app-launch-${API_VERSION}/ubuntu-app-launch" |
307 | +) |
308 | + |
309 | +install( |
310 | TARGETS ubuntu-launcher |
311 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} |
312 | ) |
313 | |
314 | === added file 'libubuntu-app-launch/appid.h' |
315 | --- libubuntu-app-launch/appid.h 1970-01-01 00:00:00 +0000 |
316 | +++ libubuntu-app-launch/appid.h 2016-03-04 11:34:19 +0000 |
317 | @@ -0,0 +1,83 @@ |
318 | +/* |
319 | + * Copyright © 2016 Canonical Ltd. |
320 | + * |
321 | + * This program is free software: you can redistribute it and/or modify it |
322 | + * under the terms of the GNU General Public License version 3, as published |
323 | + * by the Free Software Foundation. |
324 | + * |
325 | + * This program is distributed in the hope that it will be useful, but |
326 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
327 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
328 | + * PURPOSE. See the GNU General Public License for more details. |
329 | + * |
330 | + * You should have received a copy of the GNU General Public License along |
331 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
332 | + * |
333 | + * Authors: |
334 | + * Ted Gould <ted.gould@canonical.com> |
335 | + */ |
336 | + |
337 | +#include <string> |
338 | + |
339 | +#include "type-tagger.h" |
340 | + |
341 | +#pragma once |
342 | +#pragma GCC visibility push(default) |
343 | + |
344 | +namespace ubuntu |
345 | +{ |
346 | +namespace app_launch |
347 | +{ |
348 | + |
349 | +struct AppID |
350 | +{ |
351 | + struct PackageTag; |
352 | + struct AppNameTag; |
353 | + struct VersionTag; |
354 | + |
355 | + typedef TypeTagger<PackageTag, std::string> Package; |
356 | + typedef TypeTagger<AppNameTag, std::string> AppName; |
357 | + typedef TypeTagger<VersionTag, std::string> Version; |
358 | + |
359 | + Package package; |
360 | + AppName appname; |
361 | + Version version; |
362 | + |
363 | + operator std::string() const; |
364 | + |
365 | + AppID(); |
366 | + AppID(Package pkg, AppName app, Version ver); |
367 | + bool empty() const; |
368 | + |
369 | + static AppID parse(const std::string& appid); |
370 | + |
371 | + enum class ApplicationWildcard |
372 | + { |
373 | + FIRST_LISTED, |
374 | + LAST_LISTED, |
375 | + ONLY_LISTED, |
376 | + }; |
377 | + enum class VersionWildcard |
378 | + { |
379 | + CURRENT_USER_VERSION, |
380 | + }; |
381 | + |
382 | + static AppID discover(const std::string& package, |
383 | + ApplicationWildcard appwildcard = ApplicationWildcard::FIRST_LISTED, |
384 | + VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION); |
385 | + static AppID discover(const std::string& package, |
386 | + const std::string& appname, |
387 | + VersionWildcard versionwildcard = VersionWildcard::CURRENT_USER_VERSION); |
388 | + static AppID discover(const std::string& package, const std::string& appname, const std::string& version); |
389 | + |
390 | + static AppID find(const std::string& sappid); |
391 | + static bool valid(const std::string& sappid); |
392 | +}; |
393 | + |
394 | +bool operator==(const AppID& a, const AppID& b); |
395 | +bool operator!=(const AppID& a, const AppID& b); |
396 | + |
397 | +}; // namespace app_launch |
398 | +}; // namespace ubuntu |
399 | + |
400 | +#pragma GCC visibility pop |
401 | |
402 | === added file 'libubuntu-app-launch/application-impl-base.cpp' |
403 | --- libubuntu-app-launch/application-impl-base.cpp 1970-01-01 00:00:00 +0000 |
404 | +++ libubuntu-app-launch/application-impl-base.cpp 2016-03-04 11:34:19 +0000 |
405 | @@ -0,0 +1,162 @@ |
406 | +/* |
407 | + * Copyright © 2016 Canonical Ltd. |
408 | + * |
409 | + * This program is free software: you can redistribute it and/or modify it |
410 | + * under the terms of the GNU General Public License version 3, as published |
411 | + * by the Free Software Foundation. |
412 | + * |
413 | + * This program is distributed in the hope that it will be useful, but |
414 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
415 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
416 | + * PURPOSE. See the GNU General Public License for more details. |
417 | + * |
418 | + * You should have received a copy of the GNU General Public License along |
419 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
420 | + * |
421 | + * Authors: |
422 | + * Ted Gould <ted.gould@canonical.com> |
423 | + */ |
424 | + |
425 | +#include "application-impl-base.h" |
426 | + |
427 | +namespace ubuntu |
428 | +{ |
429 | +namespace app_launch |
430 | +{ |
431 | +namespace app_impls |
432 | +{ |
433 | + |
434 | +Base::Base(const std::shared_ptr<Registry>& registry) |
435 | + : _registry(registry) |
436 | +{ |
437 | +} |
438 | + |
439 | +bool Base::hasInstances() |
440 | +{ |
441 | + std::string sappid = appId(); |
442 | + return ubuntu_app_launch_get_primary_pid(sappid.c_str()) != 0; |
443 | +} |
444 | + |
445 | +class BaseInstance : public Application::Instance |
446 | +{ |
447 | +public: |
448 | + explicit BaseInstance(const std::string& appId); |
449 | + |
450 | + /* Query lifecycle */ |
451 | + bool isRunning() override |
452 | + { |
453 | + return ubuntu_app_launch_get_primary_pid(_appId.c_str()) != 0; |
454 | + } |
455 | + pid_t primaryPid() override |
456 | + { |
457 | + return ubuntu_app_launch_get_primary_pid(_appId.c_str()); |
458 | + } |
459 | + bool hasPid(pid_t pid) override |
460 | + { |
461 | + return ubuntu_app_launch_pid_in_app_id(pid, _appId.c_str()) == TRUE; |
462 | + } |
463 | + std::string logPath() override |
464 | + { |
465 | + auto cpath = ubuntu_app_launch_application_log_path(_appId.c_str()); |
466 | + if (cpath != nullptr) |
467 | + { |
468 | + std::string retval(cpath); |
469 | + g_free(cpath); |
470 | + return retval; |
471 | + } |
472 | + else |
473 | + { |
474 | + return {}; |
475 | + } |
476 | + } |
477 | + std::vector<pid_t> pids() override |
478 | + { |
479 | + std::vector<pid_t> vector; |
480 | + GList* list = ubuntu_app_launch_get_pids(_appId.c_str()); |
481 | + |
482 | + for (GList* pntr = list; pntr != nullptr; pntr = g_list_next(pntr)) |
483 | + { |
484 | + vector.push_back(static_cast<pid_t>(GPOINTER_TO_INT(list->data))); |
485 | + } |
486 | + |
487 | + g_list_free(list); |
488 | + |
489 | + return vector; |
490 | + } |
491 | + |
492 | + /* Manage lifecycle */ |
493 | + void pause() override |
494 | + { |
495 | + ubuntu_app_launch_pause_application(_appId.c_str()); |
496 | + } |
497 | + void resume() override |
498 | + { |
499 | + ubuntu_app_launch_resume_application(_appId.c_str()); |
500 | + } |
501 | + void stop() override |
502 | + { |
503 | + ubuntu_app_launch_stop_application(_appId.c_str()); |
504 | + } |
505 | + |
506 | +private: |
507 | + std::string _appId; |
508 | +}; |
509 | + |
510 | +BaseInstance::BaseInstance(const std::string& appId) |
511 | + : _appId(appId) |
512 | +{ |
513 | +} |
514 | + |
515 | +std::vector<std::shared_ptr<Application::Instance>> Base::instances() |
516 | +{ |
517 | + std::vector<std::shared_ptr<Instance>> vect; |
518 | + vect.emplace_back(std::make_shared<BaseInstance>(appId())); |
519 | + return vect; |
520 | +} |
521 | + |
522 | +std::shared_ptr<gchar*> urlsToStrv(std::vector<Application::URL> urls) |
523 | +{ |
524 | + auto array = g_array_new(TRUE, FALSE, sizeof(gchar*)); |
525 | + |
526 | + for (auto url : urls) |
527 | + { |
528 | + auto str = g_strdup(url.value().c_str()); |
529 | + g_array_append_val(array, str); |
530 | + } |
531 | + |
532 | + return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev); |
533 | +} |
534 | + |
535 | +std::shared_ptr<Application::Instance> Base::launch(const std::vector<Application::URL>& urls) |
536 | +{ |
537 | + std::string appIdStr = appId(); |
538 | + std::shared_ptr<gchar*> urlstrv; |
539 | + |
540 | + if (urls.size() > 0) |
541 | + { |
542 | + urlstrv = urlsToStrv(urls); |
543 | + } |
544 | + |
545 | + ubuntu_app_launch_start_application(appIdStr.c_str(), urlstrv.get()); |
546 | + |
547 | + return std::make_shared<BaseInstance>(appIdStr); |
548 | +} |
549 | + |
550 | +std::shared_ptr<Application::Instance> Base::launchTest(const std::vector<Application::URL>& urls) |
551 | +{ |
552 | + std::string appIdStr = appId(); |
553 | + std::shared_ptr<gchar*> urlstrv; |
554 | + |
555 | + if (urls.size() > 0) |
556 | + { |
557 | + urlstrv = urlsToStrv(urls); |
558 | + } |
559 | + |
560 | + ubuntu_app_launch_start_application_test(appIdStr.c_str(), urlstrv.get()); |
561 | + |
562 | + return std::make_shared<BaseInstance>(appIdStr); |
563 | +} |
564 | + |
565 | +}; // namespace app_impls |
566 | +}; // namespace app_launch |
567 | +}; // namespace ubuntu |
568 | |
569 | === added file 'libubuntu-app-launch/application-impl-base.h' |
570 | --- libubuntu-app-launch/application-impl-base.h 1970-01-01 00:00:00 +0000 |
571 | +++ libubuntu-app-launch/application-impl-base.h 2016-03-04 11:34:19 +0000 |
572 | @@ -0,0 +1,52 @@ |
573 | +/* |
574 | + * Copyright © 2016 Canonical Ltd. |
575 | + * |
576 | + * This program is free software: you can redistribute it and/or modify it |
577 | + * under the terms of the GNU General Public License version 3, as published |
578 | + * by the Free Software Foundation. |
579 | + * |
580 | + * This program is distributed in the hope that it will be useful, but |
581 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
582 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
583 | + * PURPOSE. See the GNU General Public License for more details. |
584 | + * |
585 | + * You should have received a copy of the GNU General Public License along |
586 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
587 | + * |
588 | + * Authors: |
589 | + * Ted Gould <ted.gould@canonical.com> |
590 | + */ |
591 | + |
592 | +#include "application.h" |
593 | + |
594 | +extern "C" { |
595 | +#include "ubuntu-app-launch.h" |
596 | +} |
597 | + |
598 | +#pragma once |
599 | + |
600 | +namespace ubuntu |
601 | +{ |
602 | +namespace app_launch |
603 | +{ |
604 | +namespace app_impls |
605 | +{ |
606 | + |
607 | +class Base : public ubuntu::app_launch::Application |
608 | +{ |
609 | +public: |
610 | + Base(const std::shared_ptr<Registry> ®istry); |
611 | + |
612 | + bool hasInstances() override; |
613 | + std::vector<std::shared_ptr<Instance>> instances() override; |
614 | + |
615 | + std::shared_ptr<Instance> launch(const std::vector<Application::URL> &urls = {}) override; |
616 | + std::shared_ptr<Instance> launchTest(const std::vector<Application::URL> &urls = {}) override; |
617 | + |
618 | +protected: |
619 | + std::shared_ptr<Registry> _registry; |
620 | +}; |
621 | + |
622 | +}; // namespace app_impls |
623 | +}; // namespace app_launch |
624 | +}; // namespace ubuntu |
625 | |
626 | === added file 'libubuntu-app-launch/application-impl-click.cpp' |
627 | --- libubuntu-app-launch/application-impl-click.cpp 1970-01-01 00:00:00 +0000 |
628 | +++ libubuntu-app-launch/application-impl-click.cpp 2016-03-04 11:34:19 +0000 |
629 | @@ -0,0 +1,157 @@ |
630 | +/* |
631 | + * Copyright © 2016 Canonical Ltd. |
632 | + * |
633 | + * This program is free software: you can redistribute it and/or modify it |
634 | + * under the terms of the GNU General Public License version 3, as published |
635 | + * by the Free Software Foundation. |
636 | + * |
637 | + * This program is distributed in the hope that it will be useful, but |
638 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
639 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
640 | + * PURPOSE. See the GNU General Public License for more details. |
641 | + * |
642 | + * You should have received a copy of the GNU General Public License along |
643 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
644 | + * |
645 | + * Authors: |
646 | + * Ted Gould <ted.gould@canonical.com> |
647 | + */ |
648 | + |
649 | +#include "application-impl-click.h" |
650 | +#include "application-info-desktop.h" |
651 | +#include "registry-impl.h" |
652 | + |
653 | +namespace ubuntu |
654 | +{ |
655 | +namespace app_launch |
656 | +{ |
657 | +namespace app_impls |
658 | +{ |
659 | + |
660 | +AppID::Version manifestVersion(const std::shared_ptr<JsonObject>& manifest); |
661 | +std::list<AppID::AppName> manifestApps(const std::shared_ptr<JsonObject>& manifest); |
662 | +std::shared_ptr<GKeyFile> manifestAppDesktop(const std::shared_ptr<JsonObject>& manifest, |
663 | + const std::string& app, |
664 | + const std::string& clickDir); |
665 | + |
666 | +Click::Click(const AppID& appid, const std::shared_ptr<Registry>& registry) |
667 | + : Click(appid, registry->impl->getClickManifest(appid.package), registry) |
668 | +{ |
669 | +} |
670 | + |
671 | +Click::Click(const AppID& appid, const std::shared_ptr<JsonObject>& manifest, const std::shared_ptr<Registry>& registry) |
672 | + : Base(registry) |
673 | + , _appid(appid) |
674 | + , _manifest(manifest) |
675 | + , _clickDir(registry->impl->getClickDir(appid.package)) |
676 | + , _keyfile(manifestAppDesktop(manifest, appid.appname, _clickDir)) |
677 | +{ |
678 | + if (!_keyfile) |
679 | + throw std::runtime_error{"No keyfile found for click application: " + (std::string)appid}; |
680 | +} |
681 | + |
682 | +AppID Click::appId() |
683 | +{ |
684 | + return _appid; |
685 | +} |
686 | + |
687 | +std::shared_ptr<Application::Info> Click::info() |
688 | +{ |
689 | + return std::make_shared<app_info::Desktop>(_keyfile, _clickDir); |
690 | +} |
691 | + |
692 | +AppID::Version manifestVersion(const std::shared_ptr<JsonObject>& manifest) |
693 | +{ |
694 | + auto cstr = json_object_get_string_member(manifest.get(), "version"); |
695 | + |
696 | + if (cstr == nullptr) |
697 | + throw std::runtime_error("Unable to find version number in manifest"); |
698 | + |
699 | + auto cppstr = AppID::Version::from_raw((const gchar*)cstr); |
700 | + return cppstr; |
701 | +} |
702 | + |
703 | +std::list<AppID::AppName> manifestApps(const std::shared_ptr<JsonObject>& manifest) |
704 | +{ |
705 | + auto hooks = json_object_get_object_member(manifest.get(), "hooks"); |
706 | + if (hooks == nullptr) |
707 | + throw std::runtime_error("Manifest for application does not have a 'hooks' field"); |
708 | + |
709 | + auto gapps = json_object_get_members(hooks); |
710 | + if (gapps == nullptr) |
711 | + throw std::runtime_error("GLib JSON confusion, please talk to your library vendor"); |
712 | + |
713 | + std::list<AppID::AppName> apps; |
714 | + |
715 | + for (GList* item = gapps; item != nullptr; item = g_list_next(item)) |
716 | + { |
717 | + auto appname = (const gchar*)item->data; |
718 | + |
719 | + auto hooklist = json_object_get_object_member(hooks, appname); |
720 | + |
721 | + if (json_object_has_member(hooklist, "desktop") == TRUE) |
722 | + { |
723 | + apps.emplace_back(AppID::AppName::from_raw(appname)); |
724 | + } |
725 | + } |
726 | + |
727 | + g_list_free_full(gapps, g_free); |
728 | + return apps; |
729 | +} |
730 | + |
731 | +std::shared_ptr<GKeyFile> manifestAppDesktop(const std::shared_ptr<JsonObject>& manifest, |
732 | + const std::string& app, |
733 | + const std::string& clickDir) |
734 | +{ |
735 | + auto hooks = json_object_get_object_member(manifest.get(), "hooks"); |
736 | + if (hooks == nullptr) |
737 | + throw std::runtime_error("Manifest for application '" + app + "' does not have a 'hooks' field"); |
738 | + |
739 | + auto gapps = json_object_get_members(hooks); |
740 | + if (gapps == nullptr) |
741 | + throw std::runtime_error("GLib JSON confusion, please talk to your library vendor"); |
742 | + |
743 | + auto hooklist = json_object_get_object_member(hooks, app.c_str()); |
744 | + if (hooklist == nullptr) |
745 | + throw std::runtime_error("Manifest for does not have an application '" + app + "'"); |
746 | + |
747 | + auto desktoppath = json_object_get_string_member(hooklist, "desktop"); |
748 | + if (desktoppath == nullptr) |
749 | + throw std::runtime_error("Manifest for application '" + app + "' does not have a 'desktop' hook"); |
750 | + |
751 | + auto path = std::shared_ptr<gchar>(g_build_filename(clickDir.c_str(), desktoppath, nullptr), g_free); |
752 | + |
753 | + std::shared_ptr<GKeyFile> keyfile(g_key_file_new(), g_key_file_free); |
754 | + GError* error = nullptr; |
755 | + g_key_file_load_from_file(keyfile.get(), path.get(), G_KEY_FILE_NONE, &error); |
756 | + if (error != nullptr) |
757 | + { |
758 | + auto perror = std::shared_ptr<GError>(error, g_error_free); |
759 | + throw std::runtime_error(perror.get()->message); |
760 | + } |
761 | + |
762 | + return keyfile; |
763 | +} |
764 | + |
765 | +std::list<std::shared_ptr<Application>> Click::list(const std::shared_ptr<Registry>& registry) |
766 | +{ |
767 | + std::list<std::shared_ptr<Application>> applist; |
768 | + |
769 | + for (auto pkg : registry->impl->getClickPackages()) |
770 | + { |
771 | + auto manifest = registry->impl->getClickManifest(pkg); |
772 | + |
773 | + for (auto appname : manifestApps(manifest)) |
774 | + { |
775 | + AppID appid{package : pkg, appname : appname, version : manifestVersion(manifest)}; |
776 | + auto app = std::make_shared<Click>(appid, manifest, registry); |
777 | + applist.push_back(app); |
778 | + } |
779 | + } |
780 | + |
781 | + return applist; |
782 | +} |
783 | + |
784 | +}; // namespace app_impls |
785 | +}; // namespace app_launch |
786 | +}; // namespace ubuntu |
787 | |
788 | === added file 'libubuntu-app-launch/application-impl-click.h' |
789 | --- libubuntu-app-launch/application-impl-click.h 1970-01-01 00:00:00 +0000 |
790 | +++ libubuntu-app-launch/application-impl-click.h 2016-03-04 11:34:19 +0000 |
791 | @@ -0,0 +1,56 @@ |
792 | +/* |
793 | + * Copyright © 2016 Canonical Ltd. |
794 | + * |
795 | + * This program is free software: you can redistribute it and/or modify it |
796 | + * under the terms of the GNU General Public License version 3, as published |
797 | + * by the Free Software Foundation. |
798 | + * |
799 | + * This program is distributed in the hope that it will be useful, but |
800 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
801 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
802 | + * PURPOSE. See the GNU General Public License for more details. |
803 | + * |
804 | + * You should have received a copy of the GNU General Public License along |
805 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
806 | + * |
807 | + * Authors: |
808 | + * Ted Gould <ted.gould@canonical.com> |
809 | + */ |
810 | + |
811 | +#include <gio/gdesktopappinfo.h> |
812 | +#include <json-glib/json-glib.h> |
813 | +#include "application-impl-base.h" |
814 | + |
815 | +#pragma once |
816 | + |
817 | +namespace ubuntu |
818 | +{ |
819 | +namespace app_launch |
820 | +{ |
821 | +namespace app_impls |
822 | +{ |
823 | + |
824 | +class Click : public Base |
825 | +{ |
826 | +public: |
827 | + Click(const AppID& appid, const std::shared_ptr<Registry>& registry); |
828 | + Click(const AppID& appid, const std::shared_ptr<JsonObject>& manifest, const std::shared_ptr<Registry>& registry); |
829 | + |
830 | + static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry>& registry); |
831 | + |
832 | + AppID appId() override; |
833 | + |
834 | + std::shared_ptr<Info> info() override; |
835 | + |
836 | +private: |
837 | + AppID _appid; |
838 | + |
839 | + std::shared_ptr<JsonObject> _manifest; |
840 | + |
841 | + std::string _clickDir; |
842 | + std::shared_ptr<GKeyFile> _keyfile; |
843 | +}; |
844 | + |
845 | +}; // namespace app_impls |
846 | +}; // namespace app_launch |
847 | +}; // namespace ubuntu |
848 | |
849 | === added file 'libubuntu-app-launch/application-impl-legacy.cpp' |
850 | --- libubuntu-app-launch/application-impl-legacy.cpp 1970-01-01 00:00:00 +0000 |
851 | +++ libubuntu-app-launch/application-impl-legacy.cpp 2016-03-04 11:34:19 +0000 |
852 | @@ -0,0 +1,128 @@ |
853 | +/* |
854 | + * Copyright © 2016 Canonical Ltd. |
855 | + * |
856 | + * This program is free software: you can redistribute it and/or modify it |
857 | + * under the terms of the GNU General Public License version 3, as published |
858 | + * by the Free Software Foundation. |
859 | + * |
860 | + * This program is distributed in the hope that it will be useful, but |
861 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
862 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
863 | + * PURPOSE. See the GNU General Public License for more details. |
864 | + * |
865 | + * You should have received a copy of the GNU General Public License along |
866 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
867 | + * |
868 | + * Authors: |
869 | + * Ted Gould <ted.gould@canonical.com> |
870 | + */ |
871 | + |
872 | +#include "application-impl-legacy.h" |
873 | +#include "application-info-desktop.h" |
874 | + |
875 | +namespace ubuntu |
876 | +{ |
877 | +namespace app_launch |
878 | +{ |
879 | +namespace app_impls |
880 | +{ |
881 | + |
882 | +std::shared_ptr<GKeyFile> keyfileForApp(const AppID::AppName& name); |
883 | + |
884 | +void clear_keyfile(GKeyFile* keyfile) |
885 | +{ |
886 | + if (keyfile != nullptr) |
887 | + { |
888 | + g_key_file_free(keyfile); |
889 | + } |
890 | +} |
891 | + |
892 | +Legacy::Legacy(const AppID::AppName& appname, |
893 | + const std::shared_ptr<GKeyFile>& keyfile, |
894 | + const std::shared_ptr<Registry>& registry) |
895 | + : Base(registry) |
896 | + , _appname(appname) |
897 | + , _keyfile(keyfile) |
898 | +{ |
899 | + if (!_keyfile) |
900 | + throw std::runtime_error{"Unable to find keyfile for legacy application: " + appname.value()}; |
901 | +} |
902 | + |
903 | +Legacy::Legacy(const AppID::AppName& appname, const std::shared_ptr<Registry>& registry) |
904 | + : Legacy(appname, keyfileForApp(appname), registry) |
905 | +{ |
906 | +} |
907 | + |
908 | +std::shared_ptr<GKeyFile> keyfileForApp(const AppID::AppName& name) |
909 | +{ |
910 | + std::string desktopName = name.value() + ".desktop"; |
911 | + auto keyfilecheck = [desktopName](const gchar* dir) -> std::shared_ptr<GKeyFile> { |
912 | + auto fullname = g_build_filename(dir, "applications", desktopName.c_str(), nullptr); |
913 | + if (!g_file_test(fullname, G_FILE_TEST_EXISTS)) |
914 | + { |
915 | + g_free(fullname); |
916 | + return {}; |
917 | + } |
918 | + |
919 | + auto keyfile = std::shared_ptr<GKeyFile>(g_key_file_new(), clear_keyfile); |
920 | + |
921 | + GError* error = nullptr; |
922 | + g_key_file_load_from_file(keyfile.get(), fullname, G_KEY_FILE_NONE, &error); |
923 | + g_free(fullname); |
924 | + |
925 | + if (error != nullptr) |
926 | + { |
927 | + g_debug("Unable to load keyfile '%s' becuase: %s", desktopName.c_str(), error->message); |
928 | + g_error_free(error); |
929 | + return {}; |
930 | + } |
931 | + |
932 | + return keyfile; |
933 | + }; |
934 | + |
935 | + auto retval = keyfilecheck(g_get_user_data_dir()); |
936 | + |
937 | + auto systemDirs = g_get_system_data_dirs(); |
938 | + for (auto i = 0; !retval && systemDirs[i] != nullptr; i++) |
939 | + { |
940 | + retval = keyfilecheck(systemDirs[i]); |
941 | + } |
942 | + |
943 | + return retval; |
944 | +} |
945 | + |
946 | +std::shared_ptr<Application::Info> Legacy::info() |
947 | +{ |
948 | + return std::make_shared<app_info::Desktop>(_keyfile, "/usr/share/icons/"); |
949 | +} |
950 | + |
951 | +std::list<std::shared_ptr<Application>> Legacy::list(const std::shared_ptr<Registry>& registry) |
952 | +{ |
953 | + std::list<std::shared_ptr<Application>> list; |
954 | + GList* head = g_app_info_get_all(); |
955 | + for (GList* item = head; item != nullptr; item = g_list_next(item)) |
956 | + { |
957 | + GDesktopAppInfo* appinfo = G_DESKTOP_APP_INFO(item->data); |
958 | + |
959 | + if (appinfo == nullptr) |
960 | + { |
961 | + continue; |
962 | + } |
963 | + |
964 | + if (g_app_info_should_show(G_APP_INFO(appinfo)) == FALSE) |
965 | + { |
966 | + continue; |
967 | + } |
968 | + |
969 | + auto app = std::make_shared<Legacy>(AppID::AppName::from_raw(g_app_info_get_id(G_APP_INFO(appinfo))), registry); |
970 | + list.push_back(app); |
971 | + } |
972 | + |
973 | + g_list_free_full(head, g_object_unref); |
974 | + |
975 | + return list; |
976 | +} |
977 | + |
978 | +}; // namespace app_impls |
979 | +}; // namespace app_launch |
980 | +}; // namespace ubuntu |
981 | |
982 | === added file 'libubuntu-app-launch/application-impl-legacy.h' |
983 | --- libubuntu-app-launch/application-impl-legacy.h 1970-01-01 00:00:00 +0000 |
984 | +++ libubuntu-app-launch/application-impl-legacy.h 2016-03-04 11:34:19 +0000 |
985 | @@ -0,0 +1,57 @@ |
986 | +/* |
987 | + * Copyright © 2016 Canonical Ltd. |
988 | + * |
989 | + * This program is free software: you can redistribute it and/or modify it |
990 | + * under the terms of the GNU General Public License version 3, as published |
991 | + * by the Free Software Foundation. |
992 | + * |
993 | + * This program is distributed in the hope that it will be useful, but |
994 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
995 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
996 | + * PURPOSE. See the GNU General Public License for more details. |
997 | + * |
998 | + * You should have received a copy of the GNU General Public License along |
999 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1000 | + * |
1001 | + * Authors: |
1002 | + * Ted Gould <ted.gould@canonical.com> |
1003 | + */ |
1004 | + |
1005 | +#include <gio/gdesktopappinfo.h> |
1006 | + |
1007 | +#include "application-impl-base.h" |
1008 | + |
1009 | +#pragma once |
1010 | + |
1011 | +namespace ubuntu |
1012 | +{ |
1013 | +namespace app_launch |
1014 | +{ |
1015 | +namespace app_impls |
1016 | +{ |
1017 | + |
1018 | +class Legacy : public Base |
1019 | +{ |
1020 | +public: |
1021 | + Legacy(const AppID::AppName& appname, const std::shared_ptr<Registry>& registry); |
1022 | + Legacy(const AppID::AppName& appname, |
1023 | + const std::shared_ptr<GKeyFile>& keyfile, |
1024 | + const std::shared_ptr<Registry>& registry); |
1025 | + |
1026 | + AppID appId() override |
1027 | + { |
1028 | + return {package : AppID::Package::from_raw({}), appname : _appname, version : AppID::Version::from_raw({})}; |
1029 | + } |
1030 | + |
1031 | + std::shared_ptr<Info> info() override; |
1032 | + |
1033 | + static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry> ®istry); |
1034 | + |
1035 | +private: |
1036 | + AppID::AppName _appname; |
1037 | + std::shared_ptr<GKeyFile> _keyfile; |
1038 | +}; |
1039 | + |
1040 | +}; // namespace app_impls |
1041 | +}; // namespace app_launch |
1042 | +}; // namespace ubuntu |
1043 | |
1044 | === added file 'libubuntu-app-launch/application-impl-libertine.cpp' |
1045 | --- libubuntu-app-launch/application-impl-libertine.cpp 1970-01-01 00:00:00 +0000 |
1046 | +++ libubuntu-app-launch/application-impl-libertine.cpp 2016-03-04 11:34:19 +0000 |
1047 | @@ -0,0 +1,127 @@ |
1048 | +/* |
1049 | + * Copyright © 2016 Canonical Ltd. |
1050 | + * |
1051 | + * This program is free software: you can redistribute it and/or modify it |
1052 | + * under the terms of the GNU General Public License version 3, as published |
1053 | + * by the Free Software Foundation. |
1054 | + * |
1055 | + * This program is distributed in the hope that it will be useful, but |
1056 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1057 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1058 | + * PURPOSE. See the GNU General Public License for more details. |
1059 | + * |
1060 | + * You should have received a copy of the GNU General Public License along |
1061 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1062 | + * |
1063 | + * Authors: |
1064 | + * Ted Gould <ted.gould@canonical.com> |
1065 | + */ |
1066 | + |
1067 | +#include "application-impl-libertine.h" |
1068 | +#include "application-info-desktop.h" |
1069 | +#include "libertine.h" |
1070 | + |
1071 | +namespace ubuntu |
1072 | +{ |
1073 | +namespace app_launch |
1074 | +{ |
1075 | +namespace app_impls |
1076 | +{ |
1077 | + |
1078 | +std::shared_ptr<GKeyFile> keyfileFromPath(const gchar* pathname); |
1079 | + |
1080 | +Libertine::Libertine(const AppID::Package& container, |
1081 | + const AppID::AppName& appname, |
1082 | + const std::shared_ptr<Registry>& registry) |
1083 | + : Base(registry) |
1084 | + , _container(container) |
1085 | + , _appname(appname) |
1086 | +{ |
1087 | + if (!_keyfile) |
1088 | + { |
1089 | + auto container_path = libertine_container_path(container.value().c_str()); |
1090 | + auto container_app_path = g_build_filename(container_path, "usr", "share", "applications", |
1091 | + (appname.value() + ".desktop").c_str(), NULL); |
1092 | + |
1093 | + _keyfile = keyfileFromPath(container_app_path); |
1094 | + |
1095 | + g_free(container_app_path); |
1096 | + g_free(container_path); |
1097 | + } |
1098 | + |
1099 | + if (!_keyfile) |
1100 | + { |
1101 | + auto home_path = libertine_container_home_path(container.value().c_str()); |
1102 | + auto home_app_path = g_build_filename(home_path, ".local", "share", "applications", |
1103 | + (appname.value() + ".desktop").c_str(), NULL); |
1104 | + |
1105 | + _keyfile = keyfileFromPath(home_app_path); |
1106 | + |
1107 | + g_free(home_app_path); |
1108 | + g_free(home_path); |
1109 | + } |
1110 | + |
1111 | + if (!_keyfile) |
1112 | + throw std::runtime_error{"Unable to find a keyfile for application '" + appname.value() + "' in container '" + |
1113 | + container.value() + "'"}; |
1114 | +} |
1115 | + |
1116 | +std::shared_ptr<GKeyFile> keyfileFromPath(const gchar* pathname) |
1117 | +{ |
1118 | + if (!g_file_test(pathname, G_FILE_TEST_EXISTS)) |
1119 | + { |
1120 | + return {}; |
1121 | + } |
1122 | + |
1123 | + std::shared_ptr<GKeyFile> keyfile(g_key_file_new(), [](GKeyFile* keyfile) { |
1124 | + if (keyfile != nullptr) |
1125 | + { |
1126 | + g_key_file_free(keyfile); |
1127 | + } |
1128 | + }); |
1129 | + GError* error = nullptr; |
1130 | + |
1131 | + g_key_file_load_from_file(keyfile.get(), pathname, G_KEY_FILE_NONE, &error); |
1132 | + |
1133 | + if (error != nullptr) |
1134 | + { |
1135 | + g_error_free(error); |
1136 | + return {}; |
1137 | + } |
1138 | + |
1139 | + return keyfile; |
1140 | +} |
1141 | + |
1142 | +std::list<std::shared_ptr<Application>> Libertine::list(const std::shared_ptr<Registry>& registry) |
1143 | +{ |
1144 | + std::list<std::shared_ptr<Application>> applist; |
1145 | + |
1146 | + auto containers = libertine_list_containers(); |
1147 | + |
1148 | + for (int i = 0; containers[i] != nullptr; i++) |
1149 | + { |
1150 | + auto container = containers[i]; |
1151 | + auto apps = libertine_list_apps_for_container(container); |
1152 | + |
1153 | + for (int i = 0; apps[i] != nullptr; i++) |
1154 | + { |
1155 | + auto sapp = std::make_shared<Libertine>(AppID::Package::from_raw(container), |
1156 | + AppID::AppName::from_raw(apps[i]), registry); |
1157 | + applist.push_back(sapp); |
1158 | + } |
1159 | + |
1160 | + g_strfreev(apps); |
1161 | + } |
1162 | + g_strfreev(containers); |
1163 | + |
1164 | + return applist; |
1165 | +} |
1166 | + |
1167 | +std::shared_ptr<Application::Info> Libertine::info() |
1168 | +{ |
1169 | + return std::make_shared<app_info::Desktop>(_keyfile, libertine_container_path(_container.value().c_str())); |
1170 | +} |
1171 | + |
1172 | +}; // namespace app_impls |
1173 | +}; // namespace app_launch |
1174 | +}; // namespace ubuntu |
1175 | |
1176 | === added file 'libubuntu-app-launch/application-impl-libertine.h' |
1177 | --- libubuntu-app-launch/application-impl-libertine.h 1970-01-01 00:00:00 +0000 |
1178 | +++ libubuntu-app-launch/application-impl-libertine.h 2016-03-04 11:34:19 +0000 |
1179 | @@ -0,0 +1,56 @@ |
1180 | +/* |
1181 | + * Copyright © 2016 Canonical Ltd. |
1182 | + * |
1183 | + * This program is free software: you can redistribute it and/or modify it |
1184 | + * under the terms of the GNU General Public License version 3, as published |
1185 | + * by the Free Software Foundation. |
1186 | + * |
1187 | + * This program is distributed in the hope that it will be useful, but |
1188 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1189 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1190 | + * PURPOSE. See the GNU General Public License for more details. |
1191 | + * |
1192 | + * You should have received a copy of the GNU General Public License along |
1193 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1194 | + * |
1195 | + * Authors: |
1196 | + * Ted Gould <ted.gould@canonical.com> |
1197 | + */ |
1198 | + |
1199 | +#include "application-impl-base.h" |
1200 | +#include <gio/gdesktopappinfo.h> |
1201 | + |
1202 | +#pragma once |
1203 | + |
1204 | +namespace ubuntu |
1205 | +{ |
1206 | +namespace app_launch |
1207 | +{ |
1208 | +namespace app_impls |
1209 | +{ |
1210 | + |
1211 | +class Libertine : public Base |
1212 | +{ |
1213 | +public: |
1214 | + Libertine(const AppID::Package& container, |
1215 | + const AppID::AppName& appname, |
1216 | + const std::shared_ptr<Registry>& registry); |
1217 | + |
1218 | + static std::list<std::shared_ptr<Application>> list(const std::shared_ptr<Registry> ®istry); |
1219 | + |
1220 | + AppID appId() override |
1221 | + { |
1222 | + return {package : _container, appname : _appname, version : AppID::Version::from_raw("0.0")}; |
1223 | + } |
1224 | + |
1225 | + std::shared_ptr<Info> info() override; |
1226 | + |
1227 | +private: |
1228 | + AppID::Package _container; |
1229 | + AppID::AppName _appname; |
1230 | + std::shared_ptr<GKeyFile> _keyfile; |
1231 | +}; |
1232 | + |
1233 | +}; // namespace app_impls |
1234 | +}; // namespace app_launch |
1235 | +}; // namespace ubuntu |
1236 | |
1237 | === added file 'libubuntu-app-launch/application-info-desktop.cpp' |
1238 | --- libubuntu-app-launch/application-info-desktop.cpp 1970-01-01 00:00:00 +0000 |
1239 | +++ libubuntu-app-launch/application-info-desktop.cpp 2016-03-04 11:34:19 +0000 |
1240 | @@ -0,0 +1,202 @@ |
1241 | +/* |
1242 | + * Copyright © 2016 Canonical Ltd. |
1243 | + * |
1244 | + * This program is free software: you can redistribute it and/or modify it |
1245 | + * under the terms of the GNU General Public License version 3, as published |
1246 | + * by the Free Software Foundation. |
1247 | + * |
1248 | + * This program is distributed in the hope that it will be useful, but |
1249 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1250 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1251 | + * PURPOSE. See the GNU General Public License for more details. |
1252 | + * |
1253 | + * You should have received a copy of the GNU General Public License along |
1254 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1255 | + * |
1256 | + * Authors: |
1257 | + * Ted Gould <ted.gould@canonical.com> |
1258 | + */ |
1259 | + |
1260 | +#include "application-info-desktop.h" |
1261 | + |
1262 | +namespace ubuntu |
1263 | +{ |
1264 | +namespace app_launch |
1265 | +{ |
1266 | +namespace app_info |
1267 | +{ |
1268 | + |
1269 | +constexpr const char* DESKTOP_GROUP = "Desktop Entry"; |
1270 | + |
1271 | +template <typename T> |
1272 | +auto stringFromKeyfile(std::shared_ptr<GKeyFile> keyfile, const std::string& key, const std::string& exceptionText = {}) |
1273 | + -> T |
1274 | +{ |
1275 | + GError* error = nullptr; |
1276 | + auto keyval = g_key_file_get_locale_string(keyfile.get(), DESKTOP_GROUP, key.c_str(), nullptr, &error); |
1277 | + |
1278 | + if (error != nullptr) |
1279 | + { |
1280 | + auto perror = std::shared_ptr<GError>(error, g_error_free); |
1281 | + if (!exceptionText.empty()) |
1282 | + { |
1283 | + throw std::runtime_error(exceptionText + perror.get()->message); |
1284 | + } |
1285 | + |
1286 | + return T::from_raw({}); |
1287 | + } |
1288 | + |
1289 | + T retval = T::from_raw(keyval); |
1290 | + g_free(keyval); |
1291 | + return retval; |
1292 | +} |
1293 | + |
1294 | +template <typename T> |
1295 | +auto fileFromKeyfile(std::shared_ptr<GKeyFile> keyfile, |
1296 | + const std::string basePath, |
1297 | + const std::string& key, |
1298 | + const std::string& exceptionText = {}) -> T |
1299 | +{ |
1300 | + GError* error = nullptr; |
1301 | + auto keyval = g_key_file_get_locale_string(keyfile.get(), DESKTOP_GROUP, key.c_str(), nullptr, &error); |
1302 | + |
1303 | + if (error != nullptr) |
1304 | + { |
1305 | + auto perror = std::shared_ptr<GError>(error, g_error_free); |
1306 | + if (!exceptionText.empty()) |
1307 | + { |
1308 | + throw std::runtime_error(exceptionText + perror.get()->message); |
1309 | + } |
1310 | + |
1311 | + return T::from_raw({}); |
1312 | + } |
1313 | + |
1314 | + /* If we're already an absolute path, don't prepend the base path */ |
1315 | + if (keyval[0] == '/') |
1316 | + { |
1317 | + T retval = T::from_raw(keyval); |
1318 | + g_free(keyval); |
1319 | + return retval; |
1320 | + } |
1321 | + |
1322 | + auto cpath = g_build_filename(basePath.c_str(), keyval, nullptr); |
1323 | + |
1324 | + T retval = T::from_raw(cpath); |
1325 | + |
1326 | + g_free(keyval); |
1327 | + g_free(cpath); |
1328 | + |
1329 | + return retval; |
1330 | +} |
1331 | + |
1332 | +template <typename T> |
1333 | +auto boolFromKeyfile(std::shared_ptr<GKeyFile> keyfile, |
1334 | + const std::string& key, |
1335 | + bool defaultReturn, |
1336 | + const std::string& exceptionText = {}) -> T |
1337 | +{ |
1338 | + GError* error = nullptr; |
1339 | + auto keyval = g_key_file_get_boolean(keyfile.get(), DESKTOP_GROUP, key.c_str(), &error); |
1340 | + |
1341 | + if (error != nullptr) |
1342 | + { |
1343 | + auto perror = std::shared_ptr<GError>(error, g_error_free); |
1344 | + if (!exceptionText.empty()) |
1345 | + { |
1346 | + throw std::runtime_error(exceptionText + perror.get()->message); |
1347 | + } |
1348 | + |
1349 | + return T::from_raw(defaultReturn); |
1350 | + } |
1351 | + |
1352 | + T retval = T::from_raw(keyval == TRUE); |
1353 | + return retval; |
1354 | +} |
1355 | + |
1356 | +Desktop::Desktop(std::shared_ptr<GKeyFile> keyfile, const std::string& basePath) |
1357 | + : _keyfile([keyfile]() { |
1358 | + if (!keyfile) |
1359 | + { |
1360 | + throw std::runtime_error("Can not build a desktop application info object with a null keyfile"); |
1361 | + } |
1362 | + return keyfile; |
1363 | + }()) |
1364 | + , _basePath(basePath) |
1365 | + , _name(stringFromKeyfile<Application::Info::Name>(keyfile, "Name", "Unable to get name from keyfile")) |
1366 | + , _description(stringFromKeyfile<Application::Info::Description>(keyfile, "Comment")) |
1367 | + , _iconPath( |
1368 | + fileFromKeyfile<Application::Info::IconPath>(keyfile, basePath, "Icon", "Missing icon for desktop file")) |
1369 | + , _splashInfo({ |
1370 | + title : stringFromKeyfile<Application::Info::Splash::Title>(keyfile, "X-Ubuntu-Splash-Title"), |
1371 | + image : fileFromKeyfile<Application::Info::Splash::Image>(keyfile, basePath, "X-Ubuntu-Splash-Image"), |
1372 | + backgroundColor : stringFromKeyfile<Application::Info::Splash::Color>(keyfile, "X-Ubuntu-Splash-Color"), |
1373 | + headerColor : stringFromKeyfile<Application::Info::Splash::Color>(keyfile, "X-Ubuntu-Splash-Color-Header"), |
1374 | + footerColor : stringFromKeyfile<Application::Info::Splash::Color>(keyfile, "X-Ubuntu-Splash-Color-Footer"), |
1375 | + showHeader : |
1376 | + boolFromKeyfile<Application::Info::Splash::ShowHeader>(keyfile, "X-Ubuntu-Splash-Show-Header", false) |
1377 | + }) |
1378 | + , _supportedOrientations([keyfile]() { |
1379 | + Orientations all = {portrait : true, landscape : true, invertedPortrait : true, invertedLandscape : true}; |
1380 | + |
1381 | + GError* error = nullptr; |
1382 | + auto orientationStrv = g_key_file_get_string_list(keyfile.get(), DESKTOP_GROUP, |
1383 | + "X-Ubuntu-Supported-Orientations", nullptr, &error); |
1384 | + |
1385 | + if (error != nullptr) |
1386 | + { |
1387 | + g_error_free(error); |
1388 | + return all; |
1389 | + } |
1390 | + |
1391 | + Orientations retval = |
1392 | + {portrait : false, landscape : false, invertedPortrait : false, invertedLandscape : false}; |
1393 | + |
1394 | + try |
1395 | + { |
1396 | + for (auto i = 0; orientationStrv[i] != nullptr; i++) |
1397 | + { |
1398 | + g_strstrip(orientationStrv[i]); /* remove whitespace */ |
1399 | + |
1400 | + if (g_ascii_strcasecmp("portrait", orientationStrv[i]) == 0) |
1401 | + { |
1402 | + retval.portrait = true; |
1403 | + } |
1404 | + else if (g_ascii_strcasecmp("landscape", orientationStrv[i]) == 0) |
1405 | + { |
1406 | + retval.landscape = true; |
1407 | + } |
1408 | + else if (g_ascii_strcasecmp("invertedPortrait", orientationStrv[i]) == 0) |
1409 | + { |
1410 | + retval.invertedPortrait = true; |
1411 | + } |
1412 | + else if (g_ascii_strcasecmp("invertedLandscape", orientationStrv[i]) == 0) |
1413 | + { |
1414 | + retval.invertedLandscape = true; |
1415 | + } |
1416 | + else if (g_ascii_strcasecmp("primary", orientationStrv[i]) == 0 && i == 0) |
1417 | + { |
1418 | + /* Pass, we'll let primary be the first entry, it should be the only. */ |
1419 | + } |
1420 | + else |
1421 | + { |
1422 | + throw std::runtime_error("Invalid orientation string '" + std::string(orientationStrv[i]) + "'"); |
1423 | + } |
1424 | + } |
1425 | + } |
1426 | + catch (...) |
1427 | + { |
1428 | + retval = all; |
1429 | + } |
1430 | + |
1431 | + g_strfreev(orientationStrv); |
1432 | + return retval; |
1433 | + }()) |
1434 | + , _rotatesWindow( |
1435 | + boolFromKeyfile<Application::Info::RotatesWindow>(keyfile, "X-Ubuntu-Rotates-Window-Content", false)) |
1436 | + , _ubuntuLifecycle(boolFromKeyfile<Application::Info::UbuntuLifecycle>(keyfile, "X-Ubuntu-Touch", false)) |
1437 | +{ |
1438 | +} |
1439 | + |
1440 | +}; // namespace app_info |
1441 | +}; // namespace app_launch |
1442 | +}; // namespace ubuntu |
1443 | |
1444 | === added file 'libubuntu-app-launch/application-info-desktop.h' |
1445 | --- libubuntu-app-launch/application-info-desktop.h 1970-01-01 00:00:00 +0000 |
1446 | +++ libubuntu-app-launch/application-info-desktop.h 2016-03-04 11:34:19 +0000 |
1447 | @@ -0,0 +1,87 @@ |
1448 | +/* |
1449 | + * Copyright © 2016 Canonical Ltd. |
1450 | + * |
1451 | + * This program is free software: you can redistribute it and/or modify it |
1452 | + * under the terms of the GNU General Public License version 3, as published |
1453 | + * by the Free Software Foundation. |
1454 | + * |
1455 | + * This program is distributed in the hope that it will be useful, but |
1456 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1457 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1458 | + * PURPOSE. See the GNU General Public License for more details. |
1459 | + * |
1460 | + * You should have received a copy of the GNU General Public License along |
1461 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1462 | + * |
1463 | + * Authors: |
1464 | + * Ted Gould <ted.gould@canonical.com> |
1465 | + */ |
1466 | + |
1467 | +#include "application.h" |
1468 | +#include <glib.h> |
1469 | +#include <mutex> |
1470 | + |
1471 | +#pragma once |
1472 | + |
1473 | +namespace ubuntu |
1474 | +{ |
1475 | +namespace app_launch |
1476 | +{ |
1477 | +namespace app_info |
1478 | +{ |
1479 | + |
1480 | +class Desktop : public Application::Info |
1481 | +{ |
1482 | +public: |
1483 | + Desktop(std::shared_ptr<GKeyFile> keyfile, const std::string& basePath); |
1484 | + |
1485 | + const Application::Info::Name& name() override |
1486 | + { |
1487 | + return _name; |
1488 | + } |
1489 | + const Application::Info::Description& description() override |
1490 | + { |
1491 | + return _description; |
1492 | + } |
1493 | + const Application::Info::IconPath& iconPath() override |
1494 | + { |
1495 | + return _iconPath; |
1496 | + } |
1497 | + |
1498 | + Application::Info::Splash splash() override |
1499 | + { |
1500 | + return _splashInfo; |
1501 | + } |
1502 | + |
1503 | + Application::Info::Orientations supportedOrientations() override |
1504 | + { |
1505 | + return _supportedOrientations; |
1506 | + } |
1507 | + |
1508 | + Application::Info::RotatesWindow rotatesWindowContents() override |
1509 | + { |
1510 | + return _rotatesWindow; |
1511 | + } |
1512 | + |
1513 | + Application::Info::UbuntuLifecycle supportsUbuntuLifecycle() override |
1514 | + { |
1515 | + return _ubuntuLifecycle; |
1516 | + } |
1517 | + |
1518 | +private: |
1519 | + std::shared_ptr<GKeyFile> _keyfile; |
1520 | + std::string _basePath; |
1521 | + |
1522 | + Application::Info::Name _name; |
1523 | + Application::Info::Description _description; |
1524 | + Application::Info::IconPath _iconPath; |
1525 | + |
1526 | + Application::Info::Splash _splashInfo; |
1527 | + Application::Info::Orientations _supportedOrientations; |
1528 | + Application::Info::RotatesWindow _rotatesWindow; |
1529 | + Application::Info::UbuntuLifecycle _ubuntuLifecycle; |
1530 | +}; |
1531 | + |
1532 | +}; // namespace AppInfo |
1533 | +}; // namespace AppLaunch |
1534 | +}; // namespace Ubuntu |
1535 | |
1536 | === added file 'libubuntu-app-launch/application.cpp' |
1537 | --- libubuntu-app-launch/application.cpp 1970-01-01 00:00:00 +0000 |
1538 | +++ libubuntu-app-launch/application.cpp 2016-03-04 11:34:19 +0000 |
1539 | @@ -0,0 +1,225 @@ |
1540 | +/* |
1541 | + * Copyright © 2016 Canonical Ltd. |
1542 | + * |
1543 | + * This program is free software: you can redistribute it and/or modify it |
1544 | + * under the terms of the GNU General Public License version 3, as published |
1545 | + * by the Free Software Foundation. |
1546 | + * |
1547 | + * This program is distributed in the hope that it will be useful, but |
1548 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1549 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1550 | + * PURPOSE. See the GNU General Public License for more details. |
1551 | + * |
1552 | + * You should have received a copy of the GNU General Public License along |
1553 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1554 | + * |
1555 | + * Authors: |
1556 | + * Ted Gould <ted.gould@canonical.com> |
1557 | + */ |
1558 | + |
1559 | +extern "C" { |
1560 | +#include "app-info.h" |
1561 | +#include "ubuntu-app-launch.h" |
1562 | +} |
1563 | + |
1564 | +#include "application-impl-click.h" |
1565 | +#include "application-impl-legacy.h" |
1566 | +#include "application-impl-libertine.h" |
1567 | +#include "application.h" |
1568 | + |
1569 | +#include <iostream> |
1570 | +#include <regex> |
1571 | + |
1572 | +namespace ubuntu |
1573 | +{ |
1574 | +namespace app_launch |
1575 | +{ |
1576 | + |
1577 | +std::shared_ptr<Application> Application::create(const AppID& appid, const std::shared_ptr<Registry>& registry) |
1578 | +{ |
1579 | + if (appid.empty()) |
1580 | + { |
1581 | + throw std::runtime_error("AppID is empty"); |
1582 | + } |
1583 | + |
1584 | + std::string sappid = appid; |
1585 | + if (app_info_click(sappid.c_str(), NULL, NULL)) |
1586 | + { |
1587 | + return std::make_shared<app_impls::Click>(appid, registry); |
1588 | + } |
1589 | + else if (app_info_libertine(sappid.c_str(), NULL, NULL)) |
1590 | + { |
1591 | + return std::make_shared<app_impls::Libertine>(appid.package, appid.appname, registry); |
1592 | + } |
1593 | + else if (app_info_legacy(sappid.c_str(), NULL, NULL)) |
1594 | + { |
1595 | + return std::make_shared<app_impls::Legacy>(appid.appname, registry); |
1596 | + } |
1597 | + else |
1598 | + { |
1599 | + throw std::runtime_error("Invalid app ID: " + sappid); |
1600 | + } |
1601 | +} |
1602 | + |
1603 | +AppID::AppID() |
1604 | + : package(Package::from_raw({})) |
1605 | + , appname(AppName::from_raw({})) |
1606 | + , version(Version::from_raw({})) |
1607 | +{ |
1608 | +} |
1609 | + |
1610 | +AppID::AppID(Package pkg, AppName app, Version ver) |
1611 | + : package(pkg) |
1612 | + , appname(app) |
1613 | + , version(ver) |
1614 | +{ |
1615 | +} |
1616 | + |
1617 | +/* These are the Regex's taken from the Click Reviewer Tools |
1618 | + on Jan 16, 2016 revision 566 */ |
1619 | +#define REGEX_PKGNAME "([a-z0-9][a-z0-9+.-]+)" |
1620 | +#define REGEX_APPNAME "([A-Za-z0-9+-.:~-]+)" |
1621 | +#define REGEX_VERSION "([\\d+:]?[A-Za-z0-9.+:~-]+?[-[A-Za-z0-9+.~]+]?)" |
1622 | + |
1623 | +const std::regex full_appid_regex("^" REGEX_PKGNAME "_" REGEX_APPNAME "_" REGEX_VERSION "$"); |
1624 | +const std::regex short_appid_regex("^" REGEX_PKGNAME "_" REGEX_APPNAME "$"); |
1625 | +const std::regex legacy_appid_regex("^" REGEX_APPNAME "$"); |
1626 | + |
1627 | +AppID AppID::parse(const std::string& sappid) |
1628 | +{ |
1629 | + std::smatch match; |
1630 | + |
1631 | + if (std::regex_match(sappid, match, full_appid_regex)) |
1632 | + { |
1633 | + return {AppID::Package::from_raw(match[1].str()), AppID::AppName::from_raw(match[2].str()), |
1634 | + AppID::Version::from_raw(match[3].str())}; |
1635 | + } |
1636 | + else |
1637 | + { |
1638 | + /* Allow returning an empty AppID with empty internal */ |
1639 | + return {AppID::Package::from_raw({}), AppID::AppName::from_raw({}), AppID::Version::from_raw({})}; |
1640 | + } |
1641 | +} |
1642 | + |
1643 | +bool AppID::valid(const std::string& sappid) |
1644 | +{ |
1645 | + return std::regex_match(sappid, full_appid_regex); |
1646 | +} |
1647 | + |
1648 | +AppID AppID::find(const std::string& sappid) |
1649 | +{ |
1650 | + std::smatch match; |
1651 | + |
1652 | + if (std::regex_match(sappid, match, full_appid_regex)) |
1653 | + { |
1654 | + return {AppID::Package::from_raw(match[1].str()), AppID::AppName::from_raw(match[2].str()), |
1655 | + AppID::Version::from_raw(match[3].str())}; |
1656 | + } |
1657 | + else if (std::regex_match(sappid, match, short_appid_regex)) |
1658 | + { |
1659 | + return discover(match[1].str(), match[2].str()); |
1660 | + } |
1661 | + else if (std::regex_match(sappid, match, legacy_appid_regex)) |
1662 | + { |
1663 | + return {AppID::Package::from_raw({}), AppID::AppName::from_raw(sappid), AppID::Version::from_raw({})}; |
1664 | + } |
1665 | + else |
1666 | + { |
1667 | + return {AppID::Package::from_raw({}), AppID::AppName::from_raw({}), AppID::Version::from_raw({})}; |
1668 | + } |
1669 | +} |
1670 | + |
1671 | +AppID::operator std::string() const |
1672 | +{ |
1673 | + if (package.value().empty() && version.value().empty()) |
1674 | + { |
1675 | + if (appname.value().empty()) |
1676 | + { |
1677 | + return {}; |
1678 | + } |
1679 | + else |
1680 | + { |
1681 | + return appname.value(); |
1682 | + } |
1683 | + } |
1684 | + |
1685 | + return package.value() + "_" + appname.value() + "_" + version.value(); |
1686 | +} |
1687 | + |
1688 | +bool operator==(const AppID& a, const AppID& b) |
1689 | +{ |
1690 | + return a.package.value() == b.package.value() && a.appname.value() == b.appname.value() && |
1691 | + a.version.value() == b.version.value(); |
1692 | +} |
1693 | + |
1694 | +bool operator!=(const AppID& a, const AppID& b) |
1695 | +{ |
1696 | + return a.package.value() != b.package.value() || a.appname.value() != b.appname.value() || |
1697 | + a.version.value() != b.version.value(); |
1698 | +} |
1699 | + |
1700 | +bool AppID::empty() const |
1701 | +{ |
1702 | + return package.value().empty() && appname.value().empty() && version.value().empty(); |
1703 | +} |
1704 | + |
1705 | +std::string app_wildcard(AppID::ApplicationWildcard card) |
1706 | +{ |
1707 | + switch (card) |
1708 | + { |
1709 | + case AppID::ApplicationWildcard::FIRST_LISTED: |
1710 | + return "first-listed-app"; |
1711 | + case AppID::ApplicationWildcard::LAST_LISTED: |
1712 | + return "last-listed-app"; |
1713 | + case AppID::ApplicationWildcard::ONLY_LISTED: |
1714 | + return "only-listed-app"; |
1715 | + } |
1716 | + |
1717 | + return ""; |
1718 | +} |
1719 | + |
1720 | +std::string ver_wildcard(AppID::VersionWildcard card) |
1721 | +{ |
1722 | + switch (card) |
1723 | + { |
1724 | + case AppID::VersionWildcard::CURRENT_USER_VERSION: |
1725 | + return "current-user-version"; |
1726 | + } |
1727 | + |
1728 | + return ""; |
1729 | +} |
1730 | + |
1731 | +AppID AppID::discover(const std::string& package, const std::string& appname, const std::string& version) |
1732 | +{ |
1733 | + auto cappid = ubuntu_app_launch_triplet_to_app_id(package.c_str(), appname.c_str(), version.c_str()); |
1734 | + |
1735 | + auto appid = cappid != nullptr ? AppID::parse(cappid) : AppID::parse(""); |
1736 | + |
1737 | + g_free(cappid); |
1738 | + |
1739 | + return appid; |
1740 | +} |
1741 | + |
1742 | +AppID AppID::discover(const std::string& package, ApplicationWildcard appwildcard, VersionWildcard versionwildcard) |
1743 | +{ |
1744 | + return discover(package, app_wildcard(appwildcard), ver_wildcard(versionwildcard)); |
1745 | +} |
1746 | + |
1747 | +AppID AppID::discover(const std::string& package, const std::string& appname, VersionWildcard versionwildcard) |
1748 | +{ |
1749 | + auto appid = discover(package, appname, ver_wildcard(versionwildcard)); |
1750 | + |
1751 | + if (appid.empty()) |
1752 | + { |
1753 | + /* If we weren't able to go that route, we can see if it's libertine */ |
1754 | + if (app_info_libertine((package + "_" + appname + "_0.0").c_str(), nullptr, nullptr)) |
1755 | + { |
1756 | + appid = AppID(Package::from_raw(package), AppName::from_raw(appname), Version::from_raw("0.0")); |
1757 | + } |
1758 | + } |
1759 | + |
1760 | + return appid; |
1761 | +} |
1762 | + |
1763 | +}; // namespace app_launch |
1764 | +}; // namespace ubuntu |
1765 | |
1766 | === added file 'libubuntu-app-launch/application.h' |
1767 | --- libubuntu-app-launch/application.h 1970-01-01 00:00:00 +0000 |
1768 | +++ libubuntu-app-launch/application.h 2016-03-04 11:34:19 +0000 |
1769 | @@ -0,0 +1,150 @@ |
1770 | +/* |
1771 | + * Copyright © 2016 Canonical Ltd. |
1772 | + * |
1773 | + * This program is free software: you can redistribute it and/or modify it |
1774 | + * under the terms of the GNU General Public License version 3, as published |
1775 | + * by the Free Software Foundation. |
1776 | + * |
1777 | + * This program is distributed in the hope that it will be useful, but |
1778 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1779 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1780 | + * PURPOSE. See the GNU General Public License for more details. |
1781 | + * |
1782 | + * You should have received a copy of the GNU General Public License along |
1783 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1784 | + * |
1785 | + * Authors: |
1786 | + * Ted Gould <ted.gould@canonical.com> |
1787 | + */ |
1788 | + |
1789 | +#include <list> |
1790 | +#include <memory> |
1791 | +#include <sys/types.h> |
1792 | +#include <vector> |
1793 | + |
1794 | +#include "appid.h" |
1795 | +#include "type-tagger.h" |
1796 | + |
1797 | +#pragma once |
1798 | +#pragma GCC visibility push(default) |
1799 | + |
1800 | +namespace ubuntu |
1801 | +{ |
1802 | +namespace app_launch |
1803 | +{ |
1804 | + |
1805 | +class Registry; |
1806 | + |
1807 | +class Application |
1808 | +{ |
1809 | +public: |
1810 | + struct URLTag; |
1811 | + typedef TypeTagger<URLTag, std::string> URL; |
1812 | + |
1813 | + static std::shared_ptr<Application> create(const AppID& appid, const std::shared_ptr<Registry>& registry); |
1814 | + |
1815 | + /* System level info */ |
1816 | + virtual AppID appId() = 0; |
1817 | + |
1818 | + class Info |
1819 | + { |
1820 | + public: |
1821 | + /* Basic information */ |
1822 | + struct NameTag; |
1823 | + struct DescriptionTag; |
1824 | + struct IconPathTag; |
1825 | + |
1826 | + typedef TypeTagger<NameTag, std::string> Name; |
1827 | + typedef TypeTagger<DescriptionTag, std::string> Description; |
1828 | + typedef TypeTagger<IconPathTag, std::string> IconPath; |
1829 | + |
1830 | + virtual const Name& name() = 0; |
1831 | + virtual const Description& description() = 0; |
1832 | + virtual const IconPath& iconPath() = 0; |
1833 | + |
1834 | + /* Splash information */ |
1835 | + struct Splash |
1836 | + { |
1837 | + struct TitleTag; |
1838 | + struct ImageTag; |
1839 | + struct ColorTag; |
1840 | + struct ShowHeaderTag; |
1841 | + |
1842 | + typedef TypeTagger<TitleTag, std::string> Title; |
1843 | + typedef TypeTagger<ImageTag, std::string> Image; |
1844 | + typedef TypeTagger<ColorTag, std::string> Color; |
1845 | + typedef TypeTagger<ShowHeaderTag, bool> ShowHeader; |
1846 | + |
1847 | + Title title; |
1848 | + Image image; |
1849 | + Color backgroundColor; |
1850 | + Color headerColor; |
1851 | + Color footerColor; |
1852 | + ShowHeader showHeader; |
1853 | + }; |
1854 | + |
1855 | + virtual Splash splash() = 0; |
1856 | + |
1857 | + /* Orientation and placement */ |
1858 | + struct Orientations |
1859 | + { |
1860 | + bool portrait; |
1861 | + bool landscape; |
1862 | + bool invertedPortrait; |
1863 | + bool invertedLandscape; |
1864 | + |
1865 | + bool operator==(const Orientations& b) const |
1866 | + { |
1867 | + return portrait == b.portrait && landscape == b.landscape && invertedPortrait == b.invertedPortrait && |
1868 | + invertedLandscape == b.invertedLandscape; |
1869 | + } |
1870 | + }; |
1871 | + |
1872 | + struct RotatesWindowTag; |
1873 | + |
1874 | + typedef TypeTagger<RotatesWindowTag, bool> RotatesWindow; |
1875 | + |
1876 | + virtual Orientations supportedOrientations() = 0; |
1877 | + virtual RotatesWindow rotatesWindowContents() = 0; |
1878 | + |
1879 | + /* Lifecycle */ |
1880 | + struct UbuntuLifecycleTag; |
1881 | + |
1882 | + typedef TypeTagger<UbuntuLifecycleTag, bool> UbuntuLifecycle; |
1883 | + |
1884 | + virtual UbuntuLifecycle supportsUbuntuLifecycle() = 0; |
1885 | + }; |
1886 | + |
1887 | + virtual std::shared_ptr<Info> info() = 0; |
1888 | + |
1889 | + class Instance |
1890 | + { |
1891 | + public: |
1892 | + /* Query lifecycle */ |
1893 | + virtual bool isRunning() = 0; |
1894 | + |
1895 | + /* Instance Info */ |
1896 | + virtual std::string logPath() = 0; |
1897 | + |
1898 | + /* PIDs */ |
1899 | + virtual pid_t primaryPid() = 0; |
1900 | + virtual bool hasPid(pid_t pid) = 0; |
1901 | + virtual std::vector<pid_t> pids() = 0; |
1902 | + |
1903 | + /* Manage lifecycle */ |
1904 | + virtual void pause() = 0; |
1905 | + virtual void resume() = 0; |
1906 | + virtual void stop() = 0; |
1907 | + }; |
1908 | + |
1909 | + virtual bool hasInstances() = 0; |
1910 | + virtual std::vector<std::shared_ptr<Instance>> instances() = 0; |
1911 | + |
1912 | + virtual std::shared_ptr<Instance> launch(const std::vector<URL>& urls = {}) = 0; |
1913 | + virtual std::shared_ptr<Instance> launchTest(const std::vector<URL>& urls = {}) = 0; |
1914 | +}; |
1915 | + |
1916 | +}; // namespace app_launch |
1917 | +}; // namespace ubuntu |
1918 | + |
1919 | +#pragma GCC visibility pop |
1920 | |
1921 | === modified file 'libubuntu-app-launch/desktop-exec.c' |
1922 | --- libubuntu-app-launch/desktop-exec.c 2015-08-12 02:52:05 +0000 |
1923 | +++ libubuntu-app-launch/desktop-exec.c 2016-03-04 11:34:19 +0000 |
1924 | @@ -186,7 +186,7 @@ |
1925 | /* This string is quoted using desktop file quoting: |
1926 | http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables */ |
1927 | gchar * execline = desktop_to_exec(keyfile, app_id); |
1928 | - g_return_val_if_fail(execline != NULL, 1); |
1929 | + g_return_val_if_fail(execline != NULL, FALSE); |
1930 | |
1931 | if (is_libertine) { |
1932 | static const gchar * libertine_launch = NULL; |
1933 | |
1934 | === added file 'libubuntu-app-launch/glib-thread.cpp' |
1935 | --- libubuntu-app-launch/glib-thread.cpp 1970-01-01 00:00:00 +0000 |
1936 | +++ libubuntu-app-launch/glib-thread.cpp 2016-03-04 11:34:19 +0000 |
1937 | @@ -0,0 +1,156 @@ |
1938 | +/* |
1939 | + * Copyright © 2015 Canonical Ltd. |
1940 | + * |
1941 | + * This program is free software: you can redistribute it and/or modify it |
1942 | + * under the terms of the GNU General Public License version 3, as published |
1943 | + * by the Free Software Foundation. |
1944 | + * |
1945 | + * This program is distributed in the hope that it will be useful, but |
1946 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1947 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1948 | + * PURPOSE. See the GNU General Public License for more details. |
1949 | + * |
1950 | + * You should have received a copy of the GNU General Public License along |
1951 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1952 | + * |
1953 | + * Authors: |
1954 | + * Ted Gould <ted.gould@canonical.com> |
1955 | + */ |
1956 | + |
1957 | +#include "glib-thread.h" |
1958 | + |
1959 | +namespace GLib |
1960 | +{ |
1961 | + |
1962 | +ContextThread::ContextThread(std::function<void()> beforeLoop, std::function<void()> afterLoop) |
1963 | +{ |
1964 | + _cancel = std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable* cancel) { |
1965 | + if (cancel != nullptr) |
1966 | + { |
1967 | + g_cancellable_cancel(cancel); |
1968 | + g_object_unref(cancel); |
1969 | + } |
1970 | + }); |
1971 | + std::promise<std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>> context_promise; |
1972 | + |
1973 | + /* NOTE: We copy afterLoop but reference beforeLoop. We're blocking so we |
1974 | + know that beforeLoop will stay valid long enough, but we can't say the |
1975 | + same for afterLoop */ |
1976 | + _thread = std::thread([&context_promise, &beforeLoop, afterLoop, this]() { |
1977 | + /* Build up the context and loop for the async events and a place |
1978 | + for GDBus to send its events back to */ |
1979 | + auto context = std::shared_ptr<GMainContext>( |
1980 | + g_main_context_new(), [](GMainContext* context) { g_clear_pointer(&context, g_main_context_unref); }); |
1981 | + auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(context.get(), FALSE), |
1982 | + [](GMainLoop* loop) { g_clear_pointer(&loop, g_main_loop_unref); }); |
1983 | + |
1984 | + g_main_context_push_thread_default(context.get()); |
1985 | + |
1986 | + beforeLoop(); |
1987 | + |
1988 | + /* Free's the constructor to continue */ |
1989 | + auto pair = std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>(context, loop); |
1990 | + context_promise.set_value(pair); |
1991 | + |
1992 | + if (!g_cancellable_is_cancelled(_cancel.get())) |
1993 | + { |
1994 | + g_main_loop_run(loop.get()); |
1995 | + } |
1996 | + |
1997 | + afterLoop(); |
1998 | + }); |
1999 | + |
2000 | + /* We need to have the context and the mainloop ready before |
2001 | + other functions on this object can work properly. So we wait |
2002 | + for them and set them on this thread. */ |
2003 | + auto context_future = context_promise.get_future(); |
2004 | + context_future.wait(); |
2005 | + auto context_value = context_future.get(); |
2006 | + |
2007 | + _context = context_value.first; |
2008 | + _loop = context_value.second; |
2009 | + |
2010 | + if (!_context || !_loop) |
2011 | + { |
2012 | + throw std::runtime_error("Unable to create GLib Thread"); |
2013 | + } |
2014 | +} |
2015 | + |
2016 | +ContextThread::~ContextThread() |
2017 | +{ |
2018 | + quit(); |
2019 | +} |
2020 | + |
2021 | +void ContextThread::quit() |
2022 | +{ |
2023 | + g_cancellable_cancel(_cancel.get()); /* Force the cancellation on ongoing tasks */ |
2024 | + if (_loop) |
2025 | + { |
2026 | + g_main_loop_quit(_loop.get()); /* Quit the loop */ |
2027 | + } |
2028 | + |
2029 | + /* Joining here because we want to ensure that the final afterLoop() |
2030 | + function is run before returning */ |
2031 | + if (std::this_thread::get_id() != _thread.get_id()) |
2032 | + { |
2033 | + if (_thread.joinable()) |
2034 | + { |
2035 | + _thread.join(); |
2036 | + } |
2037 | + } |
2038 | +} |
2039 | + |
2040 | +bool ContextThread::isCancelled() |
2041 | +{ |
2042 | + return g_cancellable_is_cancelled(_cancel.get()) == TRUE; |
2043 | +} |
2044 | + |
2045 | +std::shared_ptr<GCancellable> ContextThread::getCancellable() |
2046 | +{ |
2047 | + return _cancel; |
2048 | +} |
2049 | + |
2050 | +void ContextThread::simpleSource(std::function<GSource*()> srcBuilder, std::function<void()> work) |
2051 | +{ |
2052 | + if (isCancelled()) |
2053 | + { |
2054 | + throw std::runtime_error("Trying to execute work on a GLib thread that is shutting down."); |
2055 | + } |
2056 | + |
2057 | + /* Copy the work so that we can reuse it */ |
2058 | + /* Lifecycle is handled with the source pointer when we attach |
2059 | + it to the context. */ |
2060 | + auto heapWork = new std::function<void()>(work); |
2061 | + |
2062 | + auto source = std::shared_ptr<GSource>(srcBuilder(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); }); |
2063 | + g_source_set_callback(source.get(), |
2064 | + [](gpointer data) { |
2065 | + auto heapWork = static_cast<std::function<void()>*>(data); |
2066 | + (*heapWork)(); |
2067 | + return G_SOURCE_REMOVE; |
2068 | + }, |
2069 | + heapWork, |
2070 | + [](gpointer data) { |
2071 | + auto heapWork = static_cast<std::function<void()>*>(data); |
2072 | + delete heapWork; |
2073 | + }); |
2074 | + |
2075 | + g_source_attach(source.get(), _context.get()); |
2076 | +} |
2077 | + |
2078 | +void ContextThread::executeOnThread(std::function<void()> work) |
2079 | +{ |
2080 | + simpleSource(g_idle_source_new, work); |
2081 | +} |
2082 | + |
2083 | +void ContextThread::timeout(const std::chrono::milliseconds& length, std::function<void()> work) |
2084 | +{ |
2085 | + simpleSource([length]() { return g_timeout_source_new(length.count()); }, work); |
2086 | +} |
2087 | + |
2088 | +void ContextThread::timeoutSeconds(const std::chrono::seconds& length, std::function<void()> work) |
2089 | +{ |
2090 | + simpleSource([length]() { return g_timeout_source_new_seconds(length.count()); }, work); |
2091 | +} |
2092 | + |
2093 | +} // ns GLib |
2094 | |
2095 | === added file 'libubuntu-app-launch/glib-thread.h' |
2096 | --- libubuntu-app-launch/glib-thread.h 1970-01-01 00:00:00 +0000 |
2097 | +++ libubuntu-app-launch/glib-thread.h 2016-03-04 11:34:19 +0000 |
2098 | @@ -0,0 +1,90 @@ |
2099 | +/* |
2100 | + * Copyright © 2015 Canonical Ltd. |
2101 | + * |
2102 | + * This program is free software: you can redistribute it and/or modify it |
2103 | + * under the terms of the GNU General Public License version 3, as published |
2104 | + * by the Free Software Foundation. |
2105 | + * |
2106 | + * This program is distributed in the hope that it will be useful, but |
2107 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2108 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2109 | + * PURPOSE. See the GNU General Public License for more details. |
2110 | + * |
2111 | + * You should have received a copy of the GNU General Public License along |
2112 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2113 | + * |
2114 | + * Authors: |
2115 | + * Ted Gould <ted.gould@canonical.com> |
2116 | + */ |
2117 | + |
2118 | +#include <thread> |
2119 | +#include <future> |
2120 | + |
2121 | +#include <gio/gio.h> |
2122 | + |
2123 | +namespace GLib |
2124 | +{ |
2125 | + |
2126 | +class ContextThread |
2127 | +{ |
2128 | + std::thread _thread; |
2129 | + std::shared_ptr<GMainContext> _context; |
2130 | + std::shared_ptr<GMainLoop> _loop; |
2131 | + std::shared_ptr<GCancellable> _cancel; |
2132 | + |
2133 | +public: |
2134 | + ContextThread(std::function<void()> beforeLoop = |
2135 | + [] |
2136 | + { |
2137 | + }, |
2138 | + std::function<void()> afterLoop = |
2139 | + [] |
2140 | + { |
2141 | + }); |
2142 | + ~ContextThread(); |
2143 | + |
2144 | + void quit(); |
2145 | + bool isCancelled(); |
2146 | + std::shared_ptr<GCancellable> getCancellable(); |
2147 | + |
2148 | + void executeOnThread(std::function<void()> work); |
2149 | + template <typename T> |
2150 | + auto executeOnThread(std::function<T()> work) -> T |
2151 | + { |
2152 | + if (std::this_thread::get_id() == _thread.get_id()) |
2153 | + { |
2154 | + /* Don't block if we're on the same thread */ |
2155 | + return work(); |
2156 | + } |
2157 | + |
2158 | + std::promise<T> promise; |
2159 | + std::function<void()> magicFunc = [&promise, &work]() |
2160 | + { |
2161 | + promise.set_value(work()); |
2162 | + }; |
2163 | + |
2164 | + executeOnThread(magicFunc); |
2165 | + |
2166 | + auto future = promise.get_future(); |
2167 | + future.wait(); |
2168 | + return future.get(); |
2169 | + } |
2170 | + |
2171 | + void timeout(const std::chrono::milliseconds& length, std::function<void()> work); |
2172 | + template <class Rep, class Period> |
2173 | + void timeout(const std::chrono::duration<Rep, Period>& length, std::function<void()> work) |
2174 | + { |
2175 | + return timeout(std::chrono::duration_cast<std::chrono::milliseconds>(length), work); |
2176 | + } |
2177 | + |
2178 | + void timeoutSeconds(const std::chrono::seconds& length, std::function<void()> work); |
2179 | + template <class Rep, class Period> |
2180 | + void timeoutSeconds(const std::chrono::duration<Rep, Period>& length, std::function<void()> work) |
2181 | + { |
2182 | + return timeoutSeconds(std::chrono::duration_cast<std::chrono::seconds>(length), work); |
2183 | + } |
2184 | + |
2185 | +private: |
2186 | + void simpleSource(std::function<GSource*()> srcBuilder, std::function<void()> work); |
2187 | +}; |
2188 | +} |
2189 | |
2190 | === added file 'libubuntu-app-launch/helper-impl-click.cpp' |
2191 | --- libubuntu-app-launch/helper-impl-click.cpp 1970-01-01 00:00:00 +0000 |
2192 | +++ libubuntu-app-launch/helper-impl-click.cpp 2016-03-04 11:34:19 +0000 |
2193 | @@ -0,0 +1,175 @@ |
2194 | +/* |
2195 | + * Copyright © 2016 Canonical Ltd. |
2196 | + * |
2197 | + * This program is free software: you can redistribute it and/or modify it |
2198 | + * under the terms of the GNU General Public License version 3, as published |
2199 | + * by the Free Software Foundation. |
2200 | + * |
2201 | + * This program is distributed in the hope that it will be useful, but |
2202 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2203 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2204 | + * PURPOSE. See the GNU General Public License for more details. |
2205 | + * |
2206 | + * You should have received a copy of the GNU General Public License along |
2207 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2208 | + * |
2209 | + * Authors: |
2210 | + * Ted Gould <ted.gould@canonical.com> |
2211 | + */ |
2212 | + |
2213 | +#include "helper-impl-click.h" |
2214 | +#include "registry-impl.h" |
2215 | + |
2216 | +#include "ubuntu-app-launch.h" |
2217 | + |
2218 | +namespace ubuntu |
2219 | +{ |
2220 | +namespace app_launch |
2221 | +{ |
2222 | +namespace helper_impls |
2223 | +{ |
2224 | + |
2225 | +bool Click::hasInstances() |
2226 | +{ |
2227 | + return _registry->impl->thread.executeOnThread<bool>([this]() { |
2228 | + auto instances = ubuntu_app_launch_list_helper_instances(_type.value().c_str(), ((std::string)_appid).c_str()); |
2229 | + auto retval = (g_strv_length(instances) != 0); |
2230 | + |
2231 | + g_strfreev(instances); |
2232 | + |
2233 | + return retval; |
2234 | + }); |
2235 | +} |
2236 | + |
2237 | +class ClickInstance : public Helper::Instance |
2238 | +{ |
2239 | +public: /* all one file, no one to hide from */ |
2240 | + AppID _appid; |
2241 | + Helper::Type _type; |
2242 | + std::string _instanceid; |
2243 | + std::shared_ptr<Registry> _registry; |
2244 | + |
2245 | + ClickInstance(const AppID& appid, |
2246 | + const Helper::Type& type, |
2247 | + const std::string& instanceid, |
2248 | + std::shared_ptr<Registry> registry) |
2249 | + : _appid(appid) |
2250 | + , _type(type) |
2251 | + , _instanceid(instanceid) |
2252 | + , _registry(registry) |
2253 | + { |
2254 | + } |
2255 | + |
2256 | + bool isRunning() override |
2257 | + { |
2258 | + return _registry->impl->thread.executeOnThread<bool>([this]() { |
2259 | + bool found = false; |
2260 | + |
2261 | + auto instances = |
2262 | + ubuntu_app_launch_list_helper_instances(_type.value().c_str(), ((std::string)_appid).c_str()); |
2263 | + for (int i = 0; instances[i] != nullptr; i++) |
2264 | + { |
2265 | + if (_instanceid == std::string(instances[i])) |
2266 | + { |
2267 | + found = true; |
2268 | + break; |
2269 | + } |
2270 | + } |
2271 | + |
2272 | + g_strfreev(instances); |
2273 | + |
2274 | + return found; |
2275 | + }); |
2276 | + } |
2277 | + |
2278 | + void stop() override |
2279 | + { |
2280 | + _registry->impl->thread.executeOnThread<bool>([this]() { |
2281 | + return ubuntu_app_launch_stop_multiple_helper(_type.value().c_str(), ((std::string)_appid).c_str(), |
2282 | + _instanceid.c_str()) == TRUE; |
2283 | + }); |
2284 | + } |
2285 | +}; |
2286 | + |
2287 | +std::vector<std::shared_ptr<Click::Instance>> Click::instances() |
2288 | +{ |
2289 | + return _registry->impl->thread.executeOnThread<std::vector<std::shared_ptr<Click::Instance>>>( |
2290 | + [this]() -> std::vector<std::shared_ptr<Click::Instance>> { |
2291 | + std::vector<std::shared_ptr<Click::Instance>> vect; |
2292 | + auto instances = |
2293 | + ubuntu_app_launch_list_helper_instances(_type.value().c_str(), ((std::string)_appid).c_str()); |
2294 | + for (int i = 0; instances[i] != nullptr; i++) |
2295 | + { |
2296 | + auto inst = std::make_shared<ClickInstance>(_appid, _type, instances[i], _registry); |
2297 | + vect.push_back(inst); |
2298 | + } |
2299 | + |
2300 | + g_strfreev(instances); |
2301 | + |
2302 | + return vect; |
2303 | + }); |
2304 | +} |
2305 | + |
2306 | +std::shared_ptr<gchar*> urlsToStrv(std::vector<Helper::URL> urls) |
2307 | +{ |
2308 | + if (urls.size() == 0) |
2309 | + { |
2310 | + return {}; |
2311 | + } |
2312 | + |
2313 | + auto array = g_array_new(TRUE, FALSE, sizeof(gchar*)); |
2314 | + |
2315 | + for (auto url : urls) |
2316 | + { |
2317 | + auto str = g_strdup(url.value().c_str()); |
2318 | + g_array_append_val(array, str); |
2319 | + } |
2320 | + |
2321 | + return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev); |
2322 | +} |
2323 | + |
2324 | +std::shared_ptr<Click::Instance> Click::launch(std::vector<Helper::URL> urls) |
2325 | +{ |
2326 | + auto urlstrv = urlsToStrv(urls); |
2327 | + |
2328 | + return _registry->impl->thread.executeOnThread<std::shared_ptr<Click::Instance>>([this, urlstrv]() { |
2329 | + auto instanceid = ubuntu_app_launch_start_multiple_helper(_type.value().c_str(), ((std::string)_appid).c_str(), |
2330 | + urlstrv.get()); |
2331 | + |
2332 | + return std::make_shared<ClickInstance>(_appid, _type, instanceid, _registry); |
2333 | + }); |
2334 | +} |
2335 | + |
2336 | +std::shared_ptr<Click::Instance> Click::launch(MirPromptSession* session, std::vector<Helper::URL> urls) |
2337 | +{ |
2338 | + auto urlstrv = urlsToStrv(urls); |
2339 | + |
2340 | + return _registry->impl->thread.executeOnThread<std::shared_ptr<Click::Instance>>([this, session, urlstrv]() { |
2341 | + auto instanceid = ubuntu_app_launch_start_session_helper(_type.value().c_str(), session, |
2342 | + ((std::string)_appid).c_str(), urlstrv.get()); |
2343 | + |
2344 | + return std::make_shared<ClickInstance>(_appid, _type, instanceid, _registry); |
2345 | + }); |
2346 | +} |
2347 | + |
2348 | +std::list<std::shared_ptr<Helper>> Click::running(Helper::Type type, std::shared_ptr<Registry> registry) |
2349 | +{ |
2350 | + return registry->impl->thread.executeOnThread<std::list<std::shared_ptr<Helper>>>([type, registry]() { |
2351 | + std::list<std::shared_ptr<Helper>> helpers; |
2352 | + |
2353 | + auto appidv = ubuntu_app_launch_list_helpers(type.value().c_str()); |
2354 | + for (int i = 0; appidv[i] != nullptr; i++) |
2355 | + { |
2356 | + auto helper = std::make_shared<Click>(type, AppID::parse(appidv[i]), registry); |
2357 | + helpers.push_back(helper); |
2358 | + } |
2359 | + |
2360 | + g_strfreev(appidv); |
2361 | + |
2362 | + return helpers; |
2363 | + }); |
2364 | +} |
2365 | + |
2366 | +}; // namespace helper_impl |
2367 | +}; // namespace app_launch |
2368 | +}; // namespace ubuntu |
2369 | |
2370 | === added file 'libubuntu-app-launch/helper-impl-click.h' |
2371 | --- libubuntu-app-launch/helper-impl-click.h 1970-01-01 00:00:00 +0000 |
2372 | +++ libubuntu-app-launch/helper-impl-click.h 2016-03-04 11:34:19 +0000 |
2373 | @@ -0,0 +1,62 @@ |
2374 | +/* |
2375 | + * Copyright © 2016 Canonical Ltd. |
2376 | + * |
2377 | + * This program is free software: you can redistribute it and/or modify it |
2378 | + * under the terms of the GNU General Public License version 3, as published |
2379 | + * by the Free Software Foundation. |
2380 | + * |
2381 | + * This program is distributed in the hope that it will be useful, but |
2382 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2383 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2384 | + * PURPOSE. See the GNU General Public License for more details. |
2385 | + * |
2386 | + * You should have received a copy of the GNU General Public License along |
2387 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2388 | + * |
2389 | + * Authors: |
2390 | + * Ted Gould <ted.gould@canonical.com> |
2391 | + */ |
2392 | + |
2393 | +#include <list> |
2394 | + |
2395 | +#include "helper.h" |
2396 | + |
2397 | +namespace ubuntu |
2398 | +{ |
2399 | +namespace app_launch |
2400 | +{ |
2401 | +namespace helper_impls |
2402 | +{ |
2403 | + |
2404 | +class Click : public Helper |
2405 | +{ |
2406 | +public: |
2407 | + Click(const Helper::Type& type, const AppID& appid, const std::shared_ptr<Registry>& registry) |
2408 | + : _type(type) |
2409 | + , _appid(appid) |
2410 | + , _registry(registry) |
2411 | + { |
2412 | + } |
2413 | + |
2414 | + AppID appId() override |
2415 | + { |
2416 | + return _appid; |
2417 | + } |
2418 | + |
2419 | + bool hasInstances() override; |
2420 | + std::vector<std::shared_ptr<Helper::Instance>> instances() override; |
2421 | + |
2422 | + std::shared_ptr<Helper::Instance> launch(std::vector<Helper::URL> urls = {}) override; |
2423 | + std::shared_ptr<Helper::Instance> launch(MirPromptSession* session, std::vector<Helper::URL> urls = {}) override; |
2424 | + |
2425 | + static std::list<std::shared_ptr<Helper>> running(Helper::Type type, std::shared_ptr<Registry> registry); |
2426 | + |
2427 | +private: |
2428 | + Helper::Type _type; |
2429 | + AppID _appid; |
2430 | + std::shared_ptr<Registry> _registry; |
2431 | +}; |
2432 | + |
2433 | +}; // namespace helper_impl |
2434 | +}; // namespace app_launch |
2435 | +}; // namespace ubuntu |
2436 | |
2437 | === added file 'libubuntu-app-launch/helper.cpp' |
2438 | --- libubuntu-app-launch/helper.cpp 1970-01-01 00:00:00 +0000 |
2439 | +++ libubuntu-app-launch/helper.cpp 2016-03-04 11:34:19 +0000 |
2440 | @@ -0,0 +1,36 @@ |
2441 | +/* |
2442 | + * Copyright © 2016 Canonical Ltd. |
2443 | + * |
2444 | + * This program is free software: you can redistribute it and/or modify it |
2445 | + * under the terms of the GNU General Public License version 3, as published |
2446 | + * by the Free Software Foundation. |
2447 | + * |
2448 | + * This program is distributed in the hope that it will be useful, but |
2449 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2450 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2451 | + * PURPOSE. See the GNU General Public License for more details. |
2452 | + * |
2453 | + * You should have received a copy of the GNU General Public License along |
2454 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2455 | + * |
2456 | + * Authors: |
2457 | + * Ted Gould <ted.gould@canonical.com> |
2458 | + */ |
2459 | + |
2460 | +#include "helper.h" |
2461 | + |
2462 | +#include "helper-impl-click.h" |
2463 | + |
2464 | +namespace ubuntu |
2465 | +{ |
2466 | +namespace app_launch |
2467 | +{ |
2468 | + |
2469 | +std::shared_ptr<Helper> Helper::create(Type type, AppID appid, std::shared_ptr<Registry> registry) |
2470 | +{ |
2471 | + /* Only one type today */ |
2472 | + return std::make_shared<helper_impls::Click>(type, appid, registry); |
2473 | +} |
2474 | + |
2475 | +}; // namespace AppLaunch |
2476 | +}; // namespace Ubuntu |
2477 | |
2478 | === added file 'libubuntu-app-launch/helper.h' |
2479 | --- libubuntu-app-launch/helper.h 1970-01-01 00:00:00 +0000 |
2480 | +++ libubuntu-app-launch/helper.h 2016-03-04 11:34:19 +0000 |
2481 | @@ -0,0 +1,71 @@ |
2482 | +/* |
2483 | + * Copyright © 2016 Canonical Ltd. |
2484 | + * |
2485 | + * This program is free software: you can redistribute it and/or modify it |
2486 | + * under the terms of the GNU General Public License version 3, as published |
2487 | + * by the Free Software Foundation. |
2488 | + * |
2489 | + * This program is distributed in the hope that it will be useful, but |
2490 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2491 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2492 | + * PURPOSE. See the GNU General Public License for more details. |
2493 | + * |
2494 | + * You should have received a copy of the GNU General Public License along |
2495 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2496 | + * |
2497 | + * Authors: |
2498 | + * Ted Gould <ted.gould@canonical.com> |
2499 | + */ |
2500 | + |
2501 | +#include <memory> |
2502 | +#include <vector> |
2503 | + |
2504 | +#include <mir_toolkit/mir_prompt_session.h> |
2505 | + |
2506 | +#include "appid.h" |
2507 | +#include "type-tagger.h" |
2508 | + |
2509 | +#pragma once |
2510 | +#pragma GCC visibility push(default) |
2511 | + |
2512 | +namespace ubuntu |
2513 | +{ |
2514 | +namespace app_launch |
2515 | +{ |
2516 | + |
2517 | +class Registry; |
2518 | + |
2519 | +class Helper |
2520 | +{ |
2521 | +public: |
2522 | + struct TypeTag; |
2523 | + struct URLTag; |
2524 | + |
2525 | + typedef TypeTagger<TypeTag, std::string> Type; |
2526 | + typedef TypeTagger<URLTag, std::string> URL; |
2527 | + |
2528 | + static std::shared_ptr<Helper> create(Type type, AppID appid, std::shared_ptr<Registry> registry); |
2529 | + |
2530 | + virtual AppID appId() = 0; |
2531 | + |
2532 | + class Instance |
2533 | + { |
2534 | + public: |
2535 | + /* Query lifecycle */ |
2536 | + virtual bool isRunning() = 0; |
2537 | + |
2538 | + /* Manage lifecycle */ |
2539 | + virtual void stop() = 0; |
2540 | + }; |
2541 | + |
2542 | + virtual bool hasInstances() = 0; |
2543 | + virtual std::vector<std::shared_ptr<Instance>> instances() = 0; |
2544 | + |
2545 | + virtual std::shared_ptr<Instance> launch(std::vector<URL> urls = {}) = 0; |
2546 | + virtual std::shared_ptr<Instance> launch(MirPromptSession* session, std::vector<URL> urls = {}) = 0; |
2547 | +}; |
2548 | + |
2549 | +}; // namespace app_launch |
2550 | +}; // namespace ubuntu |
2551 | + |
2552 | +#pragma GCC visibility pop |
2553 | |
2554 | === modified file 'libubuntu-app-launch/libubuntu-app-launch.map' |
2555 | --- libubuntu-app-launch/libubuntu-app-launch.map 2014-11-21 15:57:08 +0000 |
2556 | +++ libubuntu-app-launch/libubuntu-app-launch.map 2016-03-04 11:34:19 +0000 |
2557 | @@ -1,6 +1,23 @@ |
2558 | { |
2559 | global: |
2560 | ubuntu_app_launch_*; |
2561 | + extern "C++" { |
2562 | + ubuntu::app_launch::Registry::[!I]*; |
2563 | + typeinfo?for?ubuntu::app_launch::Registry; |
2564 | + typeinfo?name?for?ubuntu::app_launch::Registry; |
2565 | + ubuntu::app_launch::Application::*; |
2566 | + typeinfo?for?ubuntu::app_launch::Application; |
2567 | + typeinfo?name?for?ubuntu::app_launch::Application; |
2568 | + ubuntu::app_launch::AppID::*; |
2569 | + typeinfo?for?ubuntu::app_launch::AppID; |
2570 | + typeinfo?name?for?ubuntu::app_launch::AppID; |
2571 | + ubuntu::app_launch::Helper::*; |
2572 | + typeinfo?for?ubuntu::app_launch::Helper; |
2573 | + typeinfo?name?for?ubuntu::app_launch::Helper; |
2574 | + }; |
2575 | local: |
2576 | *; |
2577 | + extern "C++" { |
2578 | + *; |
2579 | + }; |
2580 | }; |
2581 | |
2582 | === added file 'libubuntu-app-launch/registry-impl.cpp' |
2583 | --- libubuntu-app-launch/registry-impl.cpp 1970-01-01 00:00:00 +0000 |
2584 | +++ libubuntu-app-launch/registry-impl.cpp 2016-03-04 11:34:19 +0000 |
2585 | @@ -0,0 +1,185 @@ |
2586 | +/* |
2587 | + * Copyright © 2016 Canonical Ltd. |
2588 | + * |
2589 | + * This program is free software: you can redistribute it and/or modify it |
2590 | + * under the terms of the GNU General Public License version 3, as published |
2591 | + * by the Free Software Foundation. |
2592 | + * |
2593 | + * This program is distributed in the hope that it will be useful, but |
2594 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2595 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2596 | + * PURPOSE. See the GNU General Public License for more details. |
2597 | + * |
2598 | + * You should have received a copy of the GNU General Public License along |
2599 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2600 | + * |
2601 | + * Authors: |
2602 | + * Ted Gould <ted.gould@canonical.com> |
2603 | + */ |
2604 | + |
2605 | +#include "registry-impl.h" |
2606 | + |
2607 | +namespace ubuntu |
2608 | +{ |
2609 | +namespace app_launch |
2610 | +{ |
2611 | + |
2612 | +Registry::Impl::Impl(Registry* registry) |
2613 | + : thread([]() {}, |
2614 | + [this]() { |
2615 | + _clickUser.reset(); |
2616 | + _clickDB.reset(); |
2617 | + |
2618 | + g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr); |
2619 | + _dbus.reset(); |
2620 | + }) |
2621 | + , _registry(registry) |
2622 | +// _manager(nullptr) |
2623 | +{ |
2624 | + auto cancel = thread.getCancellable(); |
2625 | + _dbus = thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() { |
2626 | + return std::shared_ptr<GDBusConnection>(g_bus_get_sync(G_BUS_TYPE_SESSION, cancel.get(), nullptr), |
2627 | + [](GDBusConnection* bus) { g_clear_object(&bus); }); |
2628 | + }); |
2629 | +} |
2630 | + |
2631 | +void Registry::Impl::initClick() |
2632 | +{ |
2633 | + if (_clickDB && _clickUser) |
2634 | + { |
2635 | + return; |
2636 | + } |
2637 | + |
2638 | + auto init = thread.executeOnThread<bool>([this]() { |
2639 | + GError* error = nullptr; |
2640 | + |
2641 | + if (!_clickDB) |
2642 | + { |
2643 | + _clickDB = std::shared_ptr<ClickDB>(click_db_new(), [](ClickDB* db) { g_clear_object(&db); }); |
2644 | + /* If TEST_CLICK_DB is unset, this reads the system database. */ |
2645 | + click_db_read(_clickDB.get(), g_getenv("TEST_CLICK_DB"), &error); |
2646 | + |
2647 | + if (error != nullptr) |
2648 | + { |
2649 | + auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); }); |
2650 | + throw std::runtime_error(perror->message); |
2651 | + } |
2652 | + } |
2653 | + |
2654 | + if (!_clickUser) |
2655 | + { |
2656 | + _clickUser = |
2657 | + std::shared_ptr<ClickUser>(click_user_new_for_user(_clickDB.get(), g_getenv("TEST_CLICK_USER"), &error), |
2658 | + [](ClickUser* user) { g_clear_object(&user); }); |
2659 | + |
2660 | + if (error != nullptr) |
2661 | + { |
2662 | + auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); }); |
2663 | + throw std::runtime_error(perror->message); |
2664 | + } |
2665 | + } |
2666 | + |
2667 | + return true; |
2668 | + }); |
2669 | + |
2670 | + if (!init) |
2671 | + { |
2672 | + throw std::runtime_error("Unable to initialize the Click Database"); |
2673 | + } |
2674 | +} |
2675 | + |
2676 | +std::shared_ptr<JsonObject> Registry::Impl::getClickManifest(const std::string& package) |
2677 | +{ |
2678 | + initClick(); |
2679 | + |
2680 | + auto retval = thread.executeOnThread<std::shared_ptr<JsonObject>>([this, package]() { |
2681 | + GError* error = nullptr; |
2682 | + auto retval = std::shared_ptr<JsonObject>(click_user_get_manifest(_clickUser.get(), package.c_str(), &error), |
2683 | + [](JsonObject* obj) { |
2684 | + if (obj != nullptr) |
2685 | + { |
2686 | + json_object_unref(obj); |
2687 | + } |
2688 | + }); |
2689 | + |
2690 | + if (error != nullptr) |
2691 | + { |
2692 | + auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); }); |
2693 | + throw std::runtime_error(perror->message); |
2694 | + } |
2695 | + |
2696 | + return retval; |
2697 | + }); |
2698 | + |
2699 | + if (!retval) |
2700 | + throw std::runtime_error("Unable to get Click manifest for package: " + package); |
2701 | + |
2702 | + return retval; |
2703 | +} |
2704 | + |
2705 | +std::list<AppID::Package> Registry::Impl::getClickPackages() |
2706 | +{ |
2707 | + initClick(); |
2708 | + |
2709 | + return thread.executeOnThread<std::list<AppID::Package>>([this]() { |
2710 | + GError* error = nullptr; |
2711 | + GList* pkgs = click_db_get_packages(_clickDB.get(), FALSE, &error); |
2712 | + |
2713 | + if (error != nullptr) |
2714 | + { |
2715 | + auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); }); |
2716 | + throw std::runtime_error(perror->message); |
2717 | + } |
2718 | + |
2719 | + std::list<AppID::Package> list; |
2720 | + for (GList* item = pkgs; item != NULL; item = g_list_next(item)) |
2721 | + { |
2722 | + list.emplace_back(AppID::Package::from_raw((gchar*)item->data)); |
2723 | + } |
2724 | + |
2725 | + g_list_free_full(pkgs, g_free); |
2726 | + return list; |
2727 | + }); |
2728 | +} |
2729 | + |
2730 | +std::string Registry::Impl::getClickDir(const std::string& package) |
2731 | +{ |
2732 | + initClick(); |
2733 | + |
2734 | + return thread.executeOnThread<std::string>([this, package]() { |
2735 | + GError* error = nullptr; |
2736 | + auto dir = click_user_get_path(_clickUser.get(), package.c_str(), &error); |
2737 | + |
2738 | + if (error != nullptr) |
2739 | + { |
2740 | + auto perror = std::shared_ptr<GError>(error, [](GError* error) { g_error_free(error); }); |
2741 | + throw std::runtime_error(perror->message); |
2742 | + } |
2743 | + |
2744 | + std::string cppdir(dir); |
2745 | + g_free(dir); |
2746 | + return cppdir; |
2747 | + }); |
2748 | +} |
2749 | + |
2750 | +#if 0 |
2751 | +void |
2752 | +Registry::Impl::setManager (Registry::Manager* manager) |
2753 | +{ |
2754 | + if (_manager != nullptr) |
2755 | + { |
2756 | + throw std::runtime_error("Already have a manager and trying to set another"); |
2757 | + } |
2758 | + |
2759 | + _manager = manager; |
2760 | +} |
2761 | + |
2762 | +void |
2763 | +Registry::Impl::clearManager () |
2764 | +{ |
2765 | + _manager = nullptr; |
2766 | +} |
2767 | +#endif |
2768 | + |
2769 | +}; // namespace app_launch |
2770 | +}; // namespace ubuntu |
2771 | |
2772 | === added file 'libubuntu-app-launch/registry-impl.h' |
2773 | --- libubuntu-app-launch/registry-impl.h 1970-01-01 00:00:00 +0000 |
2774 | +++ libubuntu-app-launch/registry-impl.h 2016-03-04 11:34:19 +0000 |
2775 | @@ -0,0 +1,69 @@ |
2776 | +/* |
2777 | + * Copyright © 2016 Canonical Ltd. |
2778 | + * |
2779 | + * This program is free software: you can redistribute it and/or modify it |
2780 | + * under the terms of the GNU General Public License version 3, as published |
2781 | + * by the Free Software Foundation. |
2782 | + * |
2783 | + * This program is distributed in the hope that it will be useful, but |
2784 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2785 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2786 | + * PURPOSE. See the GNU General Public License for more details. |
2787 | + * |
2788 | + * You should have received a copy of the GNU General Public License along |
2789 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2790 | + * |
2791 | + * Authors: |
2792 | + * Ted Gould <ted.gould@canonical.com> |
2793 | + */ |
2794 | + |
2795 | +#include "registry.h" |
2796 | +#include "glib-thread.h" |
2797 | + |
2798 | +#include <json-glib/json-glib.h> |
2799 | +#include <click.h> |
2800 | +#include <gio/gio.h> |
2801 | + |
2802 | +#pragma once |
2803 | + |
2804 | +namespace ubuntu |
2805 | +{ |
2806 | +namespace app_launch |
2807 | +{ |
2808 | + |
2809 | +class Registry::Impl |
2810 | +{ |
2811 | +public: |
2812 | + Impl(Registry* registry); |
2813 | + virtual ~Impl() |
2814 | + { |
2815 | + thread.quit(); |
2816 | + } |
2817 | + |
2818 | + std::shared_ptr<JsonObject> getClickManifest(const std::string& package); |
2819 | + std::list<AppID::Package> getClickPackages(); |
2820 | + std::string getClickDir(const std::string& package); |
2821 | + |
2822 | +#if 0 |
2823 | + void setManager (Registry::Manager* manager); |
2824 | + void clearManager (); |
2825 | +#endif |
2826 | + |
2827 | + GLib::ContextThread thread; |
2828 | + |
2829 | +private: |
2830 | + Registry* _registry; |
2831 | +#if 0 |
2832 | + Registry::Manager* _manager; |
2833 | +#endif |
2834 | + |
2835 | + std::shared_ptr<ClickDB> _clickDB; |
2836 | + std::shared_ptr<ClickUser> _clickUser; |
2837 | + |
2838 | + std::shared_ptr<GDBusConnection> _dbus; |
2839 | + |
2840 | + void initClick(); |
2841 | +}; |
2842 | + |
2843 | +}; // namespace app_launch |
2844 | +}; // namespace ubuntu |
2845 | |
2846 | === added file 'libubuntu-app-launch/registry.cpp' |
2847 | --- libubuntu-app-launch/registry.cpp 1970-01-01 00:00:00 +0000 |
2848 | +++ libubuntu-app-launch/registry.cpp 2016-03-04 11:34:19 +0000 |
2849 | @@ -0,0 +1,99 @@ |
2850 | +/* |
2851 | + * Copyright © 2016 Canonical Ltd. |
2852 | + * |
2853 | + * This program is free software: you can redistribute it and/or modify it |
2854 | + * under the terms of the GNU General Public License version 3, as published |
2855 | + * by the Free Software Foundation. |
2856 | + * |
2857 | + * This program is distributed in the hope that it will be useful, but |
2858 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2859 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2860 | + * PURPOSE. See the GNU General Public License for more details. |
2861 | + * |
2862 | + * You should have received a copy of the GNU General Public License along |
2863 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2864 | + * |
2865 | + * Authors: |
2866 | + * Ted Gould <ted.gould@canonical.com> |
2867 | + */ |
2868 | + |
2869 | +#include "registry.h" |
2870 | +#include "registry-impl.h" |
2871 | + |
2872 | +#include "application-impl-click.h" |
2873 | +#include "application-impl-legacy.h" |
2874 | +#include "application-impl-libertine.h" |
2875 | + |
2876 | +#include "helper-impl-click.h" |
2877 | + |
2878 | +namespace ubuntu |
2879 | +{ |
2880 | +namespace app_launch |
2881 | +{ |
2882 | + |
2883 | +Registry::Registry() |
2884 | +{ |
2885 | + impl = std::unique_ptr<Impl>(new Impl(this)); |
2886 | +} |
2887 | + |
2888 | +Registry::~Registry() |
2889 | +{ |
2890 | +} |
2891 | + |
2892 | +std::list<std::shared_ptr<Application>> Registry::runningApps(std::shared_ptr<Registry> connection) |
2893 | +{ |
2894 | + return connection->impl->thread.executeOnThread<std::list<std::shared_ptr<Application>>>( |
2895 | + [connection]() -> std::list<std::shared_ptr<Application>> { |
2896 | + auto strv = ubuntu_app_launch_list_running_apps(); |
2897 | + if (strv == nullptr) |
2898 | + { |
2899 | + return {}; |
2900 | + } |
2901 | + |
2902 | + std::list<std::shared_ptr<Application>> list; |
2903 | + for (int i = 0; strv[i] != nullptr; i++) |
2904 | + { |
2905 | + auto appid = AppID::find(strv[i]); |
2906 | + auto app = Application::create(appid, connection); |
2907 | + list.push_back(app); |
2908 | + } |
2909 | + |
2910 | + g_strfreev(strv); |
2911 | + |
2912 | + return list; |
2913 | + }); |
2914 | +} |
2915 | + |
2916 | +std::list<std::shared_ptr<Application>> Registry::installedApps(std::shared_ptr<Registry> connection) |
2917 | +{ |
2918 | + std::list<std::shared_ptr<Application>> list; |
2919 | + |
2920 | + list.splice(list.begin(), app_impls::Click::list(connection)); |
2921 | + list.splice(list.begin(), app_impls::Legacy::list(connection)); |
2922 | + list.splice(list.begin(), app_impls::Libertine::list(connection)); |
2923 | + |
2924 | + return list; |
2925 | +} |
2926 | + |
2927 | +std::list<std::shared_ptr<Helper>> Registry::runningHelpers(Helper::Type type, std::shared_ptr<Registry> connection) |
2928 | +{ |
2929 | + std::list<std::shared_ptr<Helper>> list; |
2930 | + |
2931 | + list.splice(list.begin(), helper_impls::Click::running(type, connection)); |
2932 | + |
2933 | + return list; |
2934 | +} |
2935 | + |
2936 | +std::shared_ptr<Registry> defaultRegistry; |
2937 | +std::shared_ptr<Registry> Registry::getDefault() |
2938 | +{ |
2939 | + if (!defaultRegistry) |
2940 | + { |
2941 | + defaultRegistry = std::make_shared<Registry>(); |
2942 | + } |
2943 | + |
2944 | + return defaultRegistry; |
2945 | +} |
2946 | + |
2947 | +}; // namespace app_launch |
2948 | +}; // namespace ubuntu |
2949 | |
2950 | === added file 'libubuntu-app-launch/registry.h' |
2951 | --- libubuntu-app-launch/registry.h 1970-01-01 00:00:00 +0000 |
2952 | +++ libubuntu-app-launch/registry.h 2016-03-04 11:34:19 +0000 |
2953 | @@ -0,0 +1,91 @@ |
2954 | +/* |
2955 | + * Copyright © 2016 Canonical Ltd. |
2956 | + * |
2957 | + * This program is free software: you can redistribute it and/or modify it |
2958 | + * under the terms of the GNU General Public License version 3, as published |
2959 | + * by the Free Software Foundation. |
2960 | + * |
2961 | + * This program is distributed in the hope that it will be useful, but |
2962 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2963 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2964 | + * PURPOSE. See the GNU General Public License for more details. |
2965 | + * |
2966 | + * You should have received a copy of the GNU General Public License along |
2967 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2968 | + * |
2969 | + * Authors: |
2970 | + * Ted Gould <ted.gould@canonical.com> |
2971 | + */ |
2972 | + |
2973 | +#include <core/signal.h> |
2974 | +#include <functional> |
2975 | +#include <list> |
2976 | +#include <memory> |
2977 | + |
2978 | +#include "application.h" |
2979 | +#include "helper.h" |
2980 | + |
2981 | +#pragma once |
2982 | +#pragma GCC visibility push(default) |
2983 | + |
2984 | +namespace ubuntu |
2985 | +{ |
2986 | +namespace app_launch |
2987 | +{ |
2988 | + |
2989 | +class Registry |
2990 | +{ |
2991 | +public: |
2992 | + enum class FailureType |
2993 | + { |
2994 | + CRASH, |
2995 | + START_FAILURE, |
2996 | + }; |
2997 | + |
2998 | + Registry(); |
2999 | + virtual ~Registry(); |
3000 | + |
3001 | + /* Lots of application lists */ |
3002 | + static std::list<std::shared_ptr<Application>> runningApps(std::shared_ptr<Registry> registry = getDefault()); |
3003 | + static std::list<std::shared_ptr<Application>> installedApps(std::shared_ptr<Registry> registry = getDefault()); |
3004 | + |
3005 | +#if 0 /* TODO -- In next MR */ |
3006 | + /* Signals to discover what is happening to apps */ |
3007 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStarted; |
3008 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStopped; |
3009 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> appFailed; |
3010 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appPaused; |
3011 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appResumed; |
3012 | + |
3013 | + /* The Application Manager, almost always if you're not Unity8, don't |
3014 | + use this API. Testing is a special case. */ |
3015 | + class Manager |
3016 | + { |
3017 | + virtual bool focusRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0; |
3018 | + virtual bool startingRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0; |
3019 | + |
3020 | + protected: |
3021 | + Manager() = default; |
3022 | + }; |
3023 | + |
3024 | + void setManager (Manager* manager); |
3025 | + void clearManager (); |
3026 | +#endif |
3027 | + |
3028 | + /* Helper Lists */ |
3029 | + static std::list<std::shared_ptr<Helper>> runningHelpers(Helper::Type type, |
3030 | + std::shared_ptr<Registry> registry = getDefault()); |
3031 | + |
3032 | + /* Default Junk */ |
3033 | + static std::shared_ptr<Registry> getDefault(); |
3034 | + static void clearDefault(); |
3035 | + |
3036 | + /* Hide our implementation */ |
3037 | + class Impl; |
3038 | + std::unique_ptr<Impl> impl; |
3039 | +}; |
3040 | + |
3041 | +}; // namespace app_launch |
3042 | +}; // namespace ubuntu |
3043 | + |
3044 | +#pragma GCC visibility pop |
3045 | |
3046 | === added file 'libubuntu-app-launch/type-tagger.h' |
3047 | --- libubuntu-app-launch/type-tagger.h 1970-01-01 00:00:00 +0000 |
3048 | +++ libubuntu-app-launch/type-tagger.h 2016-03-04 11:34:19 +0000 |
3049 | @@ -0,0 +1,37 @@ |
3050 | +#pragma once |
3051 | + |
3052 | +namespace ubuntu |
3053 | +{ |
3054 | +namespace app_launch |
3055 | +{ |
3056 | + |
3057 | +template <typename Tag, typename T> |
3058 | +class TypeTagger |
3059 | +{ |
3060 | +public: |
3061 | + static TypeTagger<Tag, T> from_raw(const T& value) |
3062 | + { |
3063 | + return TypeTagger<Tag, T>(value); |
3064 | + } |
3065 | + const T& value() const |
3066 | + { |
3067 | + return _value; |
3068 | + } |
3069 | + operator T() const |
3070 | + { |
3071 | + return _value; |
3072 | + } |
3073 | + ~TypeTagger() |
3074 | + { |
3075 | + } |
3076 | + |
3077 | +private: |
3078 | + TypeTagger(const T& value) |
3079 | + : _value(value) |
3080 | + { |
3081 | + } |
3082 | + T _value; |
3083 | +}; |
3084 | + |
3085 | +}; // namespace app_launch |
3086 | +}; // namespace ubuntu |
3087 | |
3088 | === modified file 'libubuntu-app-launch/ubuntu-app-launch.c' |
3089 | --- libubuntu-app-launch/ubuntu-app-launch.c 2015-08-17 21:38:29 +0000 |
3090 | +++ libubuntu-app-launch/ubuntu-app-launch.c 2016-03-04 11:34:19 +0000 |
3091 | @@ -38,7 +38,6 @@ |
3092 | |
3093 | static void apps_for_job (GDBusConnection * con, const gchar * name, GArray * apps, gboolean truncate_legacy); |
3094 | static void free_helper (gpointer value); |
3095 | -static GList * pids_for_appid (const gchar * appid); |
3096 | int kill (pid_t pid, int signal); |
3097 | static gchar * escape_dbus_string (const gchar * input); |
3098 | |
3099 | @@ -588,7 +587,7 @@ |
3100 | |
3101 | do { |
3102 | hash_table_size = g_hash_table_size(pidssignaled); |
3103 | - GList * pidlist = pids_for_appid(appid); |
3104 | + GList * pidlist = ubuntu_app_launch_get_pids(appid); |
3105 | GList * iter; |
3106 | |
3107 | if (pidlist == NULL) { |
3108 | @@ -1506,9 +1505,10 @@ |
3109 | /* Get the PIDs for an AppID. If it's click or legacy single instance that's |
3110 | a simple call to the helper. But if it's not, we have to make a call for |
3111 | each instance of the app that we have running. */ |
3112 | -static GList * |
3113 | -pids_for_appid (const gchar * appid) |
3114 | +GList * |
3115 | +ubuntu_app_launch_get_pids (const gchar * appid) |
3116 | { |
3117 | + g_return_val_if_fail(appid != NULL, NULL); |
3118 | ual_tracepoint(pids_list_start, appid); |
3119 | |
3120 | GDBusConnection * cgmanager = cgroup_manager_connection(); |
3121 | @@ -1522,7 +1522,7 @@ |
3122 | |
3123 | ual_tracepoint(pids_list_finished, appid, g_list_length(pids)); |
3124 | return pids; |
3125 | - } else if (!is_libertine(appid) && legacy_single_instance(appid)) { |
3126 | + } else if (is_libertine(appid) || legacy_single_instance(appid)) { |
3127 | gchar * jobname = g_strdup_printf("%s-", appid); |
3128 | GList * pids = pids_from_cgroup(cgmanager, "application-legacy", jobname); |
3129 | g_free(jobname); |
3130 | @@ -1572,7 +1572,7 @@ |
3131 | return FALSE; |
3132 | } |
3133 | |
3134 | - GList * pidlist = pids_for_appid(appid); |
3135 | + GList * pidlist = ubuntu_app_launch_get_pids(appid); |
3136 | GList * head; |
3137 | |
3138 | for (head = pidlist; head != NULL; head = g_list_next(head)) { |
3139 | @@ -2403,7 +2403,7 @@ |
3140 | const gchar * job_name = g_getenv("UPSTART_JOB"); |
3141 | const gchar * instance_name = g_getenv("UPSTART_INSTANCE"); |
3142 | const gchar * demangler = g_getenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME"); |
3143 | - g_return_if_fail(job_name != NULL); |
3144 | + g_return_val_if_fail(job_name != NULL, FALSE); |
3145 | |
3146 | GError * error = NULL; |
3147 | GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); |
3148 | |
3149 | === modified file 'libubuntu-app-launch/ubuntu-app-launch.h' |
3150 | --- libubuntu-app-launch/ubuntu-app-launch.h 2015-08-10 14:19:05 +0000 |
3151 | +++ libubuntu-app-launch/ubuntu-app-launch.h 2016-03-04 11:34:19 +0000 |
3152 | @@ -388,6 +388,18 @@ |
3153 | GPid ubuntu_app_launch_get_primary_pid (const gchar * appid); |
3154 | |
3155 | /** |
3156 | + * ubuntu_app_launch_get_pids: |
3157 | + * @appid: ID of the application to look for |
3158 | + * |
3159 | + * Checks to see if an application is running and returns |
3160 | + * the PIDs associated with it. |
3161 | + * |
3162 | + * Return Value: (transfer full) (element-type GLib.Pid): A list |
3163 | + * of PIDs associated with @appid, empty if not running. |
3164 | + */ |
3165 | +GList * ubuntu_app_launch_get_pids (const gchar * appid); |
3166 | + |
3167 | +/** |
3168 | * ubuntu_app_launch_pid_in_app_id: |
3169 | * @pid: Process ID to check on |
3170 | * @appid: ID of the application to look in |
3171 | |
3172 | === modified file 'tests/CMakeLists.txt' |
3173 | --- tests/CMakeLists.txt 2015-06-26 17:26:50 +0000 |
3174 | +++ tests/CMakeLists.txt 2016-03-04 11:34:19 +0000 |
3175 | @@ -42,6 +42,11 @@ |
3176 | mir-mock.cpp) |
3177 | target_link_libraries (libual-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher) |
3178 | |
3179 | +add_executable (libual-cpp-test |
3180 | + libual-cpp-test.cc |
3181 | + mir-mock.cpp) |
3182 | +target_link_libraries (libual-cpp-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher) |
3183 | + |
3184 | add_executable (data-spew |
3185 | data-spew.c) |
3186 | target_link_libraries (data-spew ${GLIB2_LIBRARIES}) |
3187 | @@ -50,6 +55,16 @@ |
3188 | socket-tool.c) |
3189 | |
3190 | add_test (NAME libual-test COMMAND libual-test) |
3191 | +add_test (NAME libual-cpp-test COMMAND libual-cpp-test) |
3192 | + |
3193 | +# Application Info Desktop |
3194 | + |
3195 | +add_executable (application-info-desktop-test |
3196 | + application-info-desktop.cpp |
3197 | + ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/application-info-desktop.cpp) |
3198 | +target_link_libraries (application-info-desktop-test gtest ${GTEST_LIBS} ubuntu-launcher) |
3199 | + |
3200 | +add_test (NAME application-info-desktop-test COMMAND application-info-desktop-test) |
3201 | |
3202 | # Failure Test |
3203 | |
3204 | @@ -101,3 +116,8 @@ |
3205 | configure_file ("xmir-helper-test.in" "${CMAKE_CURRENT_BINARY_DIR}/xmir-helper-test" @ONLY) |
3206 | add_test (xmir-helper-test xmir-helper-test) |
3207 | |
3208 | +# Formatted code |
3209 | + |
3210 | +add_custom_target(format-tests |
3211 | + COMMAND clang-format -i -style=file libual-cpp-test.cc |
3212 | +) |
3213 | |
3214 | === added file 'tests/application-info-desktop.cpp' |
3215 | --- tests/application-info-desktop.cpp 1970-01-01 00:00:00 +0000 |
3216 | +++ tests/application-info-desktop.cpp 2016-03-04 11:34:19 +0000 |
3217 | @@ -0,0 +1,148 @@ |
3218 | +/* |
3219 | + * Copyright © 2016 Canonical Ltd. |
3220 | + * |
3221 | + * This program is free software: you can redistribute it and/or modify it |
3222 | + * under the terms of the GNU General Public License version 3, as published |
3223 | + * by the Free Software Foundation. |
3224 | + * |
3225 | + * This program is distributed in the hope that it will be useful, but |
3226 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3227 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3228 | + * PURPOSE. See the GNU General Public License for more details. |
3229 | + * |
3230 | + * You should have received a copy of the GNU General Public License along |
3231 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
3232 | + * |
3233 | + * Authors: |
3234 | + * Ted Gould <ted.gould@canonical.com> |
3235 | + */ |
3236 | + |
3237 | +#include "application-info-desktop.h" |
3238 | + |
3239 | +#include <gtest/gtest.h> |
3240 | + |
3241 | +class ApplicationInfoDesktop : public ::testing::Test |
3242 | +{ |
3243 | + virtual void SetUp() |
3244 | + { |
3245 | + } |
3246 | + |
3247 | + virtual void TearDown() |
3248 | + { |
3249 | + } |
3250 | +}; |
3251 | + |
3252 | +#define DESKTOP "Desktop Entry" |
3253 | + |
3254 | +TEST_F(ApplicationInfoDesktop, DefaultState) |
3255 | +{ |
3256 | + auto keyfile = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free); |
3257 | + g_key_file_set_string(keyfile.get(), DESKTOP, "Name", "Foo App"); |
3258 | + g_key_file_set_string(keyfile.get(), DESKTOP, "Exec", "foo"); |
3259 | + g_key_file_set_string(keyfile.get(), DESKTOP, "Icon", "foo.png"); |
3260 | + |
3261 | + auto appinfo = ubuntu::app_launch::app_info::Desktop(keyfile, "/"); |
3262 | + |
3263 | + EXPECT_EQ("Foo App", appinfo.name().value()); |
3264 | + EXPECT_EQ("", appinfo.description().value()); |
3265 | + EXPECT_EQ("/foo.png", appinfo.iconPath().value()); |
3266 | + |
3267 | + EXPECT_EQ("", appinfo.splash().title.value()); |
3268 | + EXPECT_EQ("", appinfo.splash().image.value()); |
3269 | + EXPECT_EQ("", appinfo.splash().backgroundColor.value()); |
3270 | + EXPECT_EQ("", appinfo.splash().headerColor.value()); |
3271 | + EXPECT_EQ("", appinfo.splash().footerColor.value()); |
3272 | + EXPECT_FALSE( appinfo.splash().showHeader.value()); |
3273 | + |
3274 | + EXPECT_TRUE(appinfo.supportedOrientations().portrait); |
3275 | + EXPECT_TRUE(appinfo.supportedOrientations().landscape); |
3276 | + EXPECT_TRUE(appinfo.supportedOrientations().invertedPortrait); |
3277 | + EXPECT_TRUE(appinfo.supportedOrientations().invertedLandscape); |
3278 | + |
3279 | + EXPECT_FALSE(appinfo.rotatesWindowContents().value()); |
3280 | + |
3281 | + EXPECT_FALSE(appinfo.supportsUbuntuLifecycle().value()); |
3282 | +} |
3283 | + |
3284 | +TEST_F(ApplicationInfoDesktop, KeyfileErrors) |
3285 | +{ |
3286 | + EXPECT_THROW(ubuntu::app_launch::app_info::Desktop({}, "/"), std::runtime_error); |
3287 | + |
3288 | + auto noname = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free); |
3289 | + g_key_file_set_string(noname.get(), DESKTOP, "Comment", "This is a comment"); |
3290 | + g_key_file_set_string(noname.get(), DESKTOP, "Exec", "foo"); |
3291 | + g_key_file_set_string(noname.get(), DESKTOP, "Icon", "foo.png"); |
3292 | + |
3293 | + EXPECT_THROW(ubuntu::app_launch::app_info::Desktop(noname, "/"), std::runtime_error); |
3294 | + |
3295 | + auto noicon = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free); |
3296 | + g_key_file_set_string(noicon.get(), DESKTOP, "Name", "Foo App"); |
3297 | + g_key_file_set_string(noicon.get(), DESKTOP, "Comment", "This is a comment"); |
3298 | + g_key_file_set_string(noicon.get(), DESKTOP, "Exec", "foo"); |
3299 | + |
3300 | + EXPECT_THROW(ubuntu::app_launch::app_info::Desktop(noicon, "/"), std::runtime_error); |
3301 | +} |
3302 | + |
3303 | +TEST_F(ApplicationInfoDesktop, Orientations) |
3304 | +{ |
3305 | + ubuntu::app_launch::Application::Info::Orientations defaultOrientations = |
3306 | + { |
3307 | +portrait: |
3308 | + true, |
3309 | +landscape: |
3310 | + true, |
3311 | +invertedPortrait: |
3312 | + true, |
3313 | +invertedLandscape: |
3314 | + true |
3315 | + }; |
3316 | + |
3317 | + auto keyfile = std::shared_ptr<GKeyFile>(g_key_file_new(), g_key_file_free); |
3318 | + g_key_file_set_string(keyfile.get(), DESKTOP, "Name", "Foo App"); |
3319 | + g_key_file_set_string(keyfile.get(), DESKTOP, "Exec", "foo"); |
3320 | + g_key_file_set_string(keyfile.get(), DESKTOP, "Icon", "foo.png"); |
3321 | + |
3322 | + EXPECT_EQ(defaultOrientations, ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3323 | + |
3324 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "this should not parse"); |
3325 | + EXPECT_EQ(defaultOrientations, ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3326 | + |
3327 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "this;should;not;parse;"); |
3328 | + EXPECT_EQ(defaultOrientations, ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3329 | + |
3330 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "portrait;"); |
3331 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: false, invertedPortrait: false, invertedLandscape: false}), |
3332 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3333 | + |
3334 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "landscape;portrait;"); |
3335 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: false, invertedLandscape: false}), |
3336 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3337 | + |
3338 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "landscape ; portrait; invertedPortrait"); |
3339 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: true, invertedLandscape: false}), |
3340 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3341 | + |
3342 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "portrait;landscape;"); |
3343 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: false, invertedLandscape: false}), |
3344 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3345 | + |
3346 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "portrait;landscape;invertedportrait;invertedlandscape;"); |
3347 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: true, invertedLandscape: true}), |
3348 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3349 | + |
3350 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "PORTRAIT;"); |
3351 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: false, invertedPortrait: false, invertedLandscape: false}), |
3352 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3353 | + |
3354 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "pOrTraIt;lANDscApE;inVErtEDpORtrAit;iNVErtEDLAnDsCapE;"); |
3355 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: true, landscape: true, invertedPortrait: true, invertedLandscape: true}), |
3356 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3357 | + |
3358 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "primary;"); |
3359 | + EXPECT_EQ((ubuntu::app_launch::Application::Info::Orientations {portrait: false, landscape: false, invertedPortrait: false, invertedLandscape: false}), |
3360 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3361 | + |
3362 | + g_key_file_set_string(keyfile.get(), DESKTOP, "X-Ubuntu-Supported-Orientations", "foobar;primary;"); |
3363 | + EXPECT_EQ(defaultOrientations, |
3364 | + ubuntu::app_launch::app_info::Desktop(keyfile, "/").supportedOrientations()); |
3365 | +} |
3366 | |
3367 | === modified file 'tests/click-app-dir/application.desktop' |
3368 | --- tests/click-app-dir/application.desktop 2013-09-24 03:39:27 +0000 |
3369 | +++ tests/click-app-dir/application.desktop 2016-03-04 11:34:19 +0000 |
3370 | @@ -1,4 +1,6 @@ |
3371 | [Desktop Entry] |
3372 | +Version=1.0 |
3373 | Name=Application |
3374 | Type=Application |
3375 | -Exec=foo |
3376 | +Exec=grep |
3377 | +Icon=foo |
3378 | |
3379 | === modified file 'tests/exec-util-test.cc' |
3380 | --- tests/exec-util-test.cc 2015-08-11 19:11:15 +0000 |
3381 | +++ tests/exec-util-test.cc 2016-03-04 11:34:19 +0000 |
3382 | @@ -172,7 +172,7 @@ |
3383 | {"APP_DIR", [](const gchar * value) { |
3384 | EXPECT_STREQ(APP_DIR, value); }}, |
3385 | {"APP_EXEC", [](const gchar * value) { |
3386 | - EXPECT_STREQ("foo", value); }}, |
3387 | + EXPECT_STREQ("grep", value); }}, |
3388 | {"APP_ID", [](const gchar * value) { |
3389 | EXPECT_STREQ("com.test.good_application_1.2.3", value); }}, |
3390 | {"APP_LAUNCHER_PID", [](const gchar * value) { |
3391 | |
3392 | === modified file 'tests/helper-handshake-test.cc' |
3393 | --- tests/helper-handshake-test.cc 2014-04-30 16:18:29 +0000 |
3394 | +++ tests/helper-handshake-test.cc 2016-03-04 11:34:19 +0000 |
3395 | @@ -103,6 +103,7 @@ |
3396 | { |
3397 | bool * reached = static_cast<bool *>(user_data); |
3398 | *reached = true; |
3399 | + return true; |
3400 | } |
3401 | |
3402 | TEST_F(HelperHandshakeTest, HandshakeTimeout) |
3403 | @@ -114,6 +115,8 @@ |
3404 | |
3405 | starting_handshake_wait(handshake); |
3406 | |
3407 | + g_source_remove(outertimeout); |
3408 | + |
3409 | ASSERT_FALSE(timeout_reached); |
3410 | |
3411 | return; |
3412 | |
3413 | === modified file 'tests/helper-test.cc' |
3414 | --- tests/helper-test.cc 2014-11-11 22:05:30 +0000 |
3415 | +++ tests/helper-test.cc 2016-03-04 11:34:19 +0000 |
3416 | @@ -297,8 +297,6 @@ |
3417 | ASSERT_NE(calls, nullptr); |
3418 | ASSERT_STREQ("SetEnvList", calls[0].name); |
3419 | |
3420 | - unsigned int i; |
3421 | - |
3422 | bool got_app_isolation = false; |
3423 | bool got_cache_home = false; |
3424 | bool got_config_home = false; |
3425 | |
3426 | === modified file 'tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop' |
3427 | --- tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop 2015-08-06 21:38:31 +0000 |
3428 | +++ tests/libertine-data/libertine-container/container-name/rootfs/usr/share/applications/test.desktop 2016-03-04 11:34:19 +0000 |
3429 | @@ -2,3 +2,4 @@ |
3430 | Name=Test |
3431 | Type=Application |
3432 | Exec=test |
3433 | +Icon=test |
3434 | |
3435 | === added file 'tests/libual-cpp-test.cc' |
3436 | --- tests/libual-cpp-test.cc 1970-01-01 00:00:00 +0000 |
3437 | +++ tests/libual-cpp-test.cc 2016-03-04 11:34:19 +0000 |
3438 | @@ -0,0 +1,1599 @@ |
3439 | +/* |
3440 | + * Copyright 2013 Canonical Ltd. |
3441 | + * |
3442 | + * This program is free software: you can redistribute it and/or modify it |
3443 | + * under the terms of the GNU General Public License version 3, as published |
3444 | + * by the Free Software Foundation. |
3445 | + * |
3446 | + * This program is distributed in the hope that it will be useful, but |
3447 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3448 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3449 | + * PURPOSE. See the GNU General Public License for more details. |
3450 | + * |
3451 | + * You should have received a copy of the GNU General Public License along |
3452 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
3453 | + * |
3454 | + * Authors: |
3455 | + * Ted Gould <ted.gould@canonical.com> |
3456 | + */ |
3457 | + |
3458 | +#include <future> |
3459 | +#include <thread> |
3460 | + |
3461 | +#include <gtest/gtest.h> |
3462 | +#include <gio/gio.h> |
3463 | +#include <zeitgeist.h> |
3464 | +#include "mir-mock.h" |
3465 | + |
3466 | +#include "registry.h" |
3467 | +#include "application.h" |
3468 | +#include "helper.h" |
3469 | + |
3470 | +extern "C" { |
3471 | +#include "ubuntu-app-launch.h" |
3472 | +#include "libdbustest/dbus-test.h" |
3473 | +#include <fcntl.h> |
3474 | +} |
3475 | + |
3476 | +class LibUAL : public ::testing::Test |
3477 | +{ |
3478 | +protected: |
3479 | + DbusTestService* service = NULL; |
3480 | + DbusTestDbusMock* mock = NULL; |
3481 | + DbusTestDbusMock* cgmock = NULL; |
3482 | + GDBusConnection* bus = NULL; |
3483 | + std::string last_focus_appid; |
3484 | + std::string last_resume_appid; |
3485 | + guint resume_timeout = 0; |
3486 | + std::shared_ptr<ubuntu::app_launch::Registry> registry; |
3487 | + |
3488 | +private: |
3489 | + static void focus_cb(const gchar* appid, gpointer user_data) |
3490 | + { |
3491 | + g_debug("Focus Callback: %s", appid); |
3492 | + LibUAL* _this = static_cast<LibUAL*>(user_data); |
3493 | + _this->last_focus_appid = appid; |
3494 | + } |
3495 | + |
3496 | + static void resume_cb(const gchar* appid, gpointer user_data) |
3497 | + { |
3498 | + g_debug("Resume Callback: %s", appid); |
3499 | + LibUAL* _this = static_cast<LibUAL*>(user_data); |
3500 | + _this->last_resume_appid = appid; |
3501 | + |
3502 | + if (_this->resume_timeout > 0) |
3503 | + { |
3504 | + _this->pause(_this->resume_timeout); |
3505 | + } |
3506 | + } |
3507 | + |
3508 | +protected: |
3509 | + /* Useful debugging stuff, but not on by default. You really want to |
3510 | + not get all this noise typically */ |
3511 | + void debugConnection() |
3512 | + { |
3513 | + if (true) |
3514 | + { |
3515 | + return; |
3516 | + } |
3517 | + |
3518 | + DbusTestBustle* bustle = dbus_test_bustle_new("test.bustle"); |
3519 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(bustle)); |
3520 | + g_object_unref(bustle); |
3521 | + |
3522 | + DbusTestProcess* monitor = dbus_test_process_new("dbus-monitor"); |
3523 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(monitor)); |
3524 | + g_object_unref(monitor); |
3525 | + } |
3526 | + |
3527 | + virtual void SetUp() |
3528 | + { |
3529 | + /* Click DB test mode */ |
3530 | + g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE); |
3531 | + g_setenv("TEST_CLICK_USER", "test-user", TRUE); |
3532 | + |
3533 | + gchar* linkfarmpath = g_build_filename(CMAKE_SOURCE_DIR, "link-farm", NULL); |
3534 | + g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", linkfarmpath, TRUE); |
3535 | + g_free(linkfarmpath); |
3536 | + |
3537 | + g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE); |
3538 | + g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE); |
3539 | + g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE); |
3540 | + |
3541 | + service = dbus_test_service_new(NULL); |
3542 | + |
3543 | + debugConnection(); |
3544 | + |
3545 | + mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart"); |
3546 | + |
3547 | + DbusTestDbusMockObject* obj = |
3548 | + dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL); |
3549 | + |
3550 | + dbus_test_dbus_mock_object_add_method(mock, obj, "EmitEvent", G_VARIANT_TYPE("(sasb)"), NULL, "", NULL); |
3551 | + |
3552 | + dbus_test_dbus_mock_object_add_method(mock, obj, "GetJobByName", G_VARIANT_TYPE("s"), G_VARIANT_TYPE("o"), |
3553 | + "if args[0] == 'application-click':\n" |
3554 | + " ret = dbus.ObjectPath('/com/test/application_click')\n" |
3555 | + "elif args[0] == 'application-legacy':\n" |
3556 | + " ret = dbus.ObjectPath('/com/test/application_legacy')\n" |
3557 | + "elif args[0] == 'untrusted-helper':\n" |
3558 | + " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n", |
3559 | + NULL); |
3560 | + |
3561 | + dbus_test_dbus_mock_object_add_method(mock, obj, "SetEnv", G_VARIANT_TYPE("(assb)"), NULL, "", NULL); |
3562 | + |
3563 | + /* Click App */ |
3564 | + DbusTestDbusMockObject* jobobj = |
3565 | + dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); |
3566 | + |
3567 | + dbus_test_dbus_mock_object_add_method( |
3568 | + mock, jobobj, "Start", G_VARIANT_TYPE("(asb)"), NULL, |
3569 | + "if args[0][0] == 'APP_ID=com.test.good_application_1.2.3':" |
3570 | + " raise dbus.exceptions.DBusException('Foo running', name='com.ubuntu.Upstart0_6.Error.AlreadyStarted')", |
3571 | + NULL); |
3572 | + |
3573 | + dbus_test_dbus_mock_object_add_method(mock, jobobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL); |
3574 | + |
3575 | + dbus_test_dbus_mock_object_add_method(mock, jobobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"), |
3576 | + "ret = [ dbus.ObjectPath('/com/test/app_instance') ]", NULL); |
3577 | + |
3578 | + DbusTestDbusMockObject* instobj = |
3579 | + dbus_test_dbus_mock_get_object(mock, "/com/test/app_instance", "com.ubuntu.Upstart0_6.Instance", NULL); |
3580 | + dbus_test_dbus_mock_object_add_property(mock, instobj, "name", G_VARIANT_TYPE_STRING, |
3581 | + g_variant_new_string("com.test.good_application_1.2.3"), NULL); |
3582 | + gchar* process_var = g_strdup_printf("[('main', %d)]", getpid()); |
3583 | + dbus_test_dbus_mock_object_add_property(mock, instobj, "processes", G_VARIANT_TYPE("a(si)"), |
3584 | + g_variant_new_parsed(process_var), NULL); |
3585 | + g_free(process_var); |
3586 | + |
3587 | + /* Legacy App */ |
3588 | + DbusTestDbusMockObject* ljobobj = |
3589 | + dbus_test_dbus_mock_get_object(mock, "/com/test/application_legacy", "com.ubuntu.Upstart0_6.Job", NULL); |
3590 | + |
3591 | + dbus_test_dbus_mock_object_add_method(mock, ljobobj, "Start", G_VARIANT_TYPE("(asb)"), NULL, "", NULL); |
3592 | + |
3593 | + dbus_test_dbus_mock_object_add_method(mock, ljobobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL); |
3594 | + |
3595 | + dbus_test_dbus_mock_object_add_method(mock, ljobobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"), |
3596 | + "ret = [ dbus.ObjectPath('/com/test/legacy_app_instance') ]", NULL); |
3597 | + |
3598 | + DbusTestDbusMockObject* linstobj = dbus_test_dbus_mock_get_object(mock, "/com/test/legacy_app_instance", |
3599 | + "com.ubuntu.Upstart0_6.Instance", NULL); |
3600 | + dbus_test_dbus_mock_object_add_property(mock, linstobj, "name", G_VARIANT_TYPE_STRING, |
3601 | + g_variant_new_string("multiple-2342345"), NULL); |
3602 | + dbus_test_dbus_mock_object_add_property(mock, linstobj, "processes", G_VARIANT_TYPE("a(si)"), |
3603 | + g_variant_new_parsed("[('main', 5678)]"), NULL); |
3604 | + |
3605 | + /* Untrusted Helper */ |
3606 | + DbusTestDbusMockObject* uhelperobj = |
3607 | + dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL); |
3608 | + |
3609 | + dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "Start", G_VARIANT_TYPE("(asb)"), NULL, "", NULL); |
3610 | + |
3611 | + dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL); |
3612 | + |
3613 | + dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"), |
3614 | + "ret = [ dbus.ObjectPath('/com/test/untrusted/helper/instance'), " |
3615 | + "dbus.ObjectPath('/com/test/untrusted/helper/multi_instance') ]", |
3616 | + NULL); |
3617 | + |
3618 | + DbusTestDbusMockObject* uhelperinstance = dbus_test_dbus_mock_get_object( |
3619 | + mock, "/com/test/untrusted/helper/instance", "com.ubuntu.Upstart0_6.Instance", NULL); |
3620 | + dbus_test_dbus_mock_object_add_property(mock, uhelperinstance, "name", G_VARIANT_TYPE_STRING, |
3621 | + g_variant_new_string("untrusted-type::com.foo_bar_43.23.12"), NULL); |
3622 | + |
3623 | + DbusTestDbusMockObject* unhelpermulti = dbus_test_dbus_mock_get_object( |
3624 | + mock, "/com/test/untrusted/helper/multi_instance", "com.ubuntu.Upstart0_6.Instance", NULL); |
3625 | + dbus_test_dbus_mock_object_add_property( |
3626 | + mock, unhelpermulti, "name", G_VARIANT_TYPE_STRING, |
3627 | + g_variant_new_string("untrusted-type:24034582324132:com.bar_foo_8432.13.1"), NULL); |
3628 | + |
3629 | + /* Create the cgroup manager mock */ |
3630 | + cgmock = dbus_test_dbus_mock_new("org.test.cgmock"); |
3631 | + g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME", "org.test.cgmock", TRUE); |
3632 | + |
3633 | + DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock, "/org/linuxcontainers/cgmanager", |
3634 | + "org.linuxcontainers.cgmanager0_0", NULL); |
3635 | + dbus_test_dbus_mock_object_add_method(cgmock, cgobject, "GetTasksRecursive", G_VARIANT_TYPE("(ss)"), |
3636 | + G_VARIANT_TYPE("ai"), "ret = [100, 200, 300]", NULL); |
3637 | + |
3638 | + /* Put it together */ |
3639 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); |
3640 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(cgmock)); |
3641 | + dbus_test_service_start_tasks(service); |
3642 | + |
3643 | + bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
3644 | + g_dbus_connection_set_exit_on_close(bus, FALSE); |
3645 | + g_object_add_weak_pointer(G_OBJECT(bus), (gpointer*)&bus); |
3646 | + |
3647 | + /* Make sure we pretend the CG manager is just on our bus */ |
3648 | + g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE); |
3649 | + |
3650 | + ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this)); |
3651 | + ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this)); |
3652 | + |
3653 | + registry = std::make_shared<ubuntu::app_launch::Registry>(); |
3654 | + } |
3655 | + |
3656 | + virtual void TearDown() |
3657 | + { |
3658 | + registry.reset(); |
3659 | + |
3660 | + ubuntu_app_launch_observer_delete_app_focus(focus_cb, this); |
3661 | + ubuntu_app_launch_observer_delete_app_resume(resume_cb, this); |
3662 | + |
3663 | + g_clear_object(&mock); |
3664 | + g_clear_object(&cgmock); |
3665 | + g_clear_object(&service); |
3666 | + |
3667 | + g_object_unref(bus); |
3668 | + |
3669 | + unsigned int cleartry = 0; |
3670 | + while (bus != NULL && cleartry < 100) |
3671 | + { |
3672 | + pause(100); |
3673 | + cleartry++; |
3674 | + } |
3675 | + ASSERT_EQ(nullptr, bus); |
3676 | + } |
3677 | + |
3678 | + GVariant* find_env(GVariant* env_array, const gchar* var) |
3679 | + { |
3680 | + unsigned int i; |
3681 | + GVariant* retval = nullptr; |
3682 | + |
3683 | + for (i = 0; i < g_variant_n_children(env_array); i++) |
3684 | + { |
3685 | + GVariant* child = g_variant_get_child_value(env_array, i); |
3686 | + const gchar* envvar = g_variant_get_string(child, nullptr); |
3687 | + |
3688 | + if (g_str_has_prefix(envvar, var)) |
3689 | + { |
3690 | + if (retval != nullptr) |
3691 | + { |
3692 | + g_warning("Found the env var more than once!"); |
3693 | + g_variant_unref(retval); |
3694 | + return nullptr; |
3695 | + } |
3696 | + |
3697 | + retval = child; |
3698 | + } |
3699 | + else |
3700 | + { |
3701 | + g_variant_unref(child); |
3702 | + } |
3703 | + } |
3704 | + |
3705 | + if (!retval) |
3706 | + { |
3707 | + gchar* envstr = g_variant_print(env_array, FALSE); |
3708 | + g_warning("Unable to find '%s' in '%s'", var, envstr); |
3709 | + g_free(envstr); |
3710 | + } |
3711 | + |
3712 | + return retval; |
3713 | + } |
3714 | + |
3715 | + bool check_env(GVariant* env_array, const gchar* var, const gchar* value) |
3716 | + { |
3717 | + bool found = false; |
3718 | + GVariant* val = find_env(env_array, var); |
3719 | + if (val == nullptr) |
3720 | + { |
3721 | + return false; |
3722 | + } |
3723 | + |
3724 | + const gchar* envvar = g_variant_get_string(val, nullptr); |
3725 | + |
3726 | + gchar* combined = g_strdup_printf("%s=%s", var, value); |
3727 | + if (g_strcmp0(envvar, combined) == 0) |
3728 | + { |
3729 | + found = true; |
3730 | + } |
3731 | + |
3732 | + g_variant_unref(val); |
3733 | + |
3734 | + return found; |
3735 | + } |
3736 | + |
3737 | + void pause(guint time = 0) |
3738 | + { |
3739 | + if (time > 0) |
3740 | + { |
3741 | + GMainLoop* mainloop = g_main_loop_new(NULL, FALSE); |
3742 | + |
3743 | + g_timeout_add(time, |
3744 | + [](gpointer pmainloop) -> gboolean |
3745 | + { |
3746 | + g_main_loop_quit(static_cast<GMainLoop*>(pmainloop)); |
3747 | + return G_SOURCE_REMOVE; |
3748 | + }, |
3749 | + mainloop); |
3750 | + |
3751 | + g_main_loop_run(mainloop); |
3752 | + |
3753 | + g_main_loop_unref(mainloop); |
3754 | + } |
3755 | + |
3756 | + while (g_main_pending()) |
3757 | + { |
3758 | + g_main_iteration(TRUE); |
3759 | + } |
3760 | + } |
3761 | +}; |
3762 | + |
3763 | +TEST_F(LibUAL, DISABLED_StartApplication) |
3764 | +{ |
3765 | + DbusTestDbusMockObject* obj = |
3766 | + dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); |
3767 | + |
3768 | + /* Basic make sure we can send the event */ |
3769 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3"); |
3770 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
3771 | + app->launch(); |
3772 | + |
3773 | + EXPECT_EQ(1, dbus_test_dbus_mock_object_check_method_call(mock, obj, "Start", NULL, NULL)); |
3774 | + |
3775 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
3776 | + |
3777 | + /* Now look at the details of the call */ |
3778 | + app->launch(); |
3779 | + |
3780 | + guint len = 0; |
3781 | + const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
3782 | + EXPECT_NE(nullptr, calls); |
3783 | + EXPECT_EQ(1, len); |
3784 | + |
3785 | + EXPECT_STREQ("Start", calls->name); |
3786 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
3787 | + |
3788 | + GVariant* block = g_variant_get_child_value(calls->params, 1); |
3789 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
3790 | + g_variant_unref(block); |
3791 | + |
3792 | + GVariant* env = g_variant_get_child_value(calls->params, 0); |
3793 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3")); |
3794 | + g_variant_unref(env); |
3795 | + |
3796 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
3797 | + |
3798 | + /* Let's pass some URLs */ |
3799 | + std::vector<ubuntu::app_launch::Application::URL> urls{ |
3800 | + ubuntu::app_launch::Application::URL::from_raw("http://ubuntu.com/"), |
3801 | + ubuntu::app_launch::Application::URL::from_raw("https://ubuntu.com/"), |
3802 | + ubuntu::app_launch::Application::URL::from_raw("file:///home/phablet/test.txt")}; |
3803 | + |
3804 | + app->launch(urls); |
3805 | + |
3806 | + len = 0; |
3807 | + calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
3808 | + EXPECT_NE(nullptr, calls); |
3809 | + EXPECT_EQ(1, len); |
3810 | + |
3811 | + env = g_variant_get_child_value(calls->params, 0); |
3812 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3")); |
3813 | + EXPECT_TRUE( |
3814 | + check_env(env, "APP_URIS", "'http://ubuntu.com/' 'https://ubuntu.com/' 'file:///home/phablet/test.txt'")); |
3815 | + g_variant_unref(env); |
3816 | + |
3817 | + return; |
3818 | +} |
3819 | + |
3820 | +TEST_F(LibUAL, DISABLED_StartApplicationTest) |
3821 | +{ |
3822 | + DbusTestDbusMockObject* obj = |
3823 | + dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); |
3824 | + |
3825 | + /* Basic make sure we can send the event */ |
3826 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3"); |
3827 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
3828 | + app->launchTest(); |
3829 | + |
3830 | + guint len = 0; |
3831 | + const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
3832 | + EXPECT_NE(nullptr, calls); |
3833 | + EXPECT_EQ(1, len); |
3834 | + |
3835 | + EXPECT_STREQ("Start", calls->name); |
3836 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
3837 | + |
3838 | + GVariant* block = g_variant_get_child_value(calls->params, 1); |
3839 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
3840 | + g_variant_unref(block); |
3841 | + |
3842 | + GVariant* env = g_variant_get_child_value(calls->params, 0); |
3843 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3")); |
3844 | + EXPECT_TRUE(check_env(env, "QT_LOAD_TESTABILITY", "1")); |
3845 | + g_variant_unref(env); |
3846 | +} |
3847 | + |
3848 | +TEST_F(LibUAL, DISABLED_StopApplication) |
3849 | +{ |
3850 | + DbusTestDbusMockObject* obj = |
3851 | + dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); |
3852 | + |
3853 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
3854 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
3855 | + |
3856 | + ASSERT_TRUE(app->hasInstances()); |
3857 | + EXPECT_EQ(1, app->instances().size()); |
3858 | + |
3859 | + app->instances()[0]->stop(); |
3860 | + |
3861 | + ASSERT_EQ(dbus_test_dbus_mock_object_check_method_call(mock, obj, "Stop", NULL, NULL), 1); |
3862 | +} |
3863 | + |
3864 | +/* NOTE: The fact that there is 'libertine-data' in these strings is because |
3865 | + we're using one CACHE_HOME for this test suite and the libertine functions |
3866 | + need to pull things from there, where these are only comparisons. It's just |
3867 | + what value is in the environment variable */ |
3868 | +TEST_F(LibUAL, DISABLED_ApplicationLog) |
3869 | +{ |
3870 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
3871 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
3872 | + |
3873 | + EXPECT_EQ( |
3874 | + std::string(CMAKE_SOURCE_DIR "/libertine-data/upstart/application-click-com.test.good_application_1.2.3.log"), |
3875 | + app->instances()[0]->logPath()); |
3876 | + |
3877 | + appid = ubuntu::app_launch::AppID::find("single"); |
3878 | + app = ubuntu::app_launch::Application::create(appid, registry); |
3879 | + |
3880 | + EXPECT_EQ(std::string(CMAKE_SOURCE_DIR "/libertine-data/upstart/application-legacy-single-.log"), |
3881 | + app->instances()[0]->logPath()); |
3882 | + |
3883 | + appid = ubuntu::app_launch::AppID::find("multiple"); |
3884 | + app = ubuntu::app_launch::Application::create(appid, registry); |
3885 | + |
3886 | + EXPECT_EQ(std::string(CMAKE_SOURCE_DIR "/libertine-data/upstart/application-legacy-multiple-2342345.log"), |
3887 | + app->instances()[0]->logPath()); |
3888 | +} |
3889 | + |
3890 | +TEST_F(LibUAL, DISABLED_ApplicationPid) |
3891 | +{ |
3892 | + /* Check bad params */ |
3893 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
3894 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
3895 | + |
3896 | + EXPECT_FALSE(app->instances()[0]->hasPid(0)); |
3897 | + |
3898 | + /* Check primary pid, which comes from Upstart */ |
3899 | + EXPECT_EQ(getpid(), app->instances()[0]->primaryPid()); |
3900 | + |
3901 | + auto multiappid = ubuntu::app_launch::AppID::find("multiple"); |
3902 | + auto multiapp = ubuntu::app_launch::Application::create(multiappid, registry); |
3903 | + EXPECT_EQ(5678, multiapp->instances()[0]->primaryPid()); |
3904 | + |
3905 | + /* Look at the full PID list from CG Manager */ |
3906 | + DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock, "/org/linuxcontainers/cgmanager", |
3907 | + "org.linuxcontainers.cgmanager0_0", NULL); |
3908 | + const DbusTestDbusMockCall* calls = NULL; |
3909 | + guint len = 0; |
3910 | + |
3911 | + /* Click in the set */ |
3912 | + EXPECT_TRUE(app->instances()[0]->hasPid(100)); |
3913 | + calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL); |
3914 | + EXPECT_EQ(1, len); |
3915 | + EXPECT_STREQ("GetTasksRecursive", calls->name); |
3916 | + EXPECT_TRUE(g_variant_equal( |
3917 | + calls->params, g_variant_new("(ss)", "freezer", "upstart/application-click-com.test.good_application_1.2.3"))); |
3918 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL)); |
3919 | + |
3920 | + /* Click out of the set */ |
3921 | + EXPECT_FALSE(app->instances()[0]->hasPid(101)); |
3922 | + calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL); |
3923 | + EXPECT_EQ(1, len); |
3924 | + EXPECT_STREQ("GetTasksRecursive", calls->name); |
3925 | + EXPECT_TRUE(g_variant_equal( |
3926 | + calls->params, g_variant_new("(ss)", "freezer", "upstart/application-click-com.test.good_application_1.2.3"))); |
3927 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL)); |
3928 | + |
3929 | + /* Legacy Single Instance */ |
3930 | + auto singleappid = ubuntu::app_launch::AppID::find("single"); |
3931 | + auto singleapp = ubuntu::app_launch::Application::create(singleappid, registry); |
3932 | + |
3933 | + EXPECT_TRUE(singleapp->instances()[0]->hasPid(100)); |
3934 | + |
3935 | + calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL); |
3936 | + EXPECT_EQ(1, len); |
3937 | + EXPECT_STREQ("GetTasksRecursive", calls->name); |
3938 | + EXPECT_TRUE(g_variant_equal(calls->params, g_variant_new("(ss)", "freezer", "upstart/application-legacy-single-"))); |
3939 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL)); |
3940 | + |
3941 | + /* Legacy Multi Instance */ |
3942 | + EXPECT_TRUE(multiapp->instances()[0]->hasPid(100)); |
3943 | + calls = dbus_test_dbus_mock_object_get_method_calls(cgmock, cgobject, "GetTasksRecursive", &len, NULL); |
3944 | + EXPECT_EQ(1, len); |
3945 | + EXPECT_STREQ("GetTasksRecursive", calls->name); |
3946 | + EXPECT_TRUE(g_variant_equal(calls->params, |
3947 | + g_variant_new("(ss)", "freezer", "upstart/application-legacy-multiple-2342345"))); |
3948 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(cgmock, cgobject, NULL)); |
3949 | +} |
3950 | + |
3951 | +TEST_F(LibUAL, DISABLED_ApplicationId) |
3952 | +{ |
3953 | + g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE); |
3954 | + g_setenv("TEST_CLICK_USER", "test-user", TRUE); |
3955 | + |
3956 | + /* Test with current-user-version, should return the version in the manifest */ |
3957 | + EXPECT_EQ("com.test.good_application_1.2.3", |
3958 | + (std::string)ubuntu::app_launch::AppID::discover("com.test.good", "application")); |
3959 | + |
3960 | + /* Test with version specified, shouldn't even read the manifest */ |
3961 | + EXPECT_EQ("com.test.good_application_1.2.4", |
3962 | + (std::string)ubuntu::app_launch::AppID::discover("com.test.good", "application", "1.2.4")); |
3963 | + |
3964 | + /* Test with out a version or app, should return the version in the manifest */ |
3965 | + EXPECT_EQ("com.test.good_application_1.2.3", (std::string)ubuntu::app_launch::AppID::discover( |
3966 | + "com.test.good", "first-listed-app", "current-user-version")); |
3967 | + |
3968 | + /* Make sure we can select the app from a list correctly */ |
3969 | + EXPECT_EQ("com.test.multiple_first_1.2.3", |
3970 | + (std::string)ubuntu::app_launch::AppID::discover( |
3971 | + "com.test.multiple", ubuntu::app_launch::AppID::ApplicationWildcard::FIRST_LISTED)); |
3972 | + EXPECT_EQ("com.test.multiple_first_1.2.3", (std::string)ubuntu::app_launch::AppID::discover("com.test.multiple")); |
3973 | + EXPECT_EQ("com.test.multiple_fifth_1.2.3", |
3974 | + (std::string)ubuntu::app_launch::AppID::discover( |
3975 | + "com.test.multiple", ubuntu::app_launch::AppID::ApplicationWildcard::LAST_LISTED)); |
3976 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover( |
3977 | + "com.test.multiple", ubuntu::app_launch::AppID::ApplicationWildcard::ONLY_LISTED)); |
3978 | + EXPECT_EQ("com.test.good_application_1.2.3", |
3979 | + (std::string)ubuntu::app_launch::AppID::discover( |
3980 | + "com.test.good", ubuntu::app_launch::AppID::ApplicationWildcard::ONLY_LISTED)); |
3981 | + |
3982 | + /* A bunch that should be NULL */ |
3983 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-hooks")); |
3984 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-json")); |
3985 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-object")); |
3986 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("com.test.no-version")); |
3987 | + |
3988 | + /* Libertine tests */ |
3989 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("container-name")); |
3990 | + EXPECT_EQ("", (std::string)ubuntu::app_launch::AppID::discover("container-name", "not-exist")); |
3991 | + EXPECT_EQ("container-name_test_0.0", (std::string)ubuntu::app_launch::AppID::discover("container-name", "test")); |
3992 | + EXPECT_EQ("container-name_user-app_0.0", |
3993 | + (std::string)ubuntu::app_launch::AppID::discover("container-name", "user-app")); |
3994 | +} |
3995 | + |
3996 | +TEST_F(LibUAL, AppIdParse) |
3997 | +{ |
3998 | + EXPECT_FALSE(ubuntu::app_launch::AppID::parse("com.ubuntu.test_test_123").empty()); |
3999 | + EXPECT_FALSE(ubuntu::app_launch::AppID::find("inkscape").empty()); |
4000 | + |
4001 | + auto id = ubuntu::app_launch::AppID::parse("com.ubuntu.test_test_123"); |
4002 | + |
4003 | + ASSERT_FALSE(id.empty()); |
4004 | + EXPECT_EQ("com.ubuntu.test", id.package.value()); |
4005 | + EXPECT_EQ("test", id.appname.value()); |
4006 | + EXPECT_EQ("123", id.version.value()); |
4007 | + |
4008 | + return; |
4009 | +} |
4010 | + |
4011 | +TEST_F(LibUAL, DISABLED_ApplicationList) |
4012 | +{ |
4013 | + auto apps = ubuntu::app_launch::Registry::runningApps(registry); |
4014 | + |
4015 | + ASSERT_EQ(2, apps.size()); |
4016 | + |
4017 | + apps.sort([](const std::shared_ptr<ubuntu::app_launch::Application>& a, |
4018 | + const std::shared_ptr<ubuntu::app_launch::Application>& b) |
4019 | + { |
4020 | + std::string sa = a->appId(); |
4021 | + std::string sb = b->appId(); |
4022 | + |
4023 | + return sa < sb; |
4024 | + }); |
4025 | + |
4026 | + EXPECT_EQ("com.test.good_application_1.2.3", (std::string)apps.front()->appId()); |
4027 | + EXPECT_EQ("multiple", (std::string)apps.back()->appId()); |
4028 | +} |
4029 | + |
4030 | +typedef struct |
4031 | +{ |
4032 | + unsigned int count; |
4033 | + const gchar* name; |
4034 | +} observer_data_t; |
4035 | + |
4036 | +static void observer_cb(const gchar* appid, gpointer user_data) |
4037 | +{ |
4038 | + observer_data_t* data = (observer_data_t*)user_data; |
4039 | + |
4040 | + if (data->name == NULL) |
4041 | + { |
4042 | + data->count++; |
4043 | + } |
4044 | + else if (g_strcmp0(data->name, appid) == 0) |
4045 | + { |
4046 | + data->count++; |
4047 | + } |
4048 | +} |
4049 | + |
4050 | +TEST_F(LibUAL, StartStopObserver) |
4051 | +{ |
4052 | + observer_data_t start_data = {.count = 0, .name = nullptr}; |
4053 | + observer_data_t stop_data = {.count = 0, .name = nullptr}; |
4054 | + |
4055 | + ASSERT_TRUE(ubuntu_app_launch_observer_add_app_started(observer_cb, &start_data)); |
4056 | + ASSERT_TRUE(ubuntu_app_launch_observer_add_app_stop(observer_cb, &stop_data)); |
4057 | + |
4058 | + DbusTestDbusMockObject* obj = |
4059 | + dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL); |
4060 | + |
4061 | + /* Basic start */ |
4062 | + dbus_test_dbus_mock_object_emit_signal( |
4063 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4064 | + g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"), |
4065 | + NULL); |
4066 | + |
4067 | + g_usleep(100000); |
4068 | + while (g_main_pending()) |
4069 | + { |
4070 | + g_main_iteration(TRUE); |
4071 | + } |
4072 | + |
4073 | + ASSERT_EQ(start_data.count, 1); |
4074 | + |
4075 | + /* Basic stop */ |
4076 | + dbus_test_dbus_mock_object_emit_signal( |
4077 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4078 | + g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"), |
4079 | + NULL); |
4080 | + |
4081 | + g_usleep(100000); |
4082 | + while (g_main_pending()) |
4083 | + { |
4084 | + g_main_iteration(TRUE); |
4085 | + } |
4086 | + |
4087 | + ASSERT_EQ(stop_data.count, 1); |
4088 | + |
4089 | + /* Start legacy */ |
4090 | + start_data.count = 0; |
4091 | + start_data.name = "multiple"; |
4092 | + |
4093 | + dbus_test_dbus_mock_object_emit_signal( |
4094 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4095 | + g_variant_new_parsed("('started', ['JOB=application-legacy', 'INSTANCE=multiple-234235'])"), NULL); |
4096 | + |
4097 | + g_usleep(100000); |
4098 | + while (g_main_pending()) |
4099 | + { |
4100 | + g_main_iteration(TRUE); |
4101 | + } |
4102 | + |
4103 | + ASSERT_EQ(start_data.count, 1); |
4104 | + |
4105 | + /* Legacy stop */ |
4106 | + stop_data.count = 0; |
4107 | + stop_data.name = "bar"; |
4108 | + |
4109 | + dbus_test_dbus_mock_object_emit_signal( |
4110 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4111 | + g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=bar-9344321'])"), NULL); |
4112 | + |
4113 | + g_usleep(100000); |
4114 | + while (g_main_pending()) |
4115 | + { |
4116 | + g_main_iteration(TRUE); |
4117 | + } |
4118 | + |
4119 | + ASSERT_EQ(stop_data.count, 1); |
4120 | + |
4121 | + /* Test Noise Start */ |
4122 | + start_data.count = 0; |
4123 | + start_data.name = "com.test.good_application_1.2.3"; |
4124 | + stop_data.count = 0; |
4125 | + stop_data.name = "com.test.good_application_1.2.3"; |
4126 | + |
4127 | + /* A full lifecycle */ |
4128 | + dbus_test_dbus_mock_object_emit_signal( |
4129 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4130 | + g_variant_new_parsed("('starting', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"), |
4131 | + NULL); |
4132 | + dbus_test_dbus_mock_object_emit_signal( |
4133 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4134 | + g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"), |
4135 | + NULL); |
4136 | + dbus_test_dbus_mock_object_emit_signal( |
4137 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4138 | + g_variant_new_parsed("('stopping', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"), |
4139 | + NULL); |
4140 | + dbus_test_dbus_mock_object_emit_signal( |
4141 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4142 | + g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"), |
4143 | + NULL); |
4144 | + |
4145 | + g_usleep(100000); |
4146 | + while (g_main_pending()) |
4147 | + { |
4148 | + g_main_iteration(TRUE); |
4149 | + } |
4150 | + |
4151 | + /* Ensure we just signaled once for each */ |
4152 | + ASSERT_EQ(start_data.count, 1); |
4153 | + ASSERT_EQ(stop_data.count, 1); |
4154 | + |
4155 | + /* Remove */ |
4156 | + ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_started(observer_cb, &start_data)); |
4157 | + ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_stop(observer_cb, &stop_data)); |
4158 | +} |
4159 | + |
4160 | +static GDBusMessage* filter_starting(GDBusConnection* conn, |
4161 | + GDBusMessage* message, |
4162 | + gboolean incomming, |
4163 | + gpointer user_data) |
4164 | +{ |
4165 | + if (g_strcmp0(g_dbus_message_get_member(message), "UnityStartingSignal") == 0) |
4166 | + { |
4167 | + unsigned int* count = static_cast<unsigned int*>(user_data); |
4168 | + (*count)++; |
4169 | + g_object_unref(message); |
4170 | + return NULL; |
4171 | + } |
4172 | + |
4173 | + return message; |
4174 | +} |
4175 | + |
4176 | +static void starting_observer(const gchar* appid, gpointer user_data) |
4177 | +{ |
4178 | + std::string* last = static_cast<std::string*>(user_data); |
4179 | + *last = appid; |
4180 | + return; |
4181 | +} |
4182 | + |
4183 | +TEST_F(LibUAL, StartingResponses) |
4184 | +{ |
4185 | + std::string last_observer; |
4186 | + unsigned int starting_count = 0; |
4187 | + GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4188 | + guint filter = g_dbus_connection_add_filter(session, filter_starting, &starting_count, NULL); |
4189 | + |
4190 | + EXPECT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_observer, &last_observer)); |
4191 | + |
4192 | + g_dbus_connection_emit_signal(session, NULL, /* destination */ |
4193 | + "/", /* path */ |
4194 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
4195 | + "UnityStartingBroadcast", /* signal */ |
4196 | + g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */ |
4197 | + NULL); |
4198 | + |
4199 | + pause(100); |
4200 | + |
4201 | + EXPECT_EQ("com.test.good_application_1.2.3", last_observer); |
4202 | + EXPECT_EQ(1, starting_count); |
4203 | + |
4204 | + EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_starting(starting_observer, &last_observer)); |
4205 | + |
4206 | + g_dbus_connection_remove_filter(session, filter); |
4207 | + g_object_unref(session); |
4208 | +} |
4209 | + |
4210 | +TEST_F(LibUAL, AppIdTest) |
4211 | +{ |
4212 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
4213 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
4214 | + app->launch(); |
4215 | + |
4216 | + pause(50); /* Ensure all the events come through */ |
4217 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
4218 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
4219 | +} |
4220 | + |
4221 | +GDBusMessage* filter_func_good(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data) |
4222 | +{ |
4223 | + if (!incomming) |
4224 | + { |
4225 | + return message; |
4226 | + } |
4227 | + |
4228 | + if (g_strcmp0(g_dbus_message_get_path(message), (gchar*)user_data) == 0) |
4229 | + { |
4230 | + GDBusMessage* reply = g_dbus_message_new_method_reply(message); |
4231 | + g_dbus_connection_send_message(conn, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
4232 | + g_object_unref(message); |
4233 | + return NULL; |
4234 | + } |
4235 | + |
4236 | + return message; |
4237 | +} |
4238 | + |
4239 | +TEST_F(LibUAL, UrlSendTest) |
4240 | +{ |
4241 | + GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4242 | + guint filter = g_dbus_connection_add_filter(session, filter_func_good, |
4243 | + (gpointer) "/com_2etest_2egood_5fapplication_5f1_2e2_2e3", NULL); |
4244 | + |
4245 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
4246 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
4247 | + std::vector<ubuntu::app_launch::Application::URL> uris = { |
4248 | + ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")}; |
4249 | + |
4250 | + app->launch(uris); |
4251 | + |
4252 | + pause(100); /* Ensure all the events come through */ |
4253 | + |
4254 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
4255 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
4256 | + |
4257 | + g_dbus_connection_remove_filter(session, filter); |
4258 | + |
4259 | + /* Send multiple resume responses to ensure we unsubscribe */ |
4260 | + /* Multiple to increase our chance of hitting a bad free in the middle, |
4261 | + fun with async! */ |
4262 | + int i; |
4263 | + for (i = 0; i < 5; i++) |
4264 | + { |
4265 | + g_dbus_connection_emit_signal(session, NULL, /* destination */ |
4266 | + "/", /* path */ |
4267 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
4268 | + "UnityResumeResponse", /* signal */ |
4269 | + g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */ |
4270 | + NULL); |
4271 | + |
4272 | + pause(50); /* Ensure all the events come through */ |
4273 | + } |
4274 | + |
4275 | + g_object_unref(session); |
4276 | +} |
4277 | + |
4278 | +TEST_F(LibUAL, UrlSendNoObjectTest) |
4279 | +{ |
4280 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
4281 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
4282 | + std::vector<ubuntu::app_launch::Application::URL> uris = { |
4283 | + ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")}; |
4284 | + |
4285 | + app->launch(uris); |
4286 | + |
4287 | + pause(100); /* Ensure all the events come through */ |
4288 | + |
4289 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
4290 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
4291 | +} |
4292 | + |
4293 | +TEST_F(LibUAL, UnityTimeoutTest) |
4294 | +{ |
4295 | + this->resume_timeout = 100; |
4296 | + |
4297 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
4298 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
4299 | + |
4300 | + app->launch(); |
4301 | + |
4302 | + pause(1000); /* Ensure all the events come through */ |
4303 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
4304 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
4305 | +} |
4306 | + |
4307 | +TEST_F(LibUAL, UnityTimeoutUriTest) |
4308 | +{ |
4309 | + this->resume_timeout = 200; |
4310 | + |
4311 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
4312 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
4313 | + std::vector<ubuntu::app_launch::Application::URL> uris = { |
4314 | + ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")}; |
4315 | + |
4316 | + app->launch(uris); |
4317 | + |
4318 | + pause(1000); /* Ensure all the events come through */ |
4319 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
4320 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
4321 | +} |
4322 | + |
4323 | +GDBusMessage* filter_respawn(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data) |
4324 | +{ |
4325 | + if (g_strcmp0(g_dbus_message_get_member(message), "UnityResumeResponse") == 0) |
4326 | + { |
4327 | + g_object_unref(message); |
4328 | + return NULL; |
4329 | + } |
4330 | + |
4331 | + return message; |
4332 | +} |
4333 | + |
4334 | +TEST_F(LibUAL, UnityLostTest) |
4335 | +{ |
4336 | + GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4337 | + guint filter = g_dbus_connection_add_filter(session, filter_respawn, NULL, NULL); |
4338 | + |
4339 | + guint start = g_get_monotonic_time(); |
4340 | + |
4341 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"); |
4342 | + auto app = ubuntu::app_launch::Application::create(appid, registry); |
4343 | + std::vector<ubuntu::app_launch::Application::URL> uris = { |
4344 | + ubuntu::app_launch::Application::URL::from_raw("http://www.test.com")}; |
4345 | + |
4346 | + app->launch(uris); |
4347 | + |
4348 | + guint end = g_get_monotonic_time(); |
4349 | + |
4350 | + g_debug("Start call time: %d ms", (end - start) / 1000); |
4351 | + EXPECT_LT(end - start, 2000 * 1000); |
4352 | + |
4353 | + pause(1000); /* Ensure all the events come through */ |
4354 | + |
4355 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
4356 | + EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
4357 | + |
4358 | + g_dbus_connection_remove_filter(session, filter); |
4359 | + g_object_unref(session); |
4360 | +} |
4361 | + |
4362 | +TEST_F(LibUAL, LegacySingleInstance) |
4363 | +{ |
4364 | + DbusTestDbusMockObject* obj = |
4365 | + dbus_test_dbus_mock_get_object(mock, "/com/test/application_legacy", "com.ubuntu.Upstart0_6.Job", NULL); |
4366 | + |
4367 | + /* Check for a single-instance app */ |
4368 | + auto singleappid = ubuntu::app_launch::AppID::find("single"); |
4369 | + auto singleapp = ubuntu::app_launch::Application::create(singleappid, registry); |
4370 | + |
4371 | + singleapp->launch(); |
4372 | + |
4373 | + guint len = 0; |
4374 | + const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
4375 | + EXPECT_NE(nullptr, calls); |
4376 | + EXPECT_EQ(1, len); |
4377 | + |
4378 | + EXPECT_STREQ("Start", calls->name); |
4379 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
4380 | + |
4381 | + GVariant* block = g_variant_get_child_value(calls->params, 1); |
4382 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
4383 | + g_variant_unref(block); |
4384 | + |
4385 | + GVariant* env = g_variant_get_child_value(calls->params, 0); |
4386 | + EXPECT_TRUE(check_env(env, "APP_ID", "single")); |
4387 | + EXPECT_TRUE(check_env(env, "INSTANCE_ID", "")); |
4388 | + g_variant_unref(env); |
4389 | + |
4390 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4391 | + |
4392 | + /* Check for a multi-instance app */ |
4393 | + auto multipleappid = ubuntu::app_launch::AppID::find("multiple"); |
4394 | + auto multipleapp = ubuntu::app_launch::Application::create(multipleappid, registry); |
4395 | + |
4396 | + multipleapp->launch(); |
4397 | + |
4398 | + len = 0; |
4399 | + calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
4400 | + EXPECT_NE(nullptr, calls); |
4401 | + EXPECT_EQ(1, len); |
4402 | + |
4403 | + EXPECT_STREQ("Start", calls->name); |
4404 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
4405 | + |
4406 | + block = g_variant_get_child_value(calls->params, 1); |
4407 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
4408 | + g_variant_unref(block); |
4409 | + |
4410 | + env = g_variant_get_child_value(calls->params, 0); |
4411 | + EXPECT_TRUE(check_env(env, "APP_ID", "multiple")); |
4412 | + EXPECT_FALSE(check_env(env, "INSTANCE_ID", "")); |
4413 | + g_variant_unref(env); |
4414 | +} |
4415 | + |
4416 | +static void failed_observer(const gchar* appid, UbuntuAppLaunchAppFailed reason, gpointer user_data) |
4417 | +{ |
4418 | + if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH) |
4419 | + { |
4420 | + std::string* last = static_cast<std::string*>(user_data); |
4421 | + *last = appid; |
4422 | + } |
4423 | + return; |
4424 | +} |
4425 | + |
4426 | +TEST_F(LibUAL, FailingObserver) |
4427 | +{ |
4428 | + std::string last_observer; |
4429 | + GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4430 | + |
4431 | + EXPECT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer)); |
4432 | + |
4433 | + g_dbus_connection_emit_signal( |
4434 | + session, NULL, /* destination */ |
4435 | + "/", /* path */ |
4436 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
4437 | + "ApplicationFailed", /* signal */ |
4438 | + g_variant_new("(ss)", "com.test.good_application_1.2.3", "crash"), /* params, the same */ |
4439 | + NULL); |
4440 | + |
4441 | + pause(100); |
4442 | + |
4443 | + EXPECT_EQ("com.test.good_application_1.2.3", last_observer); |
4444 | + |
4445 | + last_observer.clear(); |
4446 | + |
4447 | + g_dbus_connection_emit_signal( |
4448 | + session, NULL, /* destination */ |
4449 | + "/", /* path */ |
4450 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
4451 | + "ApplicationFailed", /* signal */ |
4452 | + g_variant_new("(ss)", "com.test.good_application_1.2.3", "blahblah"), /* params, the same */ |
4453 | + NULL); |
4454 | + |
4455 | + pause(100); |
4456 | + |
4457 | + EXPECT_EQ("com.test.good_application_1.2.3", last_observer); |
4458 | + |
4459 | + last_observer.clear(); |
4460 | + |
4461 | + g_dbus_connection_emit_signal( |
4462 | + session, NULL, /* destination */ |
4463 | + "/", /* path */ |
4464 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
4465 | + "ApplicationFailed", /* signal */ |
4466 | + g_variant_new("(ss)", "com.test.good_application_1.2.3", "start-failure"), /* params, the same */ |
4467 | + NULL); |
4468 | + |
4469 | + pause(100); |
4470 | + |
4471 | + EXPECT_TRUE(last_observer.empty()); |
4472 | + |
4473 | + EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer)); |
4474 | + |
4475 | + g_object_unref(session); |
4476 | +} |
4477 | + |
4478 | +TEST_F(LibUAL, StartHelper) |
4479 | +{ |
4480 | + DbusTestDbusMockObject* obj = |
4481 | + dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL); |
4482 | + |
4483 | + auto untrusted = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type"); |
4484 | + |
4485 | + /* Basic make sure we can send the event */ |
4486 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3"); |
4487 | + auto helper = ubuntu::app_launch::Helper::create(untrusted, appid, registry); |
4488 | + |
4489 | + helper->launch(); |
4490 | + |
4491 | + EXPECT_EQ(1, dbus_test_dbus_mock_object_check_method_call(mock, obj, "Start", NULL, NULL)); |
4492 | + |
4493 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4494 | + |
4495 | + /* Now check a multi out */ |
4496 | + helper->launch(); |
4497 | + |
4498 | + guint len = 0; |
4499 | + auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
4500 | + EXPECT_NE(nullptr, calls); |
4501 | + EXPECT_EQ(1, len); |
4502 | + |
4503 | + EXPECT_STREQ("Start", calls->name); |
4504 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
4505 | + |
4506 | + auto block = g_variant_get_child_value(calls->params, 1); |
4507 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
4508 | + g_variant_unref(block); |
4509 | + |
4510 | + auto env = g_variant_get_child_value(calls->params, 0); |
4511 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3")); |
4512 | + EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type")); |
4513 | + g_variant_unref(env); |
4514 | + |
4515 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4516 | + |
4517 | + /* Let's pass some URLs */ |
4518 | + std::vector<ubuntu::app_launch::Helper::URL> urls = { |
4519 | + ubuntu::app_launch::Helper::URL::from_raw("http://ubuntu.com/"), |
4520 | + ubuntu::app_launch::Helper::URL::from_raw("https://ubuntu.com/"), |
4521 | + ubuntu::app_launch::Helper::URL::from_raw("file:///home/phablet/test.txt")}; |
4522 | + helper->launch(urls); |
4523 | + |
4524 | + len = 0; |
4525 | + calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
4526 | + EXPECT_NE(nullptr, calls); |
4527 | + EXPECT_EQ(1, len); |
4528 | + |
4529 | + env = g_variant_get_child_value(calls->params, 0); |
4530 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3")); |
4531 | + EXPECT_TRUE( |
4532 | + check_env(env, "APP_URIS", "'http://ubuntu.com/' 'https://ubuntu.com/' 'file:///home/phablet/test.txt'")); |
4533 | + EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type")); |
4534 | + EXPECT_FALSE(check_env(env, "INSTANCE_ID", NULL)); |
4535 | + g_variant_unref(env); |
4536 | + |
4537 | + return; |
4538 | +} |
4539 | + |
4540 | +TEST_F(LibUAL, StopHelper) |
4541 | +{ |
4542 | + DbusTestDbusMockObject* obj = |
4543 | + dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL); |
4544 | + |
4545 | + /* Multi helper */ |
4546 | + auto untrusted = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type"); |
4547 | + |
4548 | + auto appid = ubuntu::app_launch::AppID::parse("com.bar_foo_8432.13.1"); |
4549 | + auto helper = ubuntu::app_launch::Helper::create(untrusted, appid, registry); |
4550 | + |
4551 | + ASSERT_TRUE(helper->hasInstances()); |
4552 | + |
4553 | + auto instances = helper->instances(); |
4554 | + |
4555 | + EXPECT_EQ(1, instances.size()); |
4556 | + |
4557 | + instances[0]->stop(); |
4558 | + |
4559 | + ASSERT_EQ(dbus_test_dbus_mock_object_check_method_call(mock, obj, "Stop", NULL, NULL), 1); |
4560 | + |
4561 | + guint len = 0; |
4562 | + auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Stop", &len, NULL); |
4563 | + EXPECT_NE(nullptr, calls); |
4564 | + EXPECT_EQ(1, len); |
4565 | + |
4566 | + EXPECT_STREQ("Stop", calls->name); |
4567 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
4568 | + |
4569 | + auto block = g_variant_get_child_value(calls->params, 1); |
4570 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
4571 | + g_variant_unref(block); |
4572 | + |
4573 | + auto env = g_variant_get_child_value(calls->params, 0); |
4574 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.bar_foo_8432.13.1")); |
4575 | + EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type")); |
4576 | + EXPECT_TRUE(check_env(env, "INSTANCE_ID", "24034582324132")); |
4577 | + g_variant_unref(env); |
4578 | + |
4579 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4580 | + |
4581 | + return; |
4582 | +} |
4583 | + |
4584 | +TEST_F(LibUAL, HelperList) |
4585 | +{ |
4586 | + auto nothelper = ubuntu::app_launch::Helper::Type::from_raw("not-a-type"); |
4587 | + auto notlist = ubuntu::app_launch::Registry::runningHelpers(nothelper, registry); |
4588 | + |
4589 | + EXPECT_EQ(0, notlist.size()); |
4590 | + |
4591 | + auto goodhelper = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type"); |
4592 | + auto goodlist = ubuntu::app_launch::Registry::runningHelpers(goodhelper, registry); |
4593 | + |
4594 | + EXPECT_EQ(2, goodlist.size()); |
4595 | + |
4596 | + goodlist.sort( |
4597 | + [](const std::shared_ptr<ubuntu::app_launch::Helper>& a, const std::shared_ptr<ubuntu::app_launch::Helper>& b) |
4598 | + { |
4599 | + std::string sa = a->appId(); |
4600 | + std::string sb = b->appId(); |
4601 | + |
4602 | + return sa < sb; |
4603 | + }); |
4604 | + |
4605 | + EXPECT_EQ("com.bar_foo_8432.13.1", (std::string)goodlist.front()->appId()); |
4606 | + EXPECT_EQ("com.foo_bar_43.23.12", (std::string)goodlist.back()->appId()); |
4607 | + |
4608 | + EXPECT_TRUE(goodlist.front()->hasInstances()); |
4609 | + EXPECT_TRUE(goodlist.back()->hasInstances()); |
4610 | + |
4611 | + EXPECT_EQ(1, goodlist.front()->instances().size()); |
4612 | + EXPECT_EQ(1, goodlist.back()->instances().size()); |
4613 | + |
4614 | + EXPECT_TRUE(goodlist.front()->instances()[0]->isRunning()); |
4615 | + EXPECT_TRUE(goodlist.back()->instances()[0]->isRunning()); |
4616 | +} |
4617 | + |
4618 | +typedef struct |
4619 | +{ |
4620 | + unsigned int count; |
4621 | + const gchar* appid; |
4622 | + const gchar* type; |
4623 | + const gchar* instance; |
4624 | +} helper_observer_data_t; |
4625 | + |
4626 | +static void helper_observer_cb(const gchar* appid, const gchar* instance, const gchar* type, gpointer user_data) |
4627 | +{ |
4628 | + helper_observer_data_t* data = (helper_observer_data_t*)user_data; |
4629 | + |
4630 | + if (g_strcmp0(data->appid, appid) == 0 && g_strcmp0(data->type, type) == 0 && |
4631 | + g_strcmp0(data->instance, instance) == 0) |
4632 | + { |
4633 | + data->count++; |
4634 | + } |
4635 | +} |
4636 | + |
4637 | +TEST_F(LibUAL, StartStopHelperObserver) |
4638 | +{ |
4639 | + helper_observer_data_t start_data = { |
4640 | + .count = 0, .appid = "com.foo_foo_1.2.3", .type = "my-type-is-scorpio", .instance = nullptr}; |
4641 | + helper_observer_data_t stop_data = { |
4642 | + .count = 0, .appid = "com.bar_bar_44.32", .type = "my-type-is-libra", .instance = "1234"}; |
4643 | + |
4644 | + ASSERT_TRUE(ubuntu_app_launch_observer_add_helper_started(helper_observer_cb, "my-type-is-scorpio", &start_data)); |
4645 | + ASSERT_TRUE(ubuntu_app_launch_observer_add_helper_stop(helper_observer_cb, "my-type-is-libra", &stop_data)); |
4646 | + |
4647 | + DbusTestDbusMockObject* obj = |
4648 | + dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL); |
4649 | + |
4650 | + /* Basic start */ |
4651 | + dbus_test_dbus_mock_object_emit_signal( |
4652 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4653 | + g_variant_new_parsed("('started', ['JOB=untrusted-helper', 'INSTANCE=my-type-is-scorpio::com.foo_foo_1.2.3'])"), |
4654 | + NULL); |
4655 | + |
4656 | + g_usleep(100000); |
4657 | + while (g_main_pending()) |
4658 | + { |
4659 | + g_main_iteration(TRUE); |
4660 | + } |
4661 | + |
4662 | + ASSERT_EQ(start_data.count, 1); |
4663 | + |
4664 | + /* Basic stop */ |
4665 | + dbus_test_dbus_mock_object_emit_signal( |
4666 | + mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), |
4667 | + g_variant_new_parsed( |
4668 | + "('stopped', ['JOB=untrusted-helper', 'INSTANCE=my-type-is-libra:1234:com.bar_bar_44.32'])"), |
4669 | + NULL); |
4670 | + |
4671 | + g_usleep(100000); |
4672 | + while (g_main_pending()) |
4673 | + { |
4674 | + g_main_iteration(TRUE); |
4675 | + } |
4676 | + |
4677 | + ASSERT_EQ(stop_data.count, 1); |
4678 | + |
4679 | + /* Remove */ |
4680 | + ASSERT_TRUE( |
4681 | + ubuntu_app_launch_observer_delete_helper_started(helper_observer_cb, "my-type-is-scorpio", &start_data)); |
4682 | + ASSERT_TRUE(ubuntu_app_launch_observer_delete_helper_stop(helper_observer_cb, "my-type-is-libra", &stop_data)); |
4683 | +} |
4684 | + |
4685 | +gboolean datain(GIOChannel* source, GIOCondition cond, gpointer data) |
4686 | +{ |
4687 | + gsize* datacnt = static_cast<gsize*>(data); |
4688 | + gchar* str = NULL; |
4689 | + gsize len = 0; |
4690 | + GError* error = NULL; |
4691 | + |
4692 | + g_io_channel_read_line(source, &str, &len, NULL, &error); |
4693 | + g_free(str); |
4694 | + |
4695 | + if (error != NULL) |
4696 | + { |
4697 | + g_warning("Unable to read from channel: %s", error->message); |
4698 | + g_error_free(error); |
4699 | + } |
4700 | + |
4701 | + *datacnt += len; |
4702 | + |
4703 | + return TRUE; |
4704 | +} |
4705 | + |
4706 | +static void signal_increment(GDBusConnection* connection, |
4707 | + const gchar* sender, |
4708 | + const gchar* path, |
4709 | + const gchar* interface, |
4710 | + const gchar* signal, |
4711 | + GVariant* params, |
4712 | + gpointer user_data) |
4713 | +{ |
4714 | + guint* count = (guint*)user_data; |
4715 | + g_debug("Count incremented to: %d", *count + 1); |
4716 | + *count = *count + 1; |
4717 | +} |
4718 | + |
4719 | +TEST_F(LibUAL, PauseResume) |
4720 | +{ |
4721 | + g_setenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH", CMAKE_BINARY_DIR "/libual-proc", 1); |
4722 | + |
4723 | + /* Setup some spew */ |
4724 | + GPid spewpid = 0; |
4725 | + gint spewstdout = 0; |
4726 | + const gchar* spewline[] = {SPEW_UTILITY, NULL}; |
4727 | + ASSERT_TRUE(g_spawn_async_with_pipes(NULL, (gchar**)spewline, NULL, /* environment */ |
4728 | + G_SPAWN_DEFAULT, NULL, NULL, /* child setup */ |
4729 | + &spewpid, NULL, /* stdin */ |
4730 | + &spewstdout, NULL, /* stderr */ |
4731 | + NULL)); /* error */ |
4732 | + |
4733 | + gsize datacnt = 0; |
4734 | + GIOChannel* spewoutchan = g_io_channel_unix_new(spewstdout); |
4735 | + g_io_channel_set_flags(spewoutchan, G_IO_FLAG_NONBLOCK, NULL); |
4736 | + g_io_add_watch(spewoutchan, G_IO_IN, datain, &datacnt); |
4737 | + |
4738 | + /* Setup our OOM adjust file */ |
4739 | + gchar* procdir = g_strdup_printf(CMAKE_BINARY_DIR "/libual-proc/%d", spewpid); |
4740 | + ASSERT_EQ(0, g_mkdir_with_parents(procdir, 0700)); |
4741 | + gchar* oomadjfile = g_strdup_printf("%s/oom_score_adj", procdir); |
4742 | + g_free(procdir); |
4743 | + ASSERT_TRUE(g_file_set_contents(oomadjfile, "0", -1, NULL)); |
4744 | + |
4745 | + /* Setup the cgroup */ |
4746 | + g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME", "org.test.cgmock2", TRUE); |
4747 | + DbusTestDbusMock* cgmock2 = dbus_test_dbus_mock_new("org.test.cgmock2"); |
4748 | + DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock2, "/org/linuxcontainers/cgmanager", |
4749 | + "org.linuxcontainers.cgmanager0_0", NULL); |
4750 | + gchar* pypids = g_strdup_printf("ret = [%d]", spewpid); |
4751 | + dbus_test_dbus_mock_object_add_method(cgmock, cgobject, "GetTasksRecursive", G_VARIANT_TYPE("(ss)"), |
4752 | + G_VARIANT_TYPE("ai"), pypids, NULL); |
4753 | + g_free(pypids); |
4754 | + |
4755 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(cgmock2)); |
4756 | + dbus_test_task_run(DBUS_TEST_TASK(cgmock2)); |
4757 | + g_object_unref(G_OBJECT(cgmock2)); |
4758 | + |
4759 | + /* Setup ZG Mock */ |
4760 | + DbusTestDbusMock* zgmock = dbus_test_dbus_mock_new("org.gnome.zeitgeist.Engine"); |
4761 | + DbusTestDbusMockObject* zgobj = |
4762 | + dbus_test_dbus_mock_get_object(zgmock, "/org/gnome/zeitgeist/log/activity", "org.gnome.zeitgeist.Log", NULL); |
4763 | + |
4764 | + dbus_test_dbus_mock_object_add_method(zgmock, zgobj, "InsertEvents", G_VARIANT_TYPE("a(asaasay)"), |
4765 | + G_VARIANT_TYPE("au"), "ret = [ 0 ]", NULL); |
4766 | + |
4767 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(zgmock)); |
4768 | + dbus_test_task_run(DBUS_TEST_TASK(zgmock)); |
4769 | + g_object_unref(G_OBJECT(zgmock)); |
4770 | + |
4771 | + /* Give things a chance to start */ |
4772 | + do |
4773 | + { |
4774 | + g_debug("Giving mocks a chance to start"); |
4775 | + pause(200); |
4776 | + } while (dbus_test_task_get_state(DBUS_TEST_TASK(cgmock2)) != DBUS_TEST_TASK_STATE_RUNNING && |
4777 | + dbus_test_task_get_state(DBUS_TEST_TASK(zgmock)) != DBUS_TEST_TASK_STATE_RUNNING); |
4778 | + |
4779 | + /* Setup signal handling */ |
4780 | + guint paused_count = 0; |
4781 | + guint resumed_count = 0; |
4782 | + guint paused_signal = |
4783 | + g_dbus_connection_signal_subscribe(bus, nullptr, "com.canonical.UbuntuAppLaunch", "ApplicationPaused", "/", |
4784 | + nullptr, G_DBUS_SIGNAL_FLAGS_NONE, signal_increment, &paused_count, nullptr); |
4785 | + guint resumed_signal = g_dbus_connection_signal_subscribe( |
4786 | + bus, nullptr, "com.canonical.UbuntuAppLaunch", "ApplicationResumed", "/", nullptr, G_DBUS_SIGNAL_FLAGS_NONE, |
4787 | + signal_increment, &resumed_count, nullptr); |
4788 | + |
4789 | + /* Test it */ |
4790 | + EXPECT_NE(0, datacnt); |
4791 | + paused_count = 0; |
4792 | + |
4793 | + /* Pause the app */ |
4794 | + EXPECT_TRUE(ubuntu_app_launch_pause_application("com.test.good_application_1.2.3")); |
4795 | + |
4796 | + pause(0); /* Flush queued events */ |
4797 | + datacnt = 0; /* clear it */ |
4798 | + |
4799 | + pause(200); |
4800 | + |
4801 | + /* Check data coming out */ |
4802 | + EXPECT_EQ(1, paused_count); |
4803 | + EXPECT_EQ(0, datacnt); |
4804 | + |
4805 | + /* Check to make sure we sent the event to ZG */ |
4806 | + guint numcalls = 0; |
4807 | + const DbusTestDbusMockCall* calls = |
4808 | + dbus_test_dbus_mock_object_get_method_calls(zgmock, zgobj, "InsertEvents", &numcalls, NULL); |
4809 | + |
4810 | + EXPECT_NE(nullptr, calls); |
4811 | + EXPECT_EQ(1, numcalls); |
4812 | + |
4813 | + dbus_test_dbus_mock_object_clear_method_calls(zgmock, zgobj, NULL); |
4814 | + |
4815 | + /* Check to ensure we set the OOM score */ |
4816 | + gchar* pauseoomscore = NULL; |
4817 | + ASSERT_TRUE(g_file_get_contents(oomadjfile, &pauseoomscore, NULL, NULL)); |
4818 | + EXPECT_STREQ("900", pauseoomscore); |
4819 | + g_free(pauseoomscore); |
4820 | + resumed_count = 0; |
4821 | + |
4822 | + /* Now Resume the App */ |
4823 | + EXPECT_TRUE(ubuntu_app_launch_resume_application("com.test.good_application_1.2.3")); |
4824 | + |
4825 | + pause(200); |
4826 | + |
4827 | + EXPECT_NE(0, datacnt); |
4828 | + EXPECT_EQ(1, resumed_count); |
4829 | + |
4830 | + /* Check to make sure we sent the event to ZG */ |
4831 | + numcalls = 0; |
4832 | + calls = dbus_test_dbus_mock_object_get_method_calls(zgmock, zgobj, "InsertEvents", &numcalls, NULL); |
4833 | + |
4834 | + EXPECT_NE(nullptr, calls); |
4835 | + EXPECT_EQ(1, numcalls); |
4836 | + |
4837 | + /* Check to ensure we set the OOM score */ |
4838 | + gchar* resumeoomscore = NULL; |
4839 | + ASSERT_TRUE(g_file_get_contents(oomadjfile, &resumeoomscore, NULL, NULL)); |
4840 | + EXPECT_STREQ("100", resumeoomscore); |
4841 | + g_free(resumeoomscore); |
4842 | + |
4843 | + /* Clean up */ |
4844 | + gchar* killstr = g_strdup_printf("kill -9 %d", spewpid); |
4845 | + ASSERT_TRUE(g_spawn_command_line_sync(killstr, NULL, NULL, NULL, NULL)); |
4846 | + g_free(killstr); |
4847 | + |
4848 | + g_io_channel_unref(spewoutchan); |
4849 | + |
4850 | + g_spawn_command_line_sync("rm -rf " CMAKE_BINARY_DIR "/libual-proc", NULL, NULL, NULL, NULL); |
4851 | + |
4852 | + g_dbus_connection_signal_unsubscribe(bus, paused_signal); |
4853 | + g_dbus_connection_signal_unsubscribe(bus, resumed_signal); |
4854 | + |
4855 | + /* Kill ZG default instance :-( */ |
4856 | + ZeitgeistLog* log = zeitgeist_log_get_default(); |
4857 | + g_object_unref(log); |
4858 | + g_object_unref(log); |
4859 | + |
4860 | + g_free(oomadjfile); |
4861 | +} |
4862 | + |
4863 | +TEST_F(LibUAL, StartSessionHelper) |
4864 | +{ |
4865 | + DbusTestDbusMockObject* obj = |
4866 | + dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL); |
4867 | + MirConnection* conn = mir_connect_sync("libual-test", "start-session-helper"); // Mocked, doesn't need cleaning up |
4868 | + MirPromptSession* msession = mir_connection_create_prompt_session_sync(conn, 5, nullptr, nullptr); |
4869 | + |
4870 | + /* Building a temporary file and making an FD for it */ |
4871 | + const char* filedata = "This is some data that we should get on the other side\n"; |
4872 | + ASSERT_TRUE(g_file_set_contents(SESSION_TEMP_FILE, filedata, strlen(filedata), nullptr) == TRUE); |
4873 | + int mirfd = open(SESSION_TEMP_FILE, 0); |
4874 | + mir_mock_set_trusted_fd(mirfd); |
4875 | + |
4876 | + /* Basic make sure we can send the event */ |
4877 | + auto untrusted = ubuntu::app_launch::Helper::Type::from_raw("untrusted-type"); |
4878 | + auto appid = ubuntu::app_launch::AppID::parse("com.test.multiple_first_1.2.3"); |
4879 | + auto helper = ubuntu::app_launch::Helper::create(untrusted, appid, registry); |
4880 | + |
4881 | + helper->launch(msession); |
4882 | + |
4883 | + guint len = 0; |
4884 | + const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); |
4885 | + EXPECT_NE(nullptr, calls); |
4886 | + EXPECT_EQ(1, len); |
4887 | + |
4888 | + EXPECT_STREQ("Start", calls->name); |
4889 | + EXPECT_EQ(2, g_variant_n_children(calls->params)); |
4890 | + |
4891 | + GVariant* block = g_variant_get_child_value(calls->params, 1); |
4892 | + EXPECT_TRUE(g_variant_get_boolean(block)); |
4893 | + g_variant_unref(block); |
4894 | + |
4895 | + /* Check the environment */ |
4896 | + GVariant* env = g_variant_get_child_value(calls->params, 0); |
4897 | + EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3")); |
4898 | + EXPECT_TRUE(check_env(env, "HELPER_TYPE", "untrusted-type")); |
4899 | + |
4900 | + GVariant* mnamev = find_env(env, "UBUNTU_APP_LAUNCH_DEMANGLE_NAME"); |
4901 | + ASSERT_NE(nullptr, mnamev); /* Have to assert because, eh, GVariant */ |
4902 | + EXPECT_STREQ(g_dbus_connection_get_unique_name(bus), |
4903 | + g_variant_get_string(mnamev, nullptr) + strlen("UBUNTU_APP_LAUNCH_DEMANGLE_NAME=")); |
4904 | + GVariant* mpathv = find_env(env, "UBUNTU_APP_LAUNCH_DEMANGLE_PATH"); |
4905 | + ASSERT_NE(nullptr, mpathv); /* Have to assert because, eh, GVariant */ |
4906 | + |
4907 | + g_variant_unref(env); |
4908 | + |
4909 | + /* Setup environment for call */ |
4910 | + const gchar* mname = g_variant_get_string(mnamev, nullptr); |
4911 | + mname += strlen("UBUNTU_APP_LAUNCH_DEMANGLE_NAME="); |
4912 | + g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", mname, TRUE); |
4913 | + g_variant_unref(mnamev); |
4914 | + |
4915 | + const gchar* mpath = g_variant_get_string(mpathv, nullptr); |
4916 | + mpath += strlen("UBUNTU_APP_LAUNCH_DEMANGLE_PATH="); |
4917 | + g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_PATH", mpath, TRUE); |
4918 | + g_variant_unref(mpathv); |
4919 | + |
4920 | + /* Exec our tool */ |
4921 | + std::promise<std::string> outputpromise; |
4922 | + std::thread t( |
4923 | + [&outputpromise]() |
4924 | + { |
4925 | + gchar* socketstdout = nullptr; |
4926 | + GError* error = nullptr; |
4927 | + g_unsetenv("G_MESSAGES_DEBUG"); |
4928 | + |
4929 | + g_spawn_command_line_sync(SOCKET_DEMANGLER " " SOCKET_TOOL, &socketstdout, nullptr, nullptr, &error); |
4930 | + |
4931 | + if (error != nullptr) |
4932 | + { |
4933 | + fprintf(stderr, "Unable to spawn '" SOCKET_DEMANGLER " " SOCKET_TOOL "': %s\n", error->message); |
4934 | + g_error_free(error); |
4935 | + outputpromise.set_value(std::string("")); |
4936 | + } |
4937 | + else |
4938 | + { |
4939 | + outputpromise.set_value(std::string(socketstdout)); |
4940 | + g_free(socketstdout); |
4941 | + } |
4942 | + }); |
4943 | + t.detach(); |
4944 | + |
4945 | + auto outputfuture = outputpromise.get_future(); |
4946 | + while (outputfuture.wait_for(std::chrono::milliseconds{1}) != std::future_status::ready) |
4947 | + { |
4948 | + pause(); |
4949 | + } |
4950 | + |
4951 | + ASSERT_STREQ(filedata, outputfuture.get().c_str()); |
4952 | + |
4953 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4954 | + |
4955 | + return; |
4956 | +} |
4957 | + |
4958 | +TEST_F(LibUAL, SetExec) |
4959 | +{ |
4960 | + DbusTestDbusMockObject* obj = |
4961 | + dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL); |
4962 | + |
4963 | + const char* exec = "lets exec this"; |
4964 | + |
4965 | + g_setenv("UPSTART_JOB", "fubar", TRUE); |
4966 | + g_unsetenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME"); |
4967 | + EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, NULL)); |
4968 | + |
4969 | + guint len = 0; |
4970 | + const DbusTestDbusMockCall* calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL); |
4971 | + ASSERT_NE(nullptr, calls); |
4972 | + EXPECT_EQ(1, len); |
4973 | + |
4974 | + gchar* appexecstr = g_strdup_printf("APP_EXEC=%s", exec); |
4975 | + GVariant* appexecenv = g_variant_get_child_value(calls[0].params, 1); |
4976 | + EXPECT_STREQ(appexecstr, g_variant_get_string(appexecenv, nullptr)); |
4977 | + g_variant_unref(appexecenv); |
4978 | + g_free(appexecstr); |
4979 | + |
4980 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4981 | + |
4982 | + /* Now check for the demangler */ |
4983 | + g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", g_dbus_connection_get_unique_name(bus), TRUE); |
4984 | + EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, NULL)); |
4985 | + |
4986 | + calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "SetEnv", &len, NULL); |
4987 | + ASSERT_NE(nullptr, calls); |
4988 | + EXPECT_EQ(1, len); |
4989 | + |
4990 | + gchar* demangleexecstr = g_strdup_printf("APP_EXEC=%s %s", SOCKET_DEMANGLER_INSTALL, exec); |
4991 | + appexecenv = g_variant_get_child_value(calls[0].params, 1); |
4992 | + EXPECT_STREQ(demangleexecstr, g_variant_get_string(appexecenv, nullptr)); |
4993 | + g_variant_unref(appexecenv); |
4994 | + g_free(demangleexecstr); |
4995 | + |
4996 | + ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); |
4997 | + |
4998 | + /* Now check for the directory */ |
4999 | + g_setenv("UBUNTU_APP_LAUNCH_DEMANGLE_NAME", g_dbus_connection_get_unique_name(bus), TRUE); |
5000 | + EXPECT_TRUE(ubuntu_app_launch_helper_set_exec(exec, "/not/a/real/directory")); |