Merge lp:~mterry/ubuntu-app-launch/warn-on-xapp into lp:ubuntu-app-launch/16.04
- warn-on-xapp
- Merge into trunk.16.04
Status: | Rejected |
---|---|
Rejected by: | dobey |
Proposed branch: | lp:~mterry/ubuntu-app-launch/warn-on-xapp |
Merge into: | lp:ubuntu-app-launch/16.04 |
Prerequisite: | lp:~mterry/ubuntu-app-launch/fix-ftbfs |
Diff against target: |
1337 lines (+760/-123) 16 files modified
CMakeLists.txt (+4/-0) data/com.canonical.UbuntuAppLaunch.xml (+4/-0) debian/changelog (+8/-0) debian/libubuntu-app-launch2.symbols (+3/-0) helpers.c (+133/-22) helpers.h (+7/-2) libubuntu-app-launch/click-exec.c (+0/-13) libubuntu-app-launch/desktop-exec.c (+0/-13) libubuntu-app-launch/ubuntu-app-launch-trace.tp (+18/-24) libubuntu-app-launch/ubuntu-app-launch.c (+124/-26) libubuntu-app-launch/ubuntu-app-launch.h (+41/-2) tests/CMakeLists.txt (+1/-1) tests/exec-util-test.cc (+2/-1) tests/helper-handshake-test.cc (+263/-13) tests/libual-test.cc (+151/-6) tools/ubuntu-app-watch.c (+1/-0) |
To merge this branch: | bzr merge lp:~mterry/ubuntu-app-launch/warn-on-xapp |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
dobey (community) | Needs Resubmitting | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+279323@code.launchpad.net |
This proposal supersedes a proposal from 2015-11-24.
Commit message
Allow Unity to prevent an app from starting and give it as long as it wants to figure that out.
Description of the change
Allow Unity to prevent an app from starting and give it as long as it wants to figure that out.
This branch gives Unity the option to prevent an app from launching by blocking on its answer before actually starting the app and then acting on that answer.
Related branches:
https:/
https:/
https:/
== The existing design ==
The current code goes through pains to avoid any knowledge of Unity (despite using the name Unity in several signals). It doesn't connect to it on the bus. And it doesn't restrict who can answer its "I am starting an app" signal.
I'm not sure why it does this, but it does. So the way it communicates is via broadcast signals on the bus. Which gets answered by "someone" (presumably Unity). If there are multiple signal observers on the bus, we continue launching the app as soon as we hear the first response.
It also waits 1s for an answer; if no answer is received, it just launches the app anyway. The signal is more of a courtesy to Unity than a requirement. So if Unity isn't running, exits before it can respond, or is taking too long, UAL just continues.
== New design ==
The new paradigm is that Unity is an approver of which apps can launch. The proximate reason for this MP is that we want to be able to stop legacy apps from launching when not in Desktop mode. But I could imagine other scenarios (parental controls on some apps, or admin privileges needed for some apps, etc). [1]
I also wanted to allow Unity to take as long as it wanted to decide. Because maybe it is taking user input (authenticating as parent maybe) or in the case of a legacy app on the phone, waiting for user to either cancel the app launch or dock the phone (in which case we'd like to continue the app launch).
If Unity is not running, I've assumed we don't want the app to launch (which is a behavior change).
== Changes ==
- So we want to change the API for observers of the "starting" signal to allow for providing an "approved or not" answer. And we want to make it easy to answer in an async manner. Unfortunately, the current API has no state (no objects on which the API acts) to match up callbacks to their answers. So rather than redo the API, I've just added a new API call that app-starting-
- We want to know if Unity is even running (if not, we should reject the app). So I've kept the immediate signal response UnityStartingSi
- As noted above, I've bumped the timeout from 1s to 5s. This is because now we reject by default if the timeout happens, so it's more important that we give unity8 time. And secondly, we're adding an async API, so increasing the potential blocking time shouldn't be as much of a burden.
- We only support listening to the first responder we hear on the bus. This would be easy to extend to support listening to all responders we hear and waiting for all of them to weigh in. But I didn't need that feature yet, so I didn't write it.
- Since we now wait as long as Unity wants, we have to be sensitive to Unity quitting on us. So once we hear UnityStartingSi
- Eventually, Unity gets back to us with a UnityStartingAp
- Consolidating the handshake logic out of click-exec.c and desktop-exec.c has the side effect of fixing a bug where task_setup failed but we already emitted a UnityStartingBr
- I've added an async version of ubuntu_
== Footnotes ==
[1] ubuntu_
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:215
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 216. By Michael Terry
-
Fix tests to still use old smaller timeout for convenience
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:216
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
If this is still relevant, it needs to be rebuilt/resubmitted against current trunk. Also, a bug describing the problem it's meant to solve would be nice.
Unmerged revisions
- 216. By Michael Terry
-
Fix tests to still use old smaller timeout for convenience
- 215. By Michael Terry
-
Bump timeout to 5s
- 214. By Michael Terry
-
Add async versions of start_application calls
- 213. By Michael Terry
-
fix tabs and revert some unnecessary wording changes
- 212. By Michael Terry
-
Wait for unity to get back to us
- 211. By Michael Terry
-
Allow server-side of handshake to be async by adding ubuntu_
app_launch_ observer_ finish_ app_starting - 210. By Michael Terry
-
Add 'approved' bool parameter to UnityStartingSignal return signal, so that unity could prevent an app from opening
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2015-08-11 02:45:49 +0000 | |||
3 | +++ CMakeLists.txt 2015-12-02 18:30:50 +0000 | |||
4 | @@ -90,6 +90,10 @@ | |||
5 | 90 | add_library(helpers STATIC helpers.c helpers-shared.c libubuntu-app-launch/recoverable-problem.c) | 90 | add_library(helpers STATIC helpers.c helpers-shared.c libubuntu-app-launch/recoverable-problem.c) |
6 | 91 | target_link_libraries(helpers ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES}) | 91 | target_link_libraries(helpers ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES}) |
7 | 92 | 92 | ||
8 | 93 | add_library(helpers-test STATIC helpers.c helpers-shared.c libubuntu-app-launch/recoverable-problem.c) | ||
9 | 94 | set_target_properties(helpers-test PROPERTIES COMPILE_DEFINITIONS "UAL_TEST_MODE") | ||
10 | 95 | target_link_libraries(helpers-test ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES}) | ||
11 | 96 | |||
12 | 93 | #################### | 97 | #################### |
13 | 94 | # desktop-hook | 98 | # desktop-hook |
14 | 95 | #################### | 99 | #################### |
15 | 96 | 100 | ||
16 | === modified file 'data/com.canonical.UbuntuAppLaunch.xml' | |||
17 | --- data/com.canonical.UbuntuAppLaunch.xml 2015-03-04 14:26:35 +0000 | |||
18 | +++ data/com.canonical.UbuntuAppLaunch.xml 2015-12-02 18:30:50 +0000 | |||
19 | @@ -16,6 +16,10 @@ | |||
20 | 16 | <signal name="UnityStartingSignal"> | 16 | <signal name="UnityStartingSignal"> |
21 | 17 | <arg type="s" name="appid" /> | 17 | <arg type="s" name="appid" /> |
22 | 18 | </signal> | 18 | </signal> |
23 | 19 | <signal name="UnityStartingApproved"> | ||
24 | 20 | <arg type="s" name="appid" /> | ||
25 | 21 | <arg type="b" name="approved" /> | ||
26 | 22 | </signal> | ||
27 | 19 | <signal name="ApplicationFailed"> | 23 | <signal name="ApplicationFailed"> |
28 | 20 | <arg type="s" name="appid" /> | 24 | <arg type="s" name="appid" /> |
29 | 21 | <arg type="s" name="stage" /> | 25 | <arg type="s" name="stage" /> |
30 | 22 | 26 | ||
31 | === modified file 'debian/changelog' | |||
32 | --- debian/changelog 2015-08-17 21:38:34 +0000 | |||
33 | +++ debian/changelog 2015-12-02 18:30:50 +0000 | |||
34 | @@ -1,3 +1,11 @@ | |||
35 | 1 | ubuntu-app-launch (0.6) xenial; urgency=medium | ||
36 | 2 | |||
37 | 3 | * Bump version because the expectations for callers of | ||
38 | 4 | ubuntu_app_launch_observer_add_app_starting has changed (to expect | ||
39 | 5 | a further call to ubuntu_app_launch_observer_finish_app_starting) | ||
40 | 6 | |||
41 | 7 | -- Michael Terry <mterry@ubuntu.com> Mon, 23 Nov 2015 16:25:15 -0500 | ||
42 | 8 | |||
43 | 1 | ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium | 9 | ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium |
44 | 2 | 10 | ||
45 | 3 | [ CI Train Bot ] | 11 | [ CI Train Bot ] |
46 | 4 | 12 | ||
47 | === modified file 'debian/libubuntu-app-launch2.symbols' | |||
48 | --- debian/libubuntu-app-launch2.symbols 2015-08-17 21:38:34 +0000 | |||
49 | +++ debian/libubuntu-app-launch2.symbols 2015-12-02 18:30:50 +0000 | |||
50 | @@ -27,10 +27,13 @@ | |||
51 | 27 | ubuntu_app_launch_observer_delete_app_stop@Base 0.4 | 27 | ubuntu_app_launch_observer_delete_app_stop@Base 0.4 |
52 | 28 | ubuntu_app_launch_observer_delete_helper_started@Base 0.4 | 28 | ubuntu_app_launch_observer_delete_helper_started@Base 0.4 |
53 | 29 | ubuntu_app_launch_observer_delete_helper_stop@Base 0.4 | 29 | ubuntu_app_launch_observer_delete_helper_stop@Base 0.4 |
54 | 30 | ubuntu_app_launch_observer_finish_app_starting@Base 0.6 | ||
55 | 30 | ubuntu_app_launch_pause_application@Base 0.4+14.10.20140915.3 | 31 | ubuntu_app_launch_pause_application@Base 0.4+14.10.20140915.3 |
56 | 31 | ubuntu_app_launch_pid_in_app_id@Base 0.4 | 32 | ubuntu_app_launch_pid_in_app_id@Base 0.4 |
57 | 32 | ubuntu_app_launch_resume_application@Base 0.4+14.10.20140915.3 | 33 | ubuntu_app_launch_resume_application@Base 0.4+14.10.20140915.3 |
58 | 33 | ubuntu_app_launch_start_application@Base 0.4 | 34 | ubuntu_app_launch_start_application@Base 0.4 |
59 | 35 | ubuntu_app_launch_start_application_async@Base 0.6 | ||
60 | 36 | ubuntu_app_launch_start_application_async_test@Base 0.6 | ||
61 | 34 | ubuntu_app_launch_start_application_test@Base 0.4 | 37 | ubuntu_app_launch_start_application_test@Base 0.4 |
62 | 35 | ubuntu_app_launch_start_helper@Base 0.4 | 38 | ubuntu_app_launch_start_helper@Base 0.4 |
63 | 36 | ubuntu_app_launch_start_multiple_helper@Base 0.4 | 39 | ubuntu_app_launch_start_multiple_helper@Base 0.4 |
64 | 37 | 40 | ||
65 | === modified file 'helpers.c' | |||
66 | --- helpers.c 2014-08-08 19:50:53 +0000 | |||
67 | +++ helpers.c 2015-12-02 18:30:50 +0000 | |||
68 | @@ -473,42 +473,146 @@ | |||
69 | 473 | return; | 473 | return; |
70 | 474 | } | 474 | } |
71 | 475 | 475 | ||
72 | 476 | static void | ||
73 | 477 | unity_signal_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) | ||
74 | 478 | { | ||
75 | 479 | GMainLoop * mainloop = (GMainLoop *)user_data; | ||
76 | 480 | g_main_loop_quit(mainloop); | ||
77 | 481 | } | ||
78 | 482 | |||
79 | 483 | struct _handshake_t { | 476 | struct _handshake_t { |
80 | 484 | GDBusConnection * con; | 477 | GDBusConnection * con; |
81 | 485 | GMainLoop * mainloop; | 478 | GMainLoop * mainloop; |
82 | 486 | guint signal_subscribe; | 479 | guint signal_subscribe; |
83 | 480 | guint approved_subscribe; | ||
84 | 481 | guint name_subscribe; | ||
85 | 487 | guint timeout; | 482 | guint timeout; |
86 | 483 | gboolean received; | ||
87 | 484 | gchar *receiver; | ||
88 | 485 | HandshakeCallback callback; | ||
89 | 486 | gpointer user_data; | ||
90 | 487 | GDestroyNotify user_data_free; | ||
91 | 488 | }; | 488 | }; |
92 | 489 | 489 | ||
93 | 490 | static void | ||
94 | 491 | handshake_free(handshake_t * handshake) | ||
95 | 492 | { | ||
96 | 493 | if (handshake->timeout != 0) { | ||
97 | 494 | g_source_remove(handshake->timeout); | ||
98 | 495 | } | ||
99 | 496 | if (handshake->mainloop != NULL) { | ||
100 | 497 | g_main_loop_unref(handshake->mainloop); | ||
101 | 498 | } | ||
102 | 499 | if (handshake->signal_subscribe != 0) { | ||
103 | 500 | g_dbus_connection_signal_unsubscribe(handshake->con, handshake->signal_subscribe); | ||
104 | 501 | } | ||
105 | 502 | if (handshake->approved_subscribe != 0) { | ||
106 | 503 | g_dbus_connection_signal_unsubscribe(handshake->con, handshake->approved_subscribe); | ||
107 | 504 | } | ||
108 | 505 | if (handshake->name_subscribe != 0) { | ||
109 | 506 | g_dbus_connection_signal_unsubscribe(handshake->con, handshake->name_subscribe); | ||
110 | 507 | } | ||
111 | 508 | g_free(handshake->receiver); | ||
112 | 509 | g_object_unref(handshake->con); | ||
113 | 510 | |||
114 | 511 | if (handshake->user_data_free != NULL && handshake->user_data != NULL) { | ||
115 | 512 | handshake->user_data_free(handshake->user_data); | ||
116 | 513 | } | ||
117 | 514 | |||
118 | 515 | g_free(handshake); | ||
119 | 516 | } | ||
120 | 517 | |||
121 | 518 | static void | ||
122 | 519 | handshake_stop(handshake_t * handshake, gboolean approved) | ||
123 | 520 | { | ||
124 | 521 | if (approved) { | ||
125 | 522 | handshake->callback(handshake->user_data); | ||
126 | 523 | } | ||
127 | 524 | |||
128 | 525 | if (handshake->mainloop != NULL) { | ||
129 | 526 | g_main_loop_quit(handshake->mainloop); | ||
130 | 527 | } | ||
131 | 528 | |||
132 | 529 | handshake_free(handshake); | ||
133 | 530 | } | ||
134 | 531 | |||
135 | 532 | static void | ||
136 | 533 | unity_name_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) | ||
137 | 534 | { | ||
138 | 535 | handshake_t * handshake = (handshake_t *)user_data; | ||
139 | 536 | |||
140 | 537 | if (g_variant_check_format_string(params, "(sss)", FALSE)) { | ||
141 | 538 | const gchar *new_name = NULL; | ||
142 | 539 | g_variant_get(params, "(ss&s)", NULL, NULL, &new_name); | ||
143 | 540 | |||
144 | 541 | if (new_name == NULL || new_name[0] == 0) { | ||
145 | 542 | // The process we were talking to exited! Let's stop waiting. | ||
146 | 543 | handshake_stop(handshake, FALSE); | ||
147 | 544 | } | ||
148 | 545 | } | ||
149 | 546 | } | ||
150 | 547 | |||
151 | 548 | static void | ||
152 | 549 | unity_signal_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) | ||
153 | 550 | { | ||
154 | 551 | handshake_t * handshake = (handshake_t *)user_data; | ||
155 | 552 | |||
156 | 553 | if (!handshake->received) { // only allow the first responder | ||
157 | 554 | handshake->receiver = g_strdup(sender); | ||
158 | 555 | handshake->received = TRUE; | ||
159 | 556 | |||
160 | 557 | handshake->name_subscribe = g_dbus_connection_signal_subscribe(handshake->con, | ||
161 | 558 | #ifdef UAL_TEST_MODE | ||
162 | 559 | NULL, /* accept from any sender, making it easier for tests to fake this signal */ | ||
163 | 560 | #else | ||
164 | 561 | "org.freedesktop.DBus", /* sender */ | ||
165 | 562 | #endif | ||
166 | 563 | "org.freedesktop.DBus", /* interface */ | ||
167 | 564 | "NameOwnerChanged", /* signal */ | ||
168 | 565 | "/org/freedesktop/DBus", /* path */ | ||
169 | 566 | sender, /* arg0 */ | ||
170 | 567 | G_DBUS_SIGNAL_FLAGS_NONE, | ||
171 | 568 | unity_name_cb, handshake, | ||
172 | 569 | NULL); /* user data destroy */ | ||
173 | 570 | } | ||
174 | 571 | } | ||
175 | 572 | |||
176 | 573 | static void | ||
177 | 574 | unity_approved_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) | ||
178 | 575 | { | ||
179 | 576 | handshake_t * handshake = (handshake_t *)user_data; | ||
180 | 577 | |||
181 | 578 | if (!handshake->received || g_strcmp0(sender, handshake->receiver) != 0) { | ||
182 | 579 | return; | ||
183 | 580 | } | ||
184 | 581 | |||
185 | 582 | gboolean approved = FALSE; | ||
186 | 583 | if (g_variant_check_format_string(params, "(sb)", FALSE)) { | ||
187 | 584 | g_variant_get(params, "(sb)", NULL, &approved); | ||
188 | 585 | } | ||
189 | 586 | |||
190 | 587 | handshake_stop(handshake, approved); | ||
191 | 588 | } | ||
192 | 589 | |||
193 | 490 | static gboolean | 590 | static gboolean |
194 | 491 | unity_too_slow_cb (gpointer user_data) | 591 | unity_too_slow_cb (gpointer user_data) |
195 | 492 | { | 592 | { |
196 | 493 | handshake_t * handshake = (handshake_t *)user_data; | 593 | handshake_t * handshake = (handshake_t *)user_data; |
197 | 494 | g_main_loop_quit(handshake->mainloop); | ||
198 | 495 | handshake->timeout = 0; | 594 | handshake->timeout = 0; |
199 | 595 | if (!handshake->received) { | ||
200 | 596 | handshake_stop(handshake, FALSE); | ||
201 | 597 | } | ||
202 | 496 | return G_SOURCE_REMOVE; | 598 | return G_SOURCE_REMOVE; |
203 | 497 | } | 599 | } |
204 | 498 | 600 | ||
205 | 499 | handshake_t * | 601 | handshake_t * |
207 | 500 | starting_handshake_start (const gchar * app_id) | 602 | starting_handshake_start (const gchar * app_id, HandshakeCallback callback, gpointer user_data, GDestroyNotify user_data_free) |
208 | 501 | { | 603 | { |
209 | 502 | GError * error = NULL; | 604 | GError * error = NULL; |
210 | 503 | handshake_t * handshake = g_new0(handshake_t, 1); | 605 | handshake_t * handshake = g_new0(handshake_t, 1); |
211 | 504 | 606 | ||
213 | 505 | handshake->mainloop = g_main_loop_new(NULL, FALSE); | 607 | handshake->callback = callback; |
214 | 608 | handshake->user_data = user_data; | ||
215 | 609 | handshake->user_data_free = user_data_free; | ||
216 | 610 | |||
217 | 506 | handshake->con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); | 611 | handshake->con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); |
218 | 507 | |||
219 | 508 | if (error != NULL) { | 612 | if (error != NULL) { |
220 | 509 | g_critical("Unable to connect to session bus: %s", error->message); | 613 | g_critical("Unable to connect to session bus: %s", error->message); |
221 | 510 | g_error_free(error); | 614 | g_error_free(error); |
223 | 511 | g_free(handshake); | 615 | handshake_free(handshake); |
224 | 512 | return NULL; | 616 | return NULL; |
225 | 513 | } | 617 | } |
226 | 514 | 618 | ||
227 | @@ -520,7 +624,17 @@ | |||
228 | 520 | "/", /* path */ | 624 | "/", /* path */ |
229 | 521 | app_id, /* arg0 */ | 625 | app_id, /* arg0 */ |
230 | 522 | G_DBUS_SIGNAL_FLAGS_NONE, | 626 | G_DBUS_SIGNAL_FLAGS_NONE, |
232 | 523 | unity_signal_cb, handshake->mainloop, | 627 | unity_signal_cb, handshake, |
233 | 628 | NULL); /* user data destroy */ | ||
234 | 629 | |||
235 | 630 | handshake->approved_subscribe = g_dbus_connection_signal_subscribe(handshake->con, | ||
236 | 631 | NULL, /* sender */ | ||
237 | 632 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
238 | 633 | "UnityStartingApproved", /* signal */ | ||
239 | 634 | "/", /* path */ | ||
240 | 635 | app_id, /* arg0 */ | ||
241 | 636 | G_DBUS_SIGNAL_FLAGS_NONE, | ||
242 | 637 | unity_approved_cb, handshake, | ||
243 | 524 | NULL); /* user data destroy */ | 638 | NULL); /* user data destroy */ |
244 | 525 | 639 | ||
245 | 526 | /* Send unfreeze to to Unity */ | 640 | /* Send unfreeze to to Unity */ |
246 | @@ -532,8 +646,12 @@ | |||
247 | 532 | g_variant_new("(s)", app_id), | 646 | g_variant_new("(s)", app_id), |
248 | 533 | &error); | 647 | &error); |
249 | 534 | 648 | ||
251 | 535 | /* Really, Unity? */ | 649 | /* In case Unity is either not running or taking way too long, we early exit after 5s */ |
252 | 650 | #ifdef UAL_TEST_MODE | ||
253 | 536 | handshake->timeout = g_timeout_add_seconds(1, unity_too_slow_cb, handshake); | 651 | handshake->timeout = g_timeout_add_seconds(1, unity_too_slow_cb, handshake); |
254 | 652 | #else | ||
255 | 653 | handshake->timeout = g_timeout_add_seconds(5, unity_too_slow_cb, handshake); | ||
256 | 654 | #endif | ||
257 | 537 | 655 | ||
258 | 538 | return handshake; | 656 | return handshake; |
259 | 539 | } | 657 | } |
260 | @@ -544,15 +662,8 @@ | |||
261 | 544 | if (handshake == NULL) | 662 | if (handshake == NULL) |
262 | 545 | return; | 663 | return; |
263 | 546 | 664 | ||
264 | 665 | handshake->mainloop = g_main_loop_new(NULL, FALSE); | ||
265 | 547 | g_main_loop_run(handshake->mainloop); | 666 | g_main_loop_run(handshake->mainloop); |
266 | 548 | |||
267 | 549 | if (handshake->timeout != 0) | ||
268 | 550 | g_source_remove(handshake->timeout); | ||
269 | 551 | g_main_loop_unref(handshake->mainloop); | ||
270 | 552 | g_dbus_connection_signal_unsubscribe(handshake->con, handshake->signal_subscribe); | ||
271 | 553 | g_object_unref(handshake->con); | ||
272 | 554 | |||
273 | 555 | g_free(handshake); | ||
274 | 556 | } | 667 | } |
275 | 557 | 668 | ||
276 | 558 | EnvHandle * | 669 | EnvHandle * |
277 | 559 | 670 | ||
278 | === modified file 'helpers.h' | |||
279 | --- helpers.h 2015-07-15 02:32:54 +0000 | |||
280 | +++ helpers.h 2015-12-02 18:30:50 +0000 | |||
281 | @@ -21,6 +21,8 @@ | |||
282 | 21 | 21 | ||
283 | 22 | typedef struct _EnvHandle EnvHandle; | 22 | typedef struct _EnvHandle EnvHandle; |
284 | 23 | 23 | ||
285 | 24 | typedef void (*HandshakeCallback) (gpointer user_data); | ||
286 | 25 | |||
287 | 24 | gboolean app_id_to_triplet (const gchar * app_id, | 26 | gboolean app_id_to_triplet (const gchar * app_id, |
288 | 25 | gchar ** package, | 27 | gchar ** package, |
289 | 26 | gchar ** application, | 28 | gchar ** application, |
290 | @@ -45,8 +47,11 @@ | |||
291 | 45 | void env_handle_finish (EnvHandle * handle); | 47 | void env_handle_finish (EnvHandle * handle); |
292 | 46 | 48 | ||
293 | 47 | typedef struct _handshake_t handshake_t; | 49 | typedef struct _handshake_t handshake_t; |
296 | 48 | handshake_t * starting_handshake_start (const gchar * app_id); | 50 | handshake_t * starting_handshake_start (const gchar * app_id, |
297 | 49 | void starting_handshake_wait (handshake_t * handshake); | 51 | HandshakeCallback callback, |
298 | 52 | gpointer user_data, | ||
299 | 53 | GDestroyNotify user_data_free); | ||
300 | 54 | void starting_handshake_wait (handshake_t * handshake); | ||
301 | 50 | 55 | ||
302 | 51 | GDBusConnection * cgroup_manager_connection (void); | 56 | GDBusConnection * cgroup_manager_connection (void); |
303 | 52 | void cgroup_manager_unref (GDBusConnection * cgroup_manager); | 57 | void cgroup_manager_unref (GDBusConnection * cgroup_manager); |
304 | 53 | 58 | ||
305 | === modified file 'libubuntu-app-launch/click-exec.c' | |||
306 | --- libubuntu-app-launch/click-exec.c 2014-09-17 14:11:59 +0000 | |||
307 | +++ libubuntu-app-launch/click-exec.c 2015-12-02 18:30:50 +0000 | |||
308 | @@ -51,13 +51,6 @@ | |||
309 | 51 | 51 | ||
310 | 52 | GError * error = NULL; | 52 | GError * error = NULL; |
311 | 53 | 53 | ||
312 | 54 | handshake_t * handshake = starting_handshake_start(app_id); | ||
313 | 55 | if (handshake == NULL) { | ||
314 | 56 | g_warning("Unable to setup starting handshake"); | ||
315 | 57 | } | ||
316 | 58 | |||
317 | 59 | ual_tracepoint(click_starting_sent, app_id); | ||
318 | 60 | |||
319 | 61 | gchar * package = NULL; | 54 | gchar * package = NULL; |
320 | 62 | /* 'Parse' the App ID */ | 55 | /* 'Parse' the App ID */ |
321 | 63 | if (!app_id_to_triplet(app_id, &package, NULL, NULL)) { | 56 | if (!app_id_to_triplet(app_id, &package, NULL, NULL)) { |
322 | @@ -160,11 +153,5 @@ | |||
323 | 160 | g_key_file_unref(keyfile); | 153 | g_key_file_unref(keyfile); |
324 | 161 | g_free(desktopfile); | 154 | g_free(desktopfile); |
325 | 162 | 155 | ||
326 | 163 | ual_tracepoint(handshake_wait, app_id); | ||
327 | 164 | |||
328 | 165 | starting_handshake_wait(handshake); | ||
329 | 166 | |||
330 | 167 | ual_tracepoint(handshake_complete, app_id); | ||
331 | 168 | |||
332 | 169 | return TRUE; | 156 | return TRUE; |
333 | 170 | } | 157 | } |
334 | 171 | 158 | ||
335 | === modified file 'libubuntu-app-launch/desktop-exec.c' | |||
336 | --- libubuntu-app-launch/desktop-exec.c 2015-08-12 02:52:05 +0000 | |||
337 | +++ libubuntu-app-launch/desktop-exec.c 2015-12-02 18:30:50 +0000 | |||
338 | @@ -126,13 +126,6 @@ | |||
339 | 126 | 126 | ||
340 | 127 | ual_tracepoint(desktop_start, app_id); | 127 | ual_tracepoint(desktop_start, app_id); |
341 | 128 | 128 | ||
342 | 129 | handshake_t * handshake = starting_handshake_start(app_id); | ||
343 | 130 | if (handshake == NULL) { | ||
344 | 131 | g_warning("Unable to setup starting handshake"); | ||
345 | 132 | } | ||
346 | 133 | |||
347 | 134 | ual_tracepoint(desktop_starting_sent, app_id); | ||
348 | 135 | |||
349 | 136 | gchar * desktopfilename = NULL; | 129 | gchar * desktopfilename = NULL; |
350 | 137 | GKeyFile * keyfile = NULL; | 130 | GKeyFile * keyfile = NULL; |
351 | 138 | gchar * libertinecontainer = NULL; | 131 | gchar * libertinecontainer = NULL; |
352 | @@ -208,11 +201,5 @@ | |||
353 | 208 | 201 | ||
354 | 209 | g_key_file_free(keyfile); | 202 | g_key_file_free(keyfile); |
355 | 210 | 203 | ||
356 | 211 | ual_tracepoint(handshake_wait, app_id); | ||
357 | 212 | |||
358 | 213 | starting_handshake_wait(handshake); | ||
359 | 214 | |||
360 | 215 | ual_tracepoint(handshake_complete, app_id); | ||
361 | 216 | |||
362 | 217 | return TRUE; | 204 | return TRUE; |
363 | 218 | } | 205 | } |
364 | 219 | 206 | ||
365 | === modified file 'libubuntu-app-launch/ubuntu-app-launch-trace.tp' | |||
366 | --- libubuntu-app-launch/ubuntu-app-launch-trace.tp 2015-07-15 01:42:22 +0000 | |||
367 | +++ libubuntu-app-launch/ubuntu-app-launch-trace.tp 2015-12-02 18:30:50 +0000 | |||
368 | @@ -44,6 +44,24 @@ | |||
369 | 44 | ctf_string(appid, appid) | 44 | ctf_string(appid, appid) |
370 | 45 | ) | 45 | ) |
371 | 46 | ) | 46 | ) |
372 | 47 | TRACEPOINT_EVENT(ubuntu_app_launch, libual_handshake_start, | ||
373 | 48 | TP_ARGS(const char *, appid), | ||
374 | 49 | TP_FIELDS( | ||
375 | 50 | ctf_string(appid, appid) | ||
376 | 51 | ) | ||
377 | 52 | ) | ||
378 | 53 | TRACEPOINT_EVENT(ubuntu_app_launch, libual_handshake_wait, | ||
379 | 54 | TP_ARGS(const char *, appid), | ||
380 | 55 | TP_FIELDS( | ||
381 | 56 | ctf_string(appid, appid) | ||
382 | 57 | ) | ||
383 | 58 | ) | ||
384 | 59 | TRACEPOINT_EVENT(ubuntu_app_launch, libual_handshake_complete, | ||
385 | 60 | TP_ARGS(const char *, appid), | ||
386 | 61 | TP_FIELDS( | ||
387 | 62 | ctf_string(appid, appid) | ||
388 | 63 | ) | ||
389 | 64 | ) | ||
390 | 47 | 65 | ||
391 | 48 | /******************************* | 66 | /******************************* |
392 | 49 | LibUAL observers | 67 | LibUAL observers |
393 | @@ -211,12 +229,6 @@ | |||
394 | 211 | ctf_string(appid, appid) | 229 | ctf_string(appid, appid) |
395 | 212 | ) | 230 | ) |
396 | 213 | ) | 231 | ) |
397 | 214 | TRACEPOINT_EVENT(ubuntu_app_launch, click_starting_sent, | ||
398 | 215 | TP_ARGS(const char *, appid), | ||
399 | 216 | TP_FIELDS( | ||
400 | 217 | ctf_string(appid, appid) | ||
401 | 218 | ) | ||
402 | 219 | ) | ||
403 | 220 | TRACEPOINT_EVENT(ubuntu_app_launch, click_found_pkgdir, | 232 | TRACEPOINT_EVENT(ubuntu_app_launch, click_found_pkgdir, |
404 | 221 | TP_ARGS(const char *, appid), | 233 | TP_ARGS(const char *, appid), |
405 | 222 | TP_FIELDS( | 234 | TP_FIELDS( |
406 | @@ -247,18 +259,6 @@ | |||
407 | 247 | ctf_string(appid, appid) | 259 | ctf_string(appid, appid) |
408 | 248 | ) | 260 | ) |
409 | 249 | ) | 261 | ) |
410 | 250 | TRACEPOINT_EVENT(ubuntu_app_launch, handshake_wait, | ||
411 | 251 | TP_ARGS(const char *, appid), | ||
412 | 252 | TP_FIELDS( | ||
413 | 253 | ctf_string(appid, appid) | ||
414 | 254 | ) | ||
415 | 255 | ) | ||
416 | 256 | TRACEPOINT_EVENT(ubuntu_app_launch, handshake_complete, | ||
417 | 257 | TP_ARGS(const char *, appid), | ||
418 | 258 | TP_FIELDS( | ||
419 | 259 | ctf_string(appid, appid) | ||
420 | 260 | ) | ||
421 | 261 | ) | ||
422 | 262 | 262 | ||
423 | 263 | /******************************* | 263 | /******************************* |
424 | 264 | Desktop Exec | 264 | Desktop Exec |
425 | @@ -270,12 +270,6 @@ | |||
426 | 270 | ctf_string(appid, appid) | 270 | ctf_string(appid, appid) |
427 | 271 | ) | 271 | ) |
428 | 272 | ) | 272 | ) |
429 | 273 | TRACEPOINT_EVENT(ubuntu_app_launch, desktop_starting_sent, | ||
430 | 274 | TP_ARGS(const char *, appid), | ||
431 | 275 | TP_FIELDS( | ||
432 | 276 | ctf_string(appid, appid) | ||
433 | 277 | ) | ||
434 | 278 | ) | ||
435 | 279 | TRACEPOINT_EVENT(ubuntu_app_launch, desktop_found, | 273 | TRACEPOINT_EVENT(ubuntu_app_launch, desktop_found, |
436 | 280 | TP_ARGS(const char *, appid), | 274 | TP_ARGS(const char *, appid), |
437 | 281 | TP_FIELDS( | 275 | TP_FIELDS( |
438 | 282 | 276 | ||
439 | === modified file 'libubuntu-app-launch/ubuntu-app-launch.c' | |||
440 | --- libubuntu-app-launch/ubuntu-app-launch.c 2015-12-02 18:30:50 +0000 | |||
441 | +++ libubuntu-app-launch/ubuntu-app-launch.c 2015-12-02 18:30:50 +0000 | |||
442 | @@ -71,6 +71,14 @@ | |||
443 | 71 | } app_start_t; | 71 | } app_start_t; |
444 | 72 | 72 | ||
445 | 73 | static void | 73 | static void |
446 | 74 | app_start_data_free (app_start_t *data) | ||
447 | 75 | { | ||
448 | 76 | g_free(data->appid); | ||
449 | 77 | g_free(data->uris); | ||
450 | 78 | g_free(data); | ||
451 | 79 | } | ||
452 | 80 | |||
453 | 81 | static void | ||
454 | 74 | application_start_cb (GObject * obj, GAsyncResult * res, gpointer user_data) | 82 | application_start_cb (GObject * obj, GAsyncResult * res, gpointer user_data) |
455 | 75 | { | 83 | { |
456 | 76 | app_start_t * data = (app_start_t *)user_data; | 84 | app_start_t * data = (app_start_t *)user_data; |
457 | @@ -101,9 +109,7 @@ | |||
458 | 101 | g_error_free(error); | 109 | g_error_free(error); |
459 | 102 | } | 110 | } |
460 | 103 | 111 | ||
464 | 104 | g_free(data->appid); | 112 | app_start_data_free(data); |
462 | 105 | g_free(data->uris); | ||
463 | 106 | g_free(data); | ||
465 | 107 | } | 113 | } |
466 | 108 | 114 | ||
467 | 109 | /* Get the path of the job from Upstart, if we've got it already, we'll just | 115 | /* Get the path of the job from Upstart, if we've got it already, we'll just |
468 | @@ -220,8 +226,55 @@ | |||
469 | 220 | } | 226 | } |
470 | 221 | } | 227 | } |
471 | 222 | 228 | ||
472 | 229 | typedef struct { | ||
473 | 230 | gchar * jobpath; | ||
474 | 231 | GVariant * params; | ||
475 | 232 | app_start_t * app_start_data; | ||
476 | 233 | } handshake_data_t; | ||
477 | 234 | |||
478 | 235 | static void | ||
479 | 236 | handshake_data_free (handshake_data_t *data) | ||
480 | 237 | { | ||
481 | 238 | g_free(data->jobpath); | ||
482 | 239 | g_variant_unref(data->params); | ||
483 | 240 | app_start_data_free(data->app_start_data); | ||
484 | 241 | g_free(data); | ||
485 | 242 | } | ||
486 | 243 | |||
487 | 244 | static void | ||
488 | 245 | handshake_cb (gpointer user_data) | ||
489 | 246 | { | ||
490 | 247 | handshake_data_t *data = (handshake_data_t *) user_data; | ||
491 | 248 | |||
492 | 249 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
493 | 250 | g_return_if_fail(con != NULL); | ||
494 | 251 | |||
495 | 252 | /* Copy app start data, so it can be freed by application_start_cb */ | ||
496 | 253 | app_start_t * app_data_copy = g_new0(app_start_t, 1); | ||
497 | 254 | app_data_copy->appid = g_strdup(data->app_start_data->appid); | ||
498 | 255 | app_data_copy->uris = g_strdup(data->app_start_data->uris); | ||
499 | 256 | |||
500 | 257 | /* Call the job start function */ | ||
501 | 258 | g_dbus_connection_call(con, | ||
502 | 259 | DBUS_SERVICE_UPSTART, | ||
503 | 260 | data->jobpath, | ||
504 | 261 | DBUS_INTERFACE_UPSTART_JOB, | ||
505 | 262 | "Start", | ||
506 | 263 | data->params, | ||
507 | 264 | NULL, | ||
508 | 265 | G_DBUS_CALL_FLAGS_NONE, | ||
509 | 266 | -1, | ||
510 | 267 | NULL, /* cancelable */ | ||
511 | 268 | application_start_cb, | ||
512 | 269 | app_data_copy); | ||
513 | 270 | |||
514 | 271 | ual_tracepoint(libual_start_message_sent, data->app_start_data->appid); | ||
515 | 272 | |||
516 | 273 | g_object_unref(con); | ||
517 | 274 | } | ||
518 | 275 | |||
519 | 223 | static gboolean | 276 | static gboolean |
521 | 224 | start_application_core (const gchar * appid, const gchar * const * uris, gboolean test) | 277 | start_application_core (const gchar * appid, const gchar * const * uris, gboolean async, gboolean test) |
522 | 225 | { | 278 | { |
523 | 226 | ual_tracepoint(libual_start, appid); | 279 | ual_tracepoint(libual_start, appid); |
524 | 227 | 280 | ||
525 | @@ -300,24 +353,29 @@ | |||
526 | 300 | if (setup_complete) { | 353 | if (setup_complete) { |
527 | 301 | g_variant_builder_close(&builder); | 354 | g_variant_builder_close(&builder); |
528 | 302 | g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE)); | 355 | g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE)); |
545 | 303 | 356 | ||
546 | 304 | /* Call the job start function */ | 357 | ual_tracepoint(libual_handshake_start, appid); |
547 | 305 | g_dbus_connection_call(con, | 358 | |
548 | 306 | DBUS_SERVICE_UPSTART, | 359 | handshake_data_t *handshake_data = g_new0(handshake_data_t, 1); |
549 | 307 | jobpath, | 360 | handshake_data->jobpath = g_strdup(jobpath); |
550 | 308 | DBUS_INTERFACE_UPSTART_JOB, | 361 | handshake_data->params = g_variant_ref_sink(g_variant_builder_end(&builder)); |
551 | 309 | "Start", | 362 | handshake_data->app_start_data = app_start_data; |
552 | 310 | g_variant_builder_end(&builder), | 363 | |
553 | 311 | NULL, | 364 | handshake_t * handshake = starting_handshake_start(appid, handshake_cb, handshake_data, (GDestroyNotify)handshake_data_free); |
554 | 312 | G_DBUS_CALL_FLAGS_NONE, | 365 | |
555 | 313 | -1, | 366 | if (handshake == NULL) { |
556 | 314 | NULL, /* cancelable */ | 367 | g_warning("Unable to setup starting handshake"); |
557 | 315 | application_start_cb, | 368 | setup_complete = FALSE; |
558 | 316 | app_start_data); | 369 | } else if (!async) { |
559 | 317 | 370 | ual_tracepoint(libual_handshake_wait, appid); | |
560 | 318 | ual_tracepoint(libual_start_message_sent, appid); | 371 | |
561 | 372 | starting_handshake_wait(handshake); | ||
562 | 373 | } | ||
563 | 374 | |||
564 | 375 | ual_tracepoint(libual_handshake_complete, appid); | ||
565 | 319 | } else { | 376 | } else { |
566 | 320 | g_variant_builder_clear(&builder); | 377 | g_variant_builder_clear(&builder); |
567 | 378 | app_start_data_free(app_start_data); | ||
568 | 321 | } | 379 | } |
569 | 322 | 380 | ||
570 | 323 | g_object_unref(con); | 381 | g_object_unref(con); |
571 | @@ -328,13 +386,25 @@ | |||
572 | 328 | gboolean | 386 | gboolean |
573 | 329 | ubuntu_app_launch_start_application (const gchar * appid, const gchar * const * uris) | 387 | ubuntu_app_launch_start_application (const gchar * appid, const gchar * const * uris) |
574 | 330 | { | 388 | { |
576 | 331 | return start_application_core(appid, uris, FALSE); | 389 | return start_application_core(appid, uris, FALSE, FALSE); |
577 | 332 | } | 390 | } |
578 | 333 | 391 | ||
579 | 334 | gboolean | 392 | gboolean |
580 | 335 | ubuntu_app_launch_start_application_test (const gchar * appid, const gchar * const * uris) | 393 | ubuntu_app_launch_start_application_test (const gchar * appid, const gchar * const * uris) |
581 | 336 | { | 394 | { |
583 | 337 | return start_application_core(appid, uris, TRUE); | 395 | return start_application_core(appid, uris, FALSE, TRUE); |
584 | 396 | } | ||
585 | 397 | |||
586 | 398 | void | ||
587 | 399 | ubuntu_app_launch_start_application_async (const gchar * appid, const gchar * const * uris) | ||
588 | 400 | { | ||
589 | 401 | start_application_core(appid, uris, TRUE, FALSE); | ||
590 | 402 | } | ||
591 | 403 | |||
592 | 404 | void | ||
593 | 405 | ubuntu_app_launch_start_application_async_test (const gchar * appid, const gchar * const * uris) | ||
594 | 406 | { | ||
595 | 407 | start_application_core(appid, uris, TRUE, TRUE); | ||
596 | 338 | } | 408 | } |
597 | 339 | 409 | ||
598 | 340 | static void | 410 | static void |
599 | @@ -953,7 +1023,7 @@ | |||
600 | 953 | observer_t * observer = (observer_t *)user_data; | 1023 | observer_t * observer = (observer_t *)user_data; |
601 | 954 | const gchar * appid = NULL; | 1024 | const gchar * appid = NULL; |
602 | 955 | 1025 | ||
604 | 956 | if (observer->func != NULL) { | 1026 | if (observer->func != NULL && g_variant_check_format_string(params, "(s)", FALSE)) { |
605 | 957 | g_variant_get(params, "(&s)", &appid); | 1027 | g_variant_get(params, "(&s)", &appid); |
606 | 958 | observer->func(appid, observer->user_data); | 1028 | observer->func(appid, observer->user_data); |
607 | 959 | } | 1029 | } |
608 | @@ -1007,14 +1077,13 @@ | |||
609 | 1007 | return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb); | 1077 | return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb); |
610 | 1008 | } | 1078 | } |
611 | 1009 | 1079 | ||
613 | 1010 | /* Handle the starting signal when it occurs, call the observer, then send a signal back when we're done */ | 1080 | /* Handle the starting signal when it occurs and call the observer, |
614 | 1081 | which will send a signal back when it's done via finish_app_starting */ | ||
615 | 1011 | static void | 1082 | static void |
616 | 1012 | starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) | 1083 | starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
617 | 1013 | { | 1084 | { |
618 | 1014 | ual_tracepoint(observer_start, "starting"); | 1085 | ual_tracepoint(observer_start, "starting"); |
619 | 1015 | 1086 | ||
620 | 1016 | generic_signal_cb(conn, sender, object, interface, signal, params, user_data); | ||
621 | 1017 | |||
622 | 1018 | GError * error = NULL; | 1087 | GError * error = NULL; |
623 | 1019 | g_dbus_connection_emit_signal(conn, | 1088 | g_dbus_connection_emit_signal(conn, |
624 | 1020 | sender, /* destination */ | 1089 | sender, /* destination */ |
625 | @@ -1027,6 +1096,8 @@ | |||
626 | 1027 | if (error != NULL) { | 1096 | if (error != NULL) { |
627 | 1028 | g_warning("Unable to emit response signal: %s", error->message); | 1097 | g_warning("Unable to emit response signal: %s", error->message); |
628 | 1029 | g_error_free(error); | 1098 | g_error_free(error); |
629 | 1099 | } else { | ||
630 | 1100 | generic_signal_cb(conn, sender, object, interface, signal, params, user_data); | ||
631 | 1030 | } | 1101 | } |
632 | 1031 | 1102 | ||
633 | 1032 | ual_tracepoint(observer_finish, "starting"); | 1103 | ual_tracepoint(observer_finish, "starting"); |
634 | @@ -1038,6 +1109,33 @@ | |||
635 | 1038 | return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb); | 1109 | return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb); |
636 | 1039 | } | 1110 | } |
637 | 1040 | 1111 | ||
638 | 1112 | gboolean | ||
639 | 1113 | ubuntu_app_launch_observer_finish_app_starting (const gchar *appid, gboolean approved) | ||
640 | 1114 | { | ||
641 | 1115 | GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
642 | 1116 | if (conn == NULL) { | ||
643 | 1117 | return FALSE; | ||
644 | 1118 | } | ||
645 | 1119 | |||
646 | 1120 | GError * error = NULL; | ||
647 | 1121 | g_dbus_connection_emit_signal(conn, | ||
648 | 1122 | NULL, /* destination */ | ||
649 | 1123 | "/", /* path */ | ||
650 | 1124 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
651 | 1125 | "UnityStartingApproved", /* signal */ | ||
652 | 1126 | g_variant_new("(sb)", appid, approved), /* params */ | ||
653 | 1127 | &error); | ||
654 | 1128 | g_object_unref(conn); | ||
655 | 1129 | |||
656 | 1130 | if (error != NULL) { | ||
657 | 1131 | g_warning("Unable to emit response signal: %s", error->message); | ||
658 | 1132 | g_error_free(error); | ||
659 | 1133 | return FALSE; | ||
660 | 1134 | } | ||
661 | 1135 | |||
662 | 1136 | return TRUE; | ||
663 | 1137 | } | ||
664 | 1138 | |||
665 | 1041 | /* Handle the failed signal when it occurs, call the observer */ | 1139 | /* Handle the failed signal when it occurs, call the observer */ |
666 | 1042 | static void | 1140 | static void |
667 | 1043 | failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) | 1141 | failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
668 | 1044 | 1142 | ||
669 | === modified file 'libubuntu-app-launch/ubuntu-app-launch.h' | |||
670 | --- libubuntu-app-launch/ubuntu-app-launch.h 2015-08-10 14:19:05 +0000 | |||
671 | +++ libubuntu-app-launch/ubuntu-app-launch.h 2015-12-02 18:30:50 +0000 | |||
672 | @@ -97,6 +97,31 @@ | |||
673 | 97 | const gchar * const * uris); | 97 | const gchar * const * uris); |
674 | 98 | 98 | ||
675 | 99 | /** | 99 | /** |
676 | 100 | * ubuntu_app_launch_start_application_async: | ||
677 | 101 | * @appid: ID of the application to launch | ||
678 | 102 | * @uris: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): A NULL terminated list of URIs to send to the application | ||
679 | 103 | * | ||
680 | 104 | * Asks upstart to launch an application, but does not block. To discover | ||
681 | 105 | * whether it successfully started or not, register an observer. For example, | ||
682 | 106 | * call ubuntu_app_launch_observer_add_app_started. | ||
683 | 107 | */ | ||
684 | 108 | void ubuntu_app_launch_start_application_async (const gchar * appid, | ||
685 | 109 | const gchar * const * uris); | ||
686 | 110 | |||
687 | 111 | /** | ||
688 | 112 | * ubuntu_app_launch_start_application_async_test: | ||
689 | 113 | * @appid: ID of the application to launch | ||
690 | 114 | * @uris: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): A NULL terminated list of URIs to send to the application | ||
691 | 115 | * | ||
692 | 116 | * Asks upstart to launch an application with environment variables set | ||
693 | 117 | * to enable testing, but does not block. To discover whether it successfully | ||
694 | 118 | * started or not, register an observer. For example, call | ||
695 | 119 | * ubuntu_app_launch_observer_add_app_started. Should only be used in testing. | ||
696 | 120 | */ | ||
697 | 121 | void ubuntu_app_launch_start_application_async_test (const gchar * appid, | ||
698 | 122 | const gchar * const * uris); | ||
699 | 123 | |||
700 | 124 | /** | ||
701 | 100 | * ubuntu_app_launch_stop_application: | 125 | * ubuntu_app_launch_stop_application: |
702 | 101 | * @appid: ID of the application to launch | 126 | * @appid: ID of the application to launch |
703 | 102 | * | 127 | * |
704 | @@ -163,14 +188,28 @@ | |||
705 | 163 | * @user_data: (closure) (allow-none): Data to pass to the observer | 188 | * @user_data: (closure) (allow-none): Data to pass to the observer |
706 | 164 | * | 189 | * |
707 | 165 | * Sets up a callback to get called each time an application | 190 | * Sets up a callback to get called each time an application |
710 | 166 | * is about to start. The application will not start until the | 191 | * is about to start. The application will not start until |
711 | 167 | * function returns. | 192 | * ubuntu_app_launch_observer_finish_app_starting is called after |
712 | 193 | * the callback is called. | ||
713 | 168 | * | 194 | * |
714 | 169 | * Return value: Whether adding the observer was successful. | 195 | * Return value: Whether adding the observer was successful. |
715 | 170 | */ | 196 | */ |
716 | 171 | gboolean ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer, | 197 | gboolean ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer, |
717 | 172 | gpointer user_data); | 198 | gpointer user_data); |
718 | 173 | /** | 199 | /** |
719 | 200 | * ubuntu_app_launch_observer_finish_app_starting: | ||
720 | 201 | * @appid: ID of the application being started | ||
721 | 202 | * @approved: Whether the app can continue starting | ||
722 | 203 | * | ||
723 | 204 | * Finishes handling an app that is starting up via the callback | ||
724 | 205 | * registered with ubuntu_app_launch_observer_add_app_starting. | ||
725 | 206 | * If the app is allowed to continue starting, pass TRUE. | ||
726 | 207 | * | ||
727 | 208 | * Return value: Whether finish app startup was successful. | ||
728 | 209 | */ | ||
729 | 210 | gboolean ubuntu_app_launch_observer_finish_app_starting (const gchar *appid, | ||
730 | 211 | gboolean approved); | ||
731 | 212 | /** | ||
732 | 174 | * ubuntu_app_launch_observer_add_app_started: | 213 | * ubuntu_app_launch_observer_add_app_started: |
733 | 175 | * @observer: (scope notified): Callback when an application started | 214 | * @observer: (scope notified): Callback when an application started |
734 | 176 | * @user_data: (closure) (allow-none): Data to pass to the observer | 215 | * @user_data: (closure) (allow-none): Data to pass to the observer |
735 | 177 | 216 | ||
736 | === modified file 'tests/CMakeLists.txt' | |||
737 | --- tests/CMakeLists.txt 2015-06-26 17:26:50 +0000 | |||
738 | +++ tests/CMakeLists.txt 2015-12-02 18:30:50 +0000 | |||
739 | @@ -24,7 +24,7 @@ | |||
740 | 24 | # Helper test | 24 | # Helper test |
741 | 25 | 25 | ||
742 | 26 | add_executable (helper-handshake-test helper-handshake-test.cc) | 26 | add_executable (helper-handshake-test helper-handshake-test.cc) |
744 | 27 | target_link_libraries (helper-handshake-test helpers gtest ${GTEST_LIBS}) | 27 | target_link_libraries (helper-handshake-test helpers-test gtest ${GTEST_LIBS}) |
745 | 28 | 28 | ||
746 | 29 | add_test (helper-handshake-test helper-handshake-test) | 29 | add_test (helper-handshake-test helper-handshake-test) |
747 | 30 | 30 | ||
748 | 31 | 31 | ||
749 | === modified file 'tests/exec-util-test.cc' | |||
750 | --- tests/exec-util-test.cc 2015-08-11 19:11:15 +0000 | |||
751 | +++ tests/exec-util-test.cc 2015-12-02 18:30:50 +0000 | |||
752 | @@ -34,7 +34,8 @@ | |||
753 | 34 | 34 | ||
754 | 35 | protected: | 35 | protected: |
755 | 36 | static void starting_cb (const gchar * appid, gpointer user_data) { | 36 | static void starting_cb (const gchar * appid, gpointer user_data) { |
757 | 37 | g_debug("I'm too sexy to callback"); | 37 | g_debug("Starting app %s", appid); |
758 | 38 | ASSERT_TRUE(ubuntu_app_launch_observer_finish_app_starting(appid, TRUE)); | ||
759 | 38 | } | 39 | } |
760 | 39 | 40 | ||
761 | 40 | virtual void SetUp() { | 41 | virtual void SetUp() { |
762 | 41 | 42 | ||
763 | === modified file 'tests/helper-handshake-test.cc' | |||
764 | --- tests/helper-handshake-test.cc 2014-04-30 16:18:29 +0000 | |||
765 | +++ tests/helper-handshake-test.cc 2015-12-02 18:30:50 +0000 | |||
766 | @@ -72,12 +72,20 @@ | |||
767 | 72 | return static_cast<HelperHandshakeTest *>(user_data)->FilterFunc(conn, message, incomming); | 72 | return static_cast<HelperHandshakeTest *>(user_data)->FilterFunc(conn, message, incomming); |
768 | 73 | } | 73 | } |
769 | 74 | 74 | ||
770 | 75 | static void | ||
771 | 76 | handshake_cb (gpointer user_data) | ||
772 | 77 | { | ||
773 | 78 | bool * reached = static_cast<bool *>(user_data); | ||
774 | 79 | *reached = true; | ||
775 | 80 | } | ||
776 | 81 | |||
777 | 75 | TEST_F(HelperHandshakeTest, BaseHandshake) | 82 | TEST_F(HelperHandshakeTest, BaseHandshake) |
778 | 76 | { | 83 | { |
779 | 84 | bool handshake_succeeded = false; | ||
780 | 77 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | 85 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
781 | 78 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | 86 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); |
782 | 79 | 87 | ||
784 | 80 | handshake_t * handshake = starting_handshake_start("fooapp"); | 88 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); |
785 | 81 | 89 | ||
786 | 82 | g_main_loop_run(mainloop); | 90 | g_main_loop_run(mainloop); |
787 | 83 | 91 | ||
788 | @@ -91,8 +99,89 @@ | |||
789 | 91 | g_variant_new("(s)", "fooapp"), /* params, the same */ | 99 | g_variant_new("(s)", "fooapp"), /* params, the same */ |
790 | 92 | NULL); | 100 | NULL); |
791 | 93 | 101 | ||
794 | 94 | starting_handshake_wait(handshake); | 102 | g_dbus_connection_emit_signal(con, |
795 | 95 | 103 | g_dbus_connection_get_unique_name(con), /* destination */ | |
796 | 104 | "/", /* path */ | ||
797 | 105 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
798 | 106 | "UnityStartingApproved", /* signal */ | ||
799 | 107 | g_variant_new("(sb)", "fooapp", TRUE), /* params */ | ||
800 | 108 | NULL); | ||
801 | 109 | |||
802 | 110 | starting_handshake_wait(handshake); | ||
803 | 111 | |||
804 | 112 | ASSERT_TRUE(handshake_succeeded); | ||
805 | 113 | g_object_unref(con); | ||
806 | 114 | |||
807 | 115 | return; | ||
808 | 116 | } | ||
809 | 117 | |||
810 | 118 | TEST_F(HelperHandshakeTest, UnapprovedHandshake) | ||
811 | 119 | { | ||
812 | 120 | bool handshake_succeeded = false; | ||
813 | 121 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
814 | 122 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | ||
815 | 123 | |||
816 | 124 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); | ||
817 | 125 | |||
818 | 126 | g_main_loop_run(mainloop); | ||
819 | 127 | |||
820 | 128 | g_dbus_connection_remove_filter(con, filter); | ||
821 | 129 | |||
822 | 130 | g_dbus_connection_emit_signal(con, | ||
823 | 131 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
824 | 132 | "/", /* path */ | ||
825 | 133 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
826 | 134 | "UnityStartingSignal", /* signal */ | ||
827 | 135 | g_variant_new("(s)", "fooapp"), /* params */ | ||
828 | 136 | NULL); | ||
829 | 137 | |||
830 | 138 | g_dbus_connection_emit_signal(con, | ||
831 | 139 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
832 | 140 | "/", /* path */ | ||
833 | 141 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
834 | 142 | "UnityStartingApproved", /* signal */ | ||
835 | 143 | g_variant_new("(sb)", "fooapp", FALSE), /* params */ | ||
836 | 144 | NULL); | ||
837 | 145 | |||
838 | 146 | starting_handshake_wait(handshake); | ||
839 | 147 | |||
840 | 148 | ASSERT_FALSE(handshake_succeeded); | ||
841 | 149 | g_object_unref(con); | ||
842 | 150 | |||
843 | 151 | return; | ||
844 | 152 | } | ||
845 | 153 | |||
846 | 154 | TEST_F(HelperHandshakeTest, InvalidHandshake) | ||
847 | 155 | { | ||
848 | 156 | bool handshake_succeeded = false; | ||
849 | 157 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
850 | 158 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | ||
851 | 159 | |||
852 | 160 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); | ||
853 | 161 | |||
854 | 162 | g_main_loop_run(mainloop); | ||
855 | 163 | |||
856 | 164 | g_dbus_connection_remove_filter(con, filter); | ||
857 | 165 | |||
858 | 166 | g_dbus_connection_emit_signal(con, | ||
859 | 167 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
860 | 168 | "/", /* path */ | ||
861 | 169 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
862 | 170 | "UnityStartingSignal", /* signal */ | ||
863 | 171 | g_variant_new("(s)", "fooapp"), /* params */ | ||
864 | 172 | NULL); | ||
865 | 173 | |||
866 | 174 | g_dbus_connection_emit_signal(con, | ||
867 | 175 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
868 | 176 | "/", /* path */ | ||
869 | 177 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
870 | 178 | "UnityStartingApproved", /* signal */ | ||
871 | 179 | g_variant_new("(ss)", "fooapp", "true"), /* bad params */ | ||
872 | 180 | NULL); | ||
873 | 181 | |||
874 | 182 | starting_handshake_wait(handshake); | ||
875 | 183 | |||
876 | 184 | ASSERT_FALSE(handshake_succeeded); | ||
877 | 96 | g_object_unref(con); | 185 | g_object_unref(con); |
878 | 97 | 186 | ||
879 | 98 | return; | 187 | return; |
880 | @@ -103,18 +192,179 @@ | |||
881 | 103 | { | 192 | { |
882 | 104 | bool * reached = static_cast<bool *>(user_data); | 193 | bool * reached = static_cast<bool *>(user_data); |
883 | 105 | *reached = true; | 194 | *reached = true; |
884 | 195 | return G_SOURCE_REMOVE; | ||
885 | 106 | } | 196 | } |
886 | 107 | 197 | ||
887 | 108 | TEST_F(HelperHandshakeTest, HandshakeTimeout) | 198 | TEST_F(HelperHandshakeTest, HandshakeTimeout) |
888 | 109 | { | 199 | { |
889 | 110 | bool timeout_reached = false; | 200 | bool timeout_reached = false; |
900 | 111 | handshake_t * handshake = starting_handshake_start("fooapp"); | 201 | bool handshake_succeeded = false; |
901 | 112 | 202 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); | |
902 | 113 | guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached); | 203 | |
903 | 114 | 204 | guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached); | |
904 | 115 | starting_handshake_wait(handshake); | 205 | |
905 | 116 | 206 | starting_handshake_wait(handshake); | |
906 | 117 | ASSERT_FALSE(timeout_reached); | 207 | |
907 | 118 | 208 | ASSERT_FALSE(timeout_reached); | |
908 | 119 | return; | 209 | ASSERT_FALSE(handshake_succeeded); |
909 | 120 | } | 210 | g_source_remove(outertimeout); |
910 | 211 | |||
911 | 212 | return; | ||
912 | 213 | } | ||
913 | 214 | |||
914 | 215 | TEST_F(HelperHandshakeTest, HandshakeTimeoutWithOnlyApprovedSignal) | ||
915 | 216 | { | ||
916 | 217 | bool timeout_reached = false; | ||
917 | 218 | bool handshake_succeeded = false; | ||
918 | 219 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
919 | 220 | |||
920 | 221 | guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached); | ||
921 | 222 | |||
922 | 223 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | ||
923 | 224 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); | ||
924 | 225 | g_main_loop_run(mainloop); | ||
925 | 226 | g_dbus_connection_remove_filter(con, filter); | ||
926 | 227 | |||
927 | 228 | g_dbus_connection_emit_signal(con, | ||
928 | 229 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
929 | 230 | "/", /* path */ | ||
930 | 231 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
931 | 232 | "UnityStartingApproved", /* signal */ | ||
932 | 233 | g_variant_new("(sb)", "fooapp", true), /* bad params */ | ||
933 | 234 | NULL); | ||
934 | 235 | |||
935 | 236 | starting_handshake_wait(handshake); | ||
936 | 237 | |||
937 | 238 | ASSERT_FALSE(timeout_reached); | ||
938 | 239 | ASSERT_FALSE(handshake_succeeded); | ||
939 | 240 | g_source_remove(outertimeout); | ||
940 | 241 | g_object_unref(con); | ||
941 | 242 | |||
942 | 243 | return; | ||
943 | 244 | } | ||
944 | 245 | |||
945 | 246 | static gboolean | ||
946 | 247 | emit_approval (gpointer user_data) | ||
947 | 248 | { | ||
948 | 249 | GDBusConnection * con = (GDBusConnection *) user_data; | ||
949 | 250 | |||
950 | 251 | g_dbus_connection_emit_signal(con, | ||
951 | 252 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
952 | 253 | "/", /* path */ | ||
953 | 254 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
954 | 255 | "UnityStartingApproved", /* signal */ | ||
955 | 256 | g_variant_new("(sb)", "fooapp", true), /* params */ | ||
956 | 257 | NULL); | ||
957 | 258 | |||
958 | 259 | return G_SOURCE_REMOVE; | ||
959 | 260 | } | ||
960 | 261 | |||
961 | 262 | TEST_F(HelperHandshakeTest, HandshakeNoTimeoutWithReceivedSignal) | ||
962 | 263 | { | ||
963 | 264 | bool timeout_reached = false; | ||
964 | 265 | bool handshake_succeeded = false; | ||
965 | 266 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
966 | 267 | |||
967 | 268 | g_timeout_add_seconds(2, two_second_reached, &timeout_reached); | ||
968 | 269 | g_timeout_add_seconds(2, emit_approval, con); | ||
969 | 270 | |||
970 | 271 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | ||
971 | 272 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); | ||
972 | 273 | g_main_loop_run(mainloop); | ||
973 | 274 | g_dbus_connection_remove_filter(con, filter); | ||
974 | 275 | |||
975 | 276 | g_dbus_connection_emit_signal(con, | ||
976 | 277 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
977 | 278 | "/", /* path */ | ||
978 | 279 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
979 | 280 | "UnityStartingSignal", /* signal */ | ||
980 | 281 | g_variant_new("(s)", "fooapp"), /* params */ | ||
981 | 282 | NULL); | ||
982 | 283 | |||
983 | 284 | starting_handshake_wait(handshake); | ||
984 | 285 | |||
985 | 286 | ASSERT_TRUE(timeout_reached); | ||
986 | 287 | ASSERT_TRUE(handshake_succeeded); | ||
987 | 288 | g_object_unref(con); | ||
988 | 289 | |||
989 | 290 | return; | ||
990 | 291 | } | ||
991 | 292 | |||
992 | 293 | TEST_F(HelperHandshakeTest, AsyncHandshake) | ||
993 | 294 | { | ||
994 | 295 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
995 | 296 | |||
996 | 297 | g_timeout_add_seconds(2, emit_approval, con); | ||
997 | 298 | |||
998 | 299 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | ||
999 | 300 | handshake_t * handshake = starting_handshake_start("fooapp", (HandshakeCallback)g_main_loop_quit, mainloop, NULL); | ||
1000 | 301 | g_main_loop_run(mainloop); | ||
1001 | 302 | g_dbus_connection_remove_filter(con, filter); | ||
1002 | 303 | |||
1003 | 304 | g_dbus_connection_emit_signal(con, | ||
1004 | 305 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
1005 | 306 | "/", /* path */ | ||
1006 | 307 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
1007 | 308 | "UnityStartingSignal", /* signal */ | ||
1008 | 309 | g_variant_new("(s)", "fooapp"), /* params, the same */ | ||
1009 | 310 | NULL); | ||
1010 | 311 | |||
1011 | 312 | g_main_loop_run(mainloop); | ||
1012 | 313 | |||
1013 | 314 | g_object_unref(con); | ||
1014 | 315 | |||
1015 | 316 | return; | ||
1016 | 317 | } | ||
1017 | 318 | |||
1018 | 319 | static gboolean | ||
1019 | 320 | emit_name_change (gpointer user_data) | ||
1020 | 321 | { | ||
1021 | 322 | GDBusConnection * con = (GDBusConnection *) user_data; | ||
1022 | 323 | const gchar *unique_name = g_dbus_connection_get_unique_name(con); | ||
1023 | 324 | |||
1024 | 325 | // We are allowed to emit this (instead of DBus) because we link against | ||
1025 | 326 | // helpers-test, a special version of the helper code that listens | ||
1026 | 327 | // for this signal from anyone. | ||
1027 | 328 | g_dbus_connection_emit_signal(con, | ||
1028 | 329 | unique_name, /* destination */ | ||
1029 | 330 | "/org/freedesktop/DBus", /* path */ | ||
1030 | 331 | "org.freedesktop.DBus", /* interface */ | ||
1031 | 332 | "NameOwnerChanged", /* signal */ | ||
1032 | 333 | g_variant_new("(sss)", unique_name, unique_name, ""), /* params */ | ||
1033 | 334 | NULL); | ||
1034 | 335 | |||
1035 | 336 | return G_SOURCE_REMOVE; | ||
1036 | 337 | } | ||
1037 | 338 | |||
1038 | 339 | TEST_F(HelperHandshakeTest, StopsWaitingIfUnityExits) | ||
1039 | 340 | { | ||
1040 | 341 | bool timeout_reached = false; | ||
1041 | 342 | bool handshake_succeeded = false; | ||
1042 | 343 | GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | ||
1043 | 344 | |||
1044 | 345 | g_timeout_add_seconds(1, emit_name_change, con); | ||
1045 | 346 | guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached); | ||
1046 | 347 | |||
1047 | 348 | guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL); | ||
1048 | 349 | handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL); | ||
1049 | 350 | g_main_loop_run(mainloop); | ||
1050 | 351 | g_dbus_connection_remove_filter(con, filter); | ||
1051 | 352 | |||
1052 | 353 | g_dbus_connection_emit_signal(con, | ||
1053 | 354 | g_dbus_connection_get_unique_name(con), /* destination */ | ||
1054 | 355 | "/", /* path */ | ||
1055 | 356 | "com.canonical.UbuntuAppLaunch", /* interface */ | ||
1056 | 357 | "UnityStartingSignal", /* signal */ | ||
1057 | 358 | g_variant_new("(s)", "fooapp"), /* params */ | ||
1058 | 359 | NULL); | ||
1059 | 360 | |||
1060 | 361 | starting_handshake_wait(handshake); | ||
1061 | 362 | |||
1062 | 363 | ASSERT_FALSE(timeout_reached); | ||
1063 | 364 | ASSERT_FALSE(handshake_succeeded); | ||
1064 | 365 | g_source_remove(outertimeout); | ||
1065 | 366 | g_object_unref(con); | ||
1066 | 367 | |||
1067 | 368 | return; | ||
1068 | 369 | } | ||
1069 | 370 | |||
1070 | 121 | 371 | ||
1071 | === modified file 'tests/libual-test.cc' | |||
1072 | --- tests/libual-test.cc 2015-08-12 02:45:40 +0000 | |||
1073 | +++ tests/libual-test.cc 2015-12-02 18:30:50 +0000 | |||
1074 | @@ -38,11 +38,19 @@ | |||
1075 | 38 | DbusTestDbusMock * mock = NULL; | 38 | DbusTestDbusMock * mock = NULL; |
1076 | 39 | DbusTestDbusMock * cgmock = NULL; | 39 | DbusTestDbusMock * cgmock = NULL; |
1077 | 40 | GDBusConnection * bus = NULL; | 40 | GDBusConnection * bus = NULL; |
1078 | 41 | std::string last_starting_appid; | ||
1079 | 41 | std::string last_focus_appid; | 42 | std::string last_focus_appid; |
1080 | 42 | std::string last_resume_appid; | 43 | std::string last_resume_appid; |
1081 | 43 | guint resume_timeout = 0; | 44 | guint resume_timeout = 0; |
1082 | 44 | 45 | ||
1083 | 45 | private: | 46 | private: |
1084 | 47 | static void starting_cb (const gchar * appid, gpointer user_data) { | ||
1085 | 48 | g_debug("Starting Callback: %s", appid); | ||
1086 | 49 | LibUAL * _this = static_cast<LibUAL *>(user_data); | ||
1087 | 50 | _this->last_starting_appid = appid; | ||
1088 | 51 | ASSERT_TRUE(ubuntu_app_launch_observer_finish_app_starting(appid, TRUE)); | ||
1089 | 52 | } | ||
1090 | 53 | |||
1091 | 46 | static void focus_cb (const gchar * appid, gpointer user_data) { | 54 | static void focus_cb (const gchar * appid, gpointer user_data) { |
1092 | 47 | g_debug("Focus Callback: %s", appid); | 55 | g_debug("Focus Callback: %s", appid); |
1093 | 48 | LibUAL * _this = static_cast<LibUAL *>(user_data); | 56 | LibUAL * _this = static_cast<LibUAL *>(user_data); |
1094 | @@ -258,11 +266,13 @@ | |||
1095 | 258 | /* Make sure we pretend the CG manager is just on our bus */ | 266 | /* Make sure we pretend the CG manager is just on our bus */ |
1096 | 259 | g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE); | 267 | g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE); |
1097 | 260 | 268 | ||
1098 | 269 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_cb, this)); | ||
1099 | 261 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this)); | 270 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this)); |
1100 | 262 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this)); | 271 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this)); |
1101 | 263 | } | 272 | } |
1102 | 264 | 273 | ||
1103 | 265 | virtual void TearDown() { | 274 | virtual void TearDown() { |
1104 | 275 | ubuntu_app_launch_observer_delete_app_starting(starting_cb, this); | ||
1105 | 266 | ubuntu_app_launch_observer_delete_app_focus(focus_cb, this); | 276 | ubuntu_app_launch_observer_delete_app_focus(focus_cb, this); |
1106 | 267 | ubuntu_app_launch_observer_delete_app_resume(resume_cb, this); | 277 | ubuntu_app_launch_observer_delete_app_resume(resume_cb, this); |
1107 | 268 | 278 | ||
1108 | @@ -426,6 +436,140 @@ | |||
1109 | 426 | g_variant_unref(env); | 436 | g_variant_unref(env); |
1110 | 427 | } | 437 | } |
1111 | 428 | 438 | ||
1112 | 439 | typedef struct { | ||
1113 | 440 | int count; | ||
1114 | 441 | GMainLoop *loop; | ||
1115 | 442 | } async_data_t; | ||
1116 | 443 | |||
1117 | 444 | static void | ||
1118 | 445 | async_test_app_focused_cb (const gchar * appid, gpointer user_data) | ||
1119 | 446 | { | ||
1120 | 447 | async_data_t * data = (async_data_t *)user_data; | ||
1121 | 448 | data->count++; | ||
1122 | 449 | g_main_loop_quit(data->loop); | ||
1123 | 450 | } | ||
1124 | 451 | |||
1125 | 452 | static gboolean | ||
1126 | 453 | two_second_reached (gpointer user_data) | ||
1127 | 454 | { | ||
1128 | 455 | GMainLoop * loop = (GMainLoop *)user_data; | ||
1129 | 456 | g_main_loop_quit(loop); | ||
1130 | 457 | return G_SOURCE_REMOVE; | ||
1131 | 458 | } | ||
1132 | 459 | |||
1133 | 460 | TEST_F(LibUAL, StartApplicationAsync) | ||
1134 | 461 | { | ||
1135 | 462 | DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); | ||
1136 | 463 | |||
1137 | 464 | async_data_t data = { | ||
1138 | 465 | .count = 0, | ||
1139 | 466 | .loop = g_main_loop_new(NULL, FALSE) | ||
1140 | 467 | }; | ||
1141 | 468 | |||
1142 | 469 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(async_test_app_focused_cb, &data)); | ||
1143 | 470 | |||
1144 | 471 | /* Basic make sure we can send the event */ | ||
1145 | 472 | guint outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop); | ||
1146 | 473 | ubuntu_app_launch_start_application_async("com.test.good_application_1.2.3", NULL); | ||
1147 | 474 | g_main_loop_run(data.loop); | ||
1148 | 475 | g_source_remove(outertimeout); | ||
1149 | 476 | ASSERT_EQ(1, data.count); | ||
1150 | 477 | |||
1151 | 478 | EXPECT_EQ(1, dbus_test_dbus_mock_object_check_method_call(mock, obj, "Start", NULL, NULL)); | ||
1152 | 479 | ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); | ||
1153 | 480 | |||
1154 | 481 | /* Now look at the details of the call */ | ||
1155 | 482 | outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop); | ||
1156 | 483 | ubuntu_app_launch_start_application_async("com.test.good_application_1.2.3", NULL); | ||
1157 | 484 | g_main_loop_run(data.loop); | ||
1158 | 485 | g_source_remove(outertimeout); | ||
1159 | 486 | ASSERT_EQ(2, data.count); | ||
1160 | 487 | |||
1161 | 488 | guint len = 0; | ||
1162 | 489 | const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); | ||
1163 | 490 | EXPECT_NE(nullptr, calls); | ||
1164 | 491 | EXPECT_EQ(1, len); | ||
1165 | 492 | |||
1166 | 493 | EXPECT_STREQ("Start", calls->name); | ||
1167 | 494 | EXPECT_EQ(2, g_variant_n_children(calls->params)); | ||
1168 | 495 | |||
1169 | 496 | GVariant * block = g_variant_get_child_value(calls->params, 1); | ||
1170 | 497 | EXPECT_TRUE(g_variant_get_boolean(block)); | ||
1171 | 498 | g_variant_unref(block); | ||
1172 | 499 | |||
1173 | 500 | GVariant * env = g_variant_get_child_value(calls->params, 0); | ||
1174 | 501 | EXPECT_TRUE(check_env(env, "APP_ID", "com.test.good_application_1.2.3")); | ||
1175 | 502 | g_variant_unref(env); | ||
1176 | 503 | |||
1177 | 504 | ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL)); | ||
1178 | 505 | |||
1179 | 506 | /* Let's pass some URLs */ | ||
1180 | 507 | const gchar * urls[] = { | ||
1181 | 508 | "http://ubuntu.com/", | ||
1182 | 509 | "https://ubuntu.com/", | ||
1183 | 510 | "file:///home/phablet/test.txt", | ||
1184 | 511 | NULL | ||
1185 | 512 | }; | ||
1186 | 513 | outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop); | ||
1187 | 514 | ubuntu_app_launch_start_application("com.test.good_application_1.2.3", urls); | ||
1188 | 515 | g_main_loop_run(data.loop); | ||
1189 | 516 | g_source_remove(outertimeout); | ||
1190 | 517 | ASSERT_EQ(3, data.count); | ||
1191 | 518 | |||
1192 | 519 | len = 0; | ||
1193 | 520 | calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); | ||
1194 | 521 | EXPECT_NE(nullptr, calls); | ||
1195 | 522 | EXPECT_EQ(1, len); | ||
1196 | 523 | |||
1197 | 524 | env = g_variant_get_child_value(calls->params, 0); | ||
1198 | 525 | EXPECT_TRUE(check_env(env, "APP_ID", "com.test.good_application_1.2.3")); | ||
1199 | 526 | EXPECT_TRUE(check_env(env, "APP_URIS", "'http://ubuntu.com/' 'https://ubuntu.com/' 'file:///home/phablet/test.txt'")); | ||
1200 | 527 | g_variant_unref(env); | ||
1201 | 528 | |||
1202 | 529 | ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_focus(async_test_app_focused_cb, &data)); | ||
1203 | 530 | g_main_loop_unref(data.loop); | ||
1204 | 531 | |||
1205 | 532 | return; | ||
1206 | 533 | } | ||
1207 | 534 | |||
1208 | 535 | TEST_F(LibUAL, StartApplicationAsyncTest) | ||
1209 | 536 | { | ||
1210 | 537 | DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); | ||
1211 | 538 | |||
1212 | 539 | async_data_t data = { | ||
1213 | 540 | .count = 0, | ||
1214 | 541 | .loop = g_main_loop_new(NULL, FALSE) | ||
1215 | 542 | }; | ||
1216 | 543 | |||
1217 | 544 | ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(async_test_app_focused_cb, &data)); | ||
1218 | 545 | |||
1219 | 546 | guint outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop); | ||
1220 | 547 | ubuntu_app_launch_start_application_async_test("com.test.good_application_1.2.3", NULL); | ||
1221 | 548 | g_main_loop_run(data.loop); | ||
1222 | 549 | g_source_remove(outertimeout); | ||
1223 | 550 | ASSERT_EQ(1, data.count); | ||
1224 | 551 | |||
1225 | 552 | guint len = 0; | ||
1226 | 553 | const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL); | ||
1227 | 554 | EXPECT_NE(nullptr, calls); | ||
1228 | 555 | EXPECT_EQ(1, len); | ||
1229 | 556 | |||
1230 | 557 | EXPECT_STREQ("Start", calls->name); | ||
1231 | 558 | EXPECT_EQ(2, g_variant_n_children(calls->params)); | ||
1232 | 559 | |||
1233 | 560 | GVariant * block = g_variant_get_child_value(calls->params, 1); | ||
1234 | 561 | EXPECT_TRUE(g_variant_get_boolean(block)); | ||
1235 | 562 | g_variant_unref(block); | ||
1236 | 563 | |||
1237 | 564 | GVariant * env = g_variant_get_child_value(calls->params, 0); | ||
1238 | 565 | EXPECT_TRUE(check_env(env, "APP_ID", "com.test.good_application_1.2.3")); | ||
1239 | 566 | EXPECT_TRUE(check_env(env, "QT_LOAD_TESTABILITY", "1")); | ||
1240 | 567 | g_variant_unref(env); | ||
1241 | 568 | |||
1242 | 569 | ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_focus(async_test_app_focused_cb, &data)); | ||
1243 | 570 | g_main_loop_unref(data.loop); | ||
1244 | 571 | } | ||
1245 | 572 | |||
1246 | 429 | TEST_F(LibUAL, StopApplication) | 573 | TEST_F(LibUAL, StopApplication) |
1247 | 430 | { | 574 | { |
1248 | 431 | DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); | 575 | DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL); |
1249 | @@ -748,7 +892,6 @@ | |||
1250 | 748 | 892 | ||
1251 | 749 | TEST_F(LibUAL, StartingResponses) | 893 | TEST_F(LibUAL, StartingResponses) |
1252 | 750 | { | 894 | { |
1253 | 751 | std::string last_observer; | ||
1254 | 752 | unsigned int starting_count = 0; | 895 | unsigned int starting_count = 0; |
1255 | 753 | GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); | 896 | GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
1256 | 754 | guint filter = g_dbus_connection_add_filter(session, | 897 | guint filter = g_dbus_connection_add_filter(session, |
1257 | @@ -756,8 +899,6 @@ | |||
1258 | 756 | &starting_count, | 899 | &starting_count, |
1259 | 757 | NULL); | 900 | NULL); |
1260 | 758 | 901 | ||
1261 | 759 | EXPECT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_observer, &last_observer)); | ||
1262 | 760 | |||
1263 | 761 | g_dbus_connection_emit_signal(session, | 902 | g_dbus_connection_emit_signal(session, |
1264 | 762 | NULL, /* destination */ | 903 | NULL, /* destination */ |
1265 | 763 | "/", /* path */ | 904 | "/", /* path */ |
1266 | @@ -768,11 +909,9 @@ | |||
1267 | 768 | 909 | ||
1268 | 769 | pause(100); | 910 | pause(100); |
1269 | 770 | 911 | ||
1271 | 771 | EXPECT_EQ("com.test.good_application_1.2.3", last_observer); | 912 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); |
1272 | 772 | EXPECT_EQ(1, starting_count); | 913 | EXPECT_EQ(1, starting_count); |
1273 | 773 | 914 | ||
1274 | 774 | EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_starting(starting_observer, &last_observer)); | ||
1275 | 775 | |||
1276 | 776 | g_dbus_connection_remove_filter(session, filter); | 915 | g_dbus_connection_remove_filter(session, filter); |
1277 | 777 | g_object_unref(session); | 916 | g_object_unref(session); |
1278 | 778 | } | 917 | } |
1279 | @@ -781,6 +920,7 @@ | |||
1280 | 781 | { | 920 | { |
1281 | 782 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL)); | 921 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL)); |
1282 | 783 | pause(50); /* Ensure all the events come through */ | 922 | pause(50); /* Ensure all the events come through */ |
1283 | 923 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); | ||
1284 | 784 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); | 924 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
1285 | 785 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); | 925 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
1286 | 786 | } | 926 | } |
1287 | @@ -817,6 +957,7 @@ | |||
1288 | 817 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris)); | 957 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris)); |
1289 | 818 | pause(100); /* Ensure all the events come through */ | 958 | pause(100); /* Ensure all the events come through */ |
1290 | 819 | 959 | ||
1291 | 960 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); | ||
1292 | 820 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); | 961 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
1293 | 821 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); | 962 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
1294 | 822 | 963 | ||
1295 | @@ -851,6 +992,7 @@ | |||
1296 | 851 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris)); | 992 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris)); |
1297 | 852 | pause(100); /* Ensure all the events come through */ | 993 | pause(100); /* Ensure all the events come through */ |
1298 | 853 | 994 | ||
1299 | 995 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); | ||
1300 | 854 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); | 996 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
1301 | 855 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); | 997 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
1302 | 856 | } | 998 | } |
1303 | @@ -861,6 +1003,7 @@ | |||
1304 | 861 | 1003 | ||
1305 | 862 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL)); | 1004 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL)); |
1306 | 863 | pause(1000); /* Ensure all the events come through */ | 1005 | pause(1000); /* Ensure all the events come through */ |
1307 | 1006 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); | ||
1308 | 864 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); | 1007 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
1309 | 865 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); | 1008 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
1310 | 866 | } | 1009 | } |
1311 | @@ -876,6 +1019,7 @@ | |||
1312 | 876 | 1019 | ||
1313 | 877 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris)); | 1020 | ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris)); |
1314 | 878 | pause(1000); /* Ensure all the events come through */ | 1021 | pause(1000); /* Ensure all the events come through */ |
1315 | 1022 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); | ||
1316 | 879 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); | 1023 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
1317 | 880 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); | 1024 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
1318 | 881 | } | 1025 | } |
1319 | @@ -915,6 +1059,7 @@ | |||
1320 | 915 | 1059 | ||
1321 | 916 | pause(1000); /* Ensure all the events come through */ | 1060 | pause(1000); /* Ensure all the events come through */ |
1322 | 917 | 1061 | ||
1323 | 1062 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid); | ||
1324 | 918 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); | 1063 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid); |
1325 | 919 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); | 1064 | EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid); |
1326 | 920 | 1065 | ||
1327 | 921 | 1066 | ||
1328 | === modified file 'tools/ubuntu-app-watch.c' | |||
1329 | --- tools/ubuntu-app-watch.c 2015-02-25 22:50:24 +0000 | |||
1330 | +++ tools/ubuntu-app-watch.c 2015-12-02 18:30:50 +0000 | |||
1331 | @@ -23,6 +23,7 @@ | |||
1332 | 23 | starting (const gchar * appid, gpointer user_data) | 23 | starting (const gchar * appid, gpointer user_data) |
1333 | 24 | { | 24 | { |
1334 | 25 | g_print("Starting %s\n", appid); | 25 | g_print("Starting %s\n", appid); |
1335 | 26 | ubuntu_app_launch_observer_finish_app_starting(appid, TRUE); | ||
1336 | 26 | return; | 27 | return; |
1337 | 27 | } | 28 | } |
1338 | 28 | 29 |
FAILED: Continuous integration, rev:215 jenkins. qa.ubuntu. com/job/ ubuntu- app-launch- ci/21/ jenkins. qa.ubuntu. com/job/ ubuntu- app-launch- wily-amd64- ci/21/console jenkins. qa.ubuntu. com/job/ ubuntu- app-launch- wily-armhf- ci/21/console jenkins. qa.ubuntu. com/job/ ubuntu- app-launch- wily-i386- ci/21/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- app-launch- ci/21/rebuild
http://