Merge lp:~ted/ubuntu-app-launch/jobs-tests into lp:ubuntu-app-launch/16.10

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/ubuntu-app-launch/jobs-tests
Merge into: lp:ubuntu-app-launch/16.10
Prerequisite: lp:~ted/ubuntu-app-launch/abstract-jobs
Diff against target: 1974 lines (+1052/-593)
11 files modified
data/com.canonical.UbuntuAppLaunch.xml (+8/-0)
debian/control (+1/-0)
libubuntu-app-launch/jobs-base.cpp (+201/-1)
libubuntu-app-launch/jobs-base.h (+60/-1)
libubuntu-app-launch/jobs-upstart.cpp (+332/-0)
libubuntu-app-launch/jobs-upstart.h (+35/-0)
libubuntu-app-launch/registry-impl.cpp (+0/-501)
libubuntu-app-launch/registry-impl.h (+2/-68)
libubuntu-app-launch/registry.cpp (+27/-7)
tests/CMakeLists.txt (+22/-15)
tests/jobs-base-test.cpp (+364/-0)
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/jobs-tests
Reviewer Review Type Date Requested Status
Unity API Team Pending
Review via email: mp+309407@code.launchpad.net

This proposal has been superseded by a proposal from 2016-11-10.

Commit message

Jobs interface specific tests

To post a comment you must log in.
lp:~ted/ubuntu-app-launch/jobs-tests updated
265. By Ted Gould

Add dependency on google mock

266. By Ted Gould

Aligning with the signal instances branch

267. By Ted Gould

Pulling through updates

268. By Ted Gould

Pulling through trunk

269. By Ted Gould

Put the spew master into its own header

270. By Ted Gould

Get the static libs into the coverage game

271. By Ted Gould

Grab signal instances changes

272. By Ted Gould

Test fixes

273. By Ted Gould

Make the comparison values unsigned

274. By Ted Gould

Merge upstream changes

275. By Ted Gould

Backport the changes on the gmock-fix branch to here, where most of the problems were caused

276. By Ted Gould

Use a clearer definition of unsigned

277. By Ted Gould

Only or onto the transitional google-mock package

278. By Ted Gould

Alphabetize list

279. By Ted Gould

Make build on 32-bit architectures where gsize is a different size

280. By Ted Gould

Make it so that tests can run in parallel without running over each other

281. By Ted Gould

Remove -lgcov hack and add snapd-info-test to the list of tests

282. By Ted Gould

Uhg, wanted to delete not just comment out

283. By Ted Gould

Update to trunk

284. By Ted Gould

Switch to the local socket to avoid conflicts

285. By Ted Gould

Make required libraries required

286. By Ted Gould

Fix listing of targets

Unmerged revisions

286. By Ted Gould

Fix listing of targets

285. By Ted Gould

Make required libraries required

284. By Ted Gould

Switch to the local socket to avoid conflicts

283. By Ted Gould

Update to trunk

282. By Ted Gould

Uhg, wanted to delete not just comment out

281. By Ted Gould

Remove -lgcov hack and add snapd-info-test to the list of tests

280. By Ted Gould

Make it so that tests can run in parallel without running over each other

279. By Ted Gould

Make build on 32-bit architectures where gsize is a different size

278. By Ted Gould

Alphabetize list

277. By Ted Gould

Only or onto the transitional google-mock package

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/com.canonical.UbuntuAppLaunch.xml'
2--- data/com.canonical.UbuntuAppLaunch.xml 2015-03-04 14:26:35 +0000
3+++ data/com.canonical.UbuntuAppLaunch.xml 2016-11-10 21:58:53 +0000
4@@ -3,29 +3,37 @@
5 <interface name="com.canonical.UpstartAppLaunch">
6 <signal name="UnityResumeRequest">
7 <arg type="s" name="appid" />
8+ <arg type="s" name="instance" />
9 </signal>
10 <signal name="UnityResumeResponse">
11 <arg type="s" name="appid" />
12+ <arg type="s" name="instance" />
13 </signal>
14 <signal name="UnityFocusRequest">
15 <arg type="s" name="appid" />
16+ <arg type="s" name="instance" />
17 </signal>
18 <signal name="UnityStartingBroadcast">
19 <arg type="s" name="appid" />
20+ <arg type="s" name="instance" />
21 </signal>
22 <signal name="UnityStartingSignal">
23 <arg type="s" name="appid" />
24+ <arg type="s" name="instance" />
25 </signal>
26 <signal name="ApplicationFailed">
27 <arg type="s" name="appid" />
28+ <arg type="s" name="instance" />
29 <arg type="s" name="stage" />
30 </signal>
31 <signal name="ApplicationPaused">
32 <arg type="s" name="appid" />
33+ <arg type="s" name="instance" />
34 <arg type="at" name="pids" />
35 </signal>
36 <signal name="ApplicationResumed">
37 <arg type="s" name="appid" />
38+ <arg type="s" name="instance" />
39 <arg type="at" name="pids" />
40 </signal>
41 </interface>
42
43=== modified file 'debian/control'
44--- debian/control 2016-08-23 15:04:08 +0000
45+++ debian/control 2016-11-10 21:58:53 +0000
46@@ -9,6 +9,7 @@
47 dbus-x11,
48 dbus-test-runner,
49 debhelper (>= 9),
50+ google-mock,
51 libcgmanager-dev,
52 libclick-0.4-dev,
53 libcurl4-dev | libcurl4-gnutls-dev,
54
55=== modified file 'libubuntu-app-launch/jobs-base.cpp'
56--- libubuntu-app-launch/jobs-base.cpp 2016-11-10 21:58:53 +0000
57+++ libubuntu-app-launch/jobs-base.cpp 2016-11-10 21:58:53 +0000
58@@ -37,7 +37,23 @@
59
60 Base::Base(const std::shared_ptr<Registry>& registry)
61 : registry_(registry)
62-{
63+ , dbus_(registry->impl->_dbus)
64+{
65+}
66+
67+Base::~Base()
68+{
69+ auto dohandle = [&](guint& handle) {
70+ if (handle != 0)
71+ {
72+ g_dbus_connection_signal_unsubscribe(dbus_.get(), handle);
73+ handle = 0;
74+ }
75+ };
76+
77+ dohandle(handle_managerSignalFocus);
78+ dohandle(handle_managerSignalResume);
79+ dohandle(handle_managerSignalStarting);
80 }
81
82 std::shared_ptr<Base> Base::determineFactory(std::shared_ptr<Registry> registry)
83@@ -45,6 +61,190 @@
84 return std::make_shared<jobs::manager::Upstart>(registry);
85 }
86
87+/** Take the GVariant of parameters and turn them into an application and
88+ and instance. Easier to read in the smaller function */
89+std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> Base::managerParams(
90+ const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg)
91+{
92+ std::shared_ptr<Application> app;
93+ std::shared_ptr<Application::Instance> instance;
94+
95+ const gchar* cappid = nullptr;
96+ g_variant_get(params.get(), "(&s)", &cappid);
97+
98+ auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
99+ app = ubuntu::app_launch::Application::create(appid, reg);
100+
101+ return std::make_tuple(app, instance);
102+}
103+
104+/** Used to store data for manager based signal handlers. Has a link to the
105+ registry and the callback to use in a C++ style. */
106+struct managerEventData
107+{
108+ /* Keeping a weak pointer because the handle is held by
109+ the registry implementation. */
110+ std::weak_ptr<Registry> weakReg;
111+ std::function<void(const std::shared_ptr<Registry>& reg,
112+ const std::shared_ptr<Application>& app,
113+ const std::shared_ptr<Application::Instance>& instance,
114+ const std::shared_ptr<GDBusConnection>&,
115+ const std::string&,
116+ const std::shared_ptr<GVariant>&)>
117+ func;
118+};
119+
120+/** Register for a signal for the manager. All of the signals needed this same
121+ code so it got pulled out into a function. Takes the same of the signal, the registry
122+ that we're using and a function to call after we've messaged all the parameters
123+ into being something C++-ish. */
124+guint Base::managerSignalHelper(const std::shared_ptr<Registry>& reg,
125+ const std::string& signalname,
126+ std::function<void(const std::shared_ptr<Registry>& reg,
127+ const std::shared_ptr<Application>& app,
128+ const std::shared_ptr<Application::Instance>& instance,
129+ const std::shared_ptr<GDBusConnection>&,
130+ const std::string&,
131+ const std::shared_ptr<GVariant>&)> responsefunc)
132+{
133+ managerEventData* focusdata = new managerEventData{reg, responsefunc};
134+
135+ return g_dbus_connection_signal_subscribe(
136+ reg->impl->_dbus.get(), /* bus */
137+ nullptr, /* sender */
138+ "com.canonical.UbuntuAppLaunch", /* interface */
139+ signalname.c_str(), /* signal */
140+ "/", /* path */
141+ nullptr, /* arg0 */
142+ G_DBUS_SIGNAL_FLAGS_NONE,
143+ [](GDBusConnection* cconn, const gchar* csender, const gchar*, const gchar*, const gchar*, GVariant* params,
144+ gpointer user_data) -> void {
145+ auto data = reinterpret_cast<managerEventData*>(user_data);
146+ auto reg = data->weakReg.lock();
147+
148+ /* If we're still conneted and the manager has been cleared
149+ we'll just be a no-op */
150+ auto ljobs = std::dynamic_pointer_cast<Base>(reg->impl->jobs);
151+ if (!ljobs->manager_)
152+ {
153+ return;
154+ }
155+
156+ auto vparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
157+ auto conn = std::shared_ptr<GDBusConnection>(reinterpret_cast<GDBusConnection*>(g_object_ref(cconn)),
158+ [](GDBusConnection* con) { g_clear_object(&con); });
159+ std::string sender = csender;
160+ std::shared_ptr<Application> app;
161+ std::shared_ptr<Application::Instance> instance;
162+
163+ std::tie(app, instance) = managerParams(vparams, reg);
164+
165+ data->func(reg, app, instance, conn, sender, vparams);
166+ },
167+ focusdata,
168+ [](gpointer user_data) {
169+ auto data = reinterpret_cast<managerEventData*>(user_data);
170+ delete data;
171+ }); /* user data destroy */
172+}
173+
174+/** Set the manager for the registry. This includes tracking the pointer
175+ as well as setting up the signals to call back into the manager. The
176+ signals are only setup once per registry even if the manager is cleared
177+ and changed again. They will just be no-op's in those cases.
178+*/
179+void Base::setManager(std::shared_ptr<Registry::Manager> manager)
180+{
181+ if (manager_)
182+ {
183+ throw std::runtime_error("Already have a manager and trying to set another");
184+ }
185+
186+ g_debug("Setting a new manager");
187+ manager_ = manager;
188+
189+ std::call_once(flag_managerSignals, [this]() {
190+ auto reg = registry_.lock();
191+
192+ if (!reg->impl->thread.executeOnThread<bool>([this, reg]() {
193+ handle_managerSignalFocus = managerSignalHelper(
194+ reg, "UnityFocusRequest",
195+ [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
196+ const std::shared_ptr<Application::Instance>& instance,
197+ const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
198+ const std::shared_ptr<GVariant>& params) {
199+ /* Nothing to do today */
200+ std::dynamic_pointer_cast<Base>(reg->impl->jobs)
201+ ->manager_->focusRequest(app, instance, [](bool response) {
202+ /* NOTE: We have no clue what thread this is gonna be
203+ executed on, but since we're just talking to the GDBus
204+ thread it isn't an issue today. Be careful in changing
205+ this code. */
206+ });
207+ });
208+ handle_managerSignalStarting = managerSignalHelper(
209+ reg, "UnityStartingBroadcast",
210+ [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
211+ const std::shared_ptr<Application::Instance>& instance,
212+ const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
213+ const std::shared_ptr<GVariant>& params) {
214+
215+ std::dynamic_pointer_cast<Base>(reg->impl->jobs)
216+ ->manager_->startingRequest(app, instance, [conn, sender, params](bool response) {
217+ /* NOTE: We have no clue what thread this is gonna be
218+ executed on, but since we're just talking to the GDBus
219+ thread it isn't an issue today. Be careful in changing
220+ this code. */
221+ if (response)
222+ {
223+ g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
224+ "/", /* path */
225+ "com.canonical.UbuntuAppLaunch", /* interface */
226+ "UnityStartingSignal", /* signal */
227+ params.get(), /* params, the same */
228+ nullptr); /* error */
229+ }
230+ });
231+ });
232+ handle_managerSignalResume = managerSignalHelper(
233+ reg, "UnityResumeRequest",
234+ [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
235+ const std::shared_ptr<Application::Instance>& instance,
236+ const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
237+ const std::shared_ptr<GVariant>& params) {
238+ std::dynamic_pointer_cast<Base>(reg->impl->jobs)
239+ ->manager_->resumeRequest(app, instance, [conn, sender, params](bool response) {
240+ /* NOTE: We have no clue what thread this is gonna be
241+ executed on, but since we're just talking to the GDBus
242+ thread it isn't an issue today. Be careful in changing
243+ this code. */
244+ if (response)
245+ {
246+ g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
247+ "/", /* path */
248+ "com.canonical.UbuntuAppLaunch", /* interface */
249+ "UnityResumeResponse", /* signal */
250+ params.get(), /* params, the same */
251+ nullptr); /* error */
252+ }
253+ });
254+ });
255+
256+ return true;
257+ }))
258+ {
259+ g_warning("Unable to install manager signals");
260+ }
261+ });
262+}
263+
264+/** Clear the manager pointer */
265+void Base::clearManager()
266+{
267+ g_debug("Clearing the manager");
268+ manager_.reset();
269+}
270+
271 } // namespace manager
272
273 namespace instance
274
275=== modified file 'libubuntu-app-launch/jobs-base.h'
276--- libubuntu-app-launch/jobs-base.h 2016-11-10 21:58:53 +0000
277+++ libubuntu-app-launch/jobs-base.h 2016-11-10 21:58:53 +0000
278@@ -18,7 +18,12 @@
279 */
280
281 #pragma once
282+
283 #include "application.h"
284+#include "registry.h"
285+
286+#include <core/signal.h>
287+#include <gio/gio.h>
288
289 namespace ubuntu
290 {
291@@ -86,7 +91,7 @@
292 {
293 public:
294 Base(const std::shared_ptr<Registry>& registry);
295- virtual ~Base() = default;
296+ virtual ~Base();
297
298 virtual std::shared_ptr<Application::Instance> launch(
299 const AppID& appId,
300@@ -107,8 +112,62 @@
301
302 static std::shared_ptr<Base> determineFactory(std::shared_ptr<Registry> registry);
303
304+ /* Signals to apps */
305+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStarted() = 0;
306+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStopped() = 0;
307+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
308+ appFailed() = 0;
309+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
310+ appPaused() = 0;
311+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
312+ appResumed() = 0;
313+
314+ /* App manager */
315+ virtual void setManager(std::shared_ptr<Registry::Manager> manager);
316+ virtual void clearManager();
317+
318 protected:
319+ /** A link to the registry */
320 std::weak_ptr<Registry> registry_;
321+
322+ /** The DBus connection we're connecting to */
323+ std::shared_ptr<GDBusConnection> dbus_;
324+
325+ /** Application manager instance */
326+ std::shared_ptr<Registry::Manager> manager_;
327+
328+ /** Signal object for applications started */
329+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> sig_appStarted;
330+ /** Signal object for applications stopped */
331+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> sig_appStopped;
332+ /** Signal object for applications failed */
333+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>
334+ sig_appFailed;
335+ /** Signal object for applications paused */
336+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>
337+ sig_appPaused;
338+ /** Signal object for applications resumed */
339+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>
340+ sig_appResumed;
341+
342+private:
343+ guint handle_managerSignalFocus{0}; /**< GDBus signal watcher handle for app focused signal */
344+ guint handle_managerSignalResume{0}; /**< GDBus signal watcher handle for app resumed signal */
345+ guint handle_managerSignalStarting{0}; /**< GDBus signal watcher handle for app starting signal */
346+
347+ std::once_flag flag_managerSignals; /**< Variable to track to see if signal handlers are installed for the manager
348+ signals of focused, resumed and starting */
349+
350+ static std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> managerParams(
351+ const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg);
352+ static guint managerSignalHelper(const std::shared_ptr<Registry>& reg,
353+ const std::string& signalname,
354+ std::function<void(const std::shared_ptr<Registry>& reg,
355+ const std::shared_ptr<Application>& app,
356+ const std::shared_ptr<Application::Instance>& instance,
357+ const std::shared_ptr<GDBusConnection>&,
358+ const std::string&,
359+ const std::shared_ptr<GVariant>&)> responsefunc);
360 };
361
362 } // namespace manager
363
364=== modified file 'libubuntu-app-launch/jobs-upstart.cpp'
365--- libubuntu-app-launch/jobs-upstart.cpp 2016-11-10 21:58:53 +0000
366+++ libubuntu-app-launch/jobs-upstart.cpp 2016-11-10 21:58:53 +0000
367@@ -403,6 +403,19 @@
368
369 Upstart::~Upstart()
370 {
371+ auto dohandle = [&](guint& handle) {
372+ if (handle != 0)
373+ {
374+ g_dbus_connection_signal_unsubscribe(dbus_.get(), handle);
375+ handle = 0;
376+ }
377+ };
378+
379+ dohandle(handle_appStarted);
380+ dohandle(handle_appStopped);
381+ dohandle(handle_appFailed);
382+ dohandle(handle_appPaused);
383+ dohandle(handle_appResumed);
384 }
385
386 /** Launch an application and create a new Upstart instance object to track
387@@ -576,6 +589,325 @@
388 return vect;
389 }
390
391+/** Structure to track the data needed for upstart events. This cleans
392+ up the lifecycle as we're passing this as a pointer through the
393+ GLib calls. */
394+struct upstartEventData
395+{
396+ /** Keeping a weak pointer because the handle is held by
397+ the registry implementation. */
398+ std::weak_ptr<Registry> weakReg;
399+};
400+
401+/** Regex to parse the JOB environment variable from Upstart */
402+std::regex jobenv_regex{"^JOB=(application\\-(?:click|snap|legacy))$"};
403+/** Regex to parse the INSTANCE environment variable from Upstart */
404+std::regex instanceenv_regex{"^INSTANCE=(.*?)(?:\\-([0-9]*))?+$"};
405+
406+/** Core of most of the events that come from Upstart directly. Includes parsing of the
407+ Upstart event environment and calling the appropriate signal with the right Application
408+ object and eventually its instance */
409+void Upstart::upstartEventEmitted(
410+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& signal,
411+ std::shared_ptr<GVariant> params,
412+ const std::shared_ptr<Registry>& reg)
413+{
414+ std::string jobname;
415+ std::string sappid;
416+ std::string instance;
417+
418+ gchar* env = nullptr;
419+ GVariant* envs = g_variant_get_child_value(params.get(), 1);
420+ GVariantIter iter;
421+ g_variant_iter_init(&iter, envs);
422+
423+ while (g_variant_iter_loop(&iter, "s", &env))
424+ {
425+ std::smatch match;
426+ std::string senv = env;
427+
428+ if (std::regex_match(senv, match, jobenv_regex))
429+ {
430+ jobname = match[1].str();
431+ }
432+ else if (std::regex_match(senv, match, instanceenv_regex))
433+ {
434+ sappid = match[1].str();
435+ instance = match[2].str();
436+ }
437+ }
438+
439+ g_variant_unref(envs);
440+
441+ if (jobname.empty())
442+ {
443+ return;
444+ }
445+
446+ g_debug("Upstart Event for job '%s' appid '%s' instance '%s'", jobname.c_str(), sappid.c_str(), instance.c_str());
447+
448+ auto appid = AppID::find(reg, sappid);
449+ auto app = Application::create(appid, reg);
450+
451+ // TODO: Figure otu creating instances
452+
453+ signal(app, {});
454+}
455+
456+/** Grab the signal object for application startup. If we're not already listing for
457+ those signals this sets up a listener for them. */
458+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Upstart::appStarted()
459+{
460+ std::call_once(flag_appStarted, [this]() {
461+ auto reg = registry_.lock();
462+
463+ reg->impl->thread.executeOnThread<bool>([this, reg]() {
464+ upstartEventData* data = new upstartEventData{reg};
465+
466+ handle_appStarted = g_dbus_connection_signal_subscribe(
467+ reg->impl->_dbus.get(), /* bus */
468+ nullptr, /* sender */
469+ DBUS_INTERFACE_UPSTART, /* interface */
470+ "EventEmitted", /* signal */
471+ DBUS_PATH_UPSTART, /* path */
472+ "started", /* arg0 */
473+ G_DBUS_SIGNAL_FLAGS_NONE,
474+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
475+ gpointer user_data) -> void {
476+ auto data = reinterpret_cast<upstartEventData*>(user_data);
477+ auto reg = data->weakReg.lock();
478+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
479+ auto upstart = std::dynamic_pointer_cast<Upstart>(reg->impl->jobs);
480+ upstart->upstartEventEmitted(upstart->sig_appStarted, sparams, reg);
481+ }, /* callback */
482+ data, /* user data */
483+ [](gpointer user_data) {
484+ auto data = reinterpret_cast<upstartEventData*>(user_data);
485+ delete data;
486+ }); /* user data destroy */
487+
488+ return true;
489+ });
490+ });
491+
492+ return sig_appStarted;
493+}
494+
495+/** Grab the signal object for application stopping. If we're not already listing for
496+ those signals this sets up a listener for them. */
497+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Upstart::appStopped()
498+{
499+ std::call_once(flag_appStopped, [this]() {
500+ auto reg = registry_.lock();
501+
502+ reg->impl->thread.executeOnThread<bool>([this, reg]() {
503+ upstartEventData* data = new upstartEventData{reg};
504+
505+ handle_appStopped = g_dbus_connection_signal_subscribe(
506+ reg->impl->_dbus.get(), /* bus */
507+ nullptr, /* sender */
508+ DBUS_INTERFACE_UPSTART, /* interface */
509+ "EventEmitted", /* signal */
510+ DBUS_PATH_UPSTART, /* path */
511+ "stopped", /* arg0 */
512+ G_DBUS_SIGNAL_FLAGS_NONE,
513+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
514+ gpointer user_data) -> void {
515+ auto data = reinterpret_cast<upstartEventData*>(user_data);
516+ auto reg = data->weakReg.lock();
517+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
518+ auto upstart = std::dynamic_pointer_cast<Upstart>(reg->impl->jobs);
519+ upstart->upstartEventEmitted(upstart->sig_appStopped, sparams, reg);
520+ }, /* callback */
521+ data, /* user data */
522+ [](gpointer user_data) {
523+ auto data = reinterpret_cast<upstartEventData*>(user_data);
524+ delete data;
525+ }); /* user data destroy */
526+
527+ return true;
528+ });
529+ });
530+
531+ return sig_appStopped;
532+}
533+
534+/** Grab the signal object for application failing. If we're not already listing for
535+ those signals this sets up a listener for them. */
536+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
537+ Upstart::appFailed()
538+{
539+ std::call_once(flag_appFailed, [this]() {
540+ auto reg = registry_.lock();
541+
542+ reg->impl->thread.executeOnThread<bool>([this, reg]() {
543+ upstartEventData* data = new upstartEventData{reg};
544+
545+ handle_appFailed = g_dbus_connection_signal_subscribe(
546+ reg->impl->_dbus.get(), /* bus */
547+ nullptr, /* sender */
548+ "com.canonical.UbuntuAppLaunch", /* interface */
549+ "ApplicationFailed", /* signal */
550+ "/", /* path */
551+ nullptr, /* arg0 */
552+ G_DBUS_SIGNAL_FLAGS_NONE,
553+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
554+ gpointer user_data) -> void {
555+ auto data = reinterpret_cast<upstartEventData*>(user_data);
556+ auto reg = data->weakReg.lock();
557+
558+ const gchar* sappid = NULL;
559+ const gchar* typestr = NULL;
560+
561+ Registry::FailureType type = Registry::FailureType::CRASH;
562+ g_variant_get(params, "(&s&s)", &sappid, &typestr);
563+
564+ if (g_strcmp0("crash", typestr) == 0)
565+ {
566+ type = Registry::FailureType::CRASH;
567+ }
568+ else if (g_strcmp0("start-failure", typestr) == 0)
569+ {
570+ type = Registry::FailureType::START_FAILURE;
571+ }
572+ else
573+ {
574+ g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
575+ }
576+
577+ auto appid = AppID::find(reg, sappid);
578+ auto app = Application::create(appid, reg);
579+
580+ /* TODO: Instance issues */
581+
582+ auto upstart = std::dynamic_pointer_cast<Upstart>(reg->impl->jobs);
583+ upstart->sig_appFailed(app, {}, type);
584+ }, /* callback */
585+ data, /* user data */
586+ [](gpointer user_data) {
587+ auto data = reinterpret_cast<upstartEventData*>(user_data);
588+ delete data;
589+ }); /* user data destroy */
590+
591+ return true;
592+ });
593+ });
594+
595+ return sig_appFailed;
596+}
597+
598+/** Core handler for pause and resume events. Includes turning the GVariant
599+ pid list into a std::vector and getting the application object. */
600+void Upstart::pauseEventEmitted(
601+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& signal,
602+ const std::shared_ptr<GVariant>& params,
603+ const std::shared_ptr<Registry>& reg)
604+{
605+ std::vector<pid_t> pids;
606+ GVariant* vappid = g_variant_get_child_value(params.get(), 0);
607+ GVariant* vpids = g_variant_get_child_value(params.get(), 1);
608+ guint64 pid;
609+ GVariantIter thispid;
610+ g_variant_iter_init(&thispid, vpids);
611+
612+ while (g_variant_iter_loop(&thispid, "t", &pid))
613+ {
614+ pids.emplace_back(pid);
615+ }
616+
617+ auto cappid = g_variant_get_string(vappid, NULL);
618+ auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
619+ auto app = Application::create(appid, reg);
620+
621+ /* TODO: Instance */
622+ signal(app, {}, pids);
623+
624+ g_variant_unref(vappid);
625+ g_variant_unref(vpids);
626+
627+ return;
628+}
629+
630+/** Grab the signal object for application paused. If we're not already listing for
631+ those signals this sets up a listener for them. */
632+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
633+ Upstart::appPaused()
634+{
635+ std::call_once(flag_appPaused, [this]() {
636+ auto reg = registry_.lock();
637+
638+ reg->impl->thread.executeOnThread<bool>([this, reg]() {
639+ upstartEventData* data = new upstartEventData{reg};
640+
641+ handle_appPaused = g_dbus_connection_signal_subscribe(
642+ reg->impl->_dbus.get(), /* bus */
643+ nullptr, /* sender */
644+ "com.canonical.UbuntuAppLaunch", /* interface */
645+ "ApplicationPaused", /* signal */
646+ "/", /* path */
647+ nullptr, /* arg0 */
648+ G_DBUS_SIGNAL_FLAGS_NONE,
649+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
650+ gpointer user_data) -> void {
651+ auto data = reinterpret_cast<upstartEventData*>(user_data);
652+ auto reg = data->weakReg.lock();
653+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
654+ auto upstart = std::dynamic_pointer_cast<Upstart>(reg->impl->jobs);
655+ upstart->pauseEventEmitted(upstart->sig_appPaused, sparams, reg);
656+ }, /* callback */
657+ data, /* user data */
658+ [](gpointer user_data) {
659+ auto data = reinterpret_cast<upstartEventData*>(user_data);
660+ delete data;
661+ }); /* user data destroy */
662+
663+ return true;
664+ });
665+ });
666+
667+ return sig_appPaused;
668+}
669+
670+/** Grab the signal object for application resumed. If we're not already listing for
671+ those signals this sets up a listener for them. */
672+core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
673+ Upstart::appResumed()
674+{
675+ std::call_once(flag_appResumed, [this]() {
676+ auto reg = registry_.lock();
677+
678+ reg->impl->thread.executeOnThread<bool>([this, reg]() {
679+ upstartEventData* data = new upstartEventData{reg};
680+
681+ handle_appResumed = g_dbus_connection_signal_subscribe(
682+ reg->impl->_dbus.get(), /* bus */
683+ nullptr, /* sender */
684+ "com.canonical.UbuntuAppLaunch", /* interface */
685+ "ApplicationResumed", /* signal */
686+ "/", /* path */
687+ nullptr, /* arg0 */
688+ G_DBUS_SIGNAL_FLAGS_NONE,
689+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
690+ gpointer user_data) -> void {
691+ auto data = reinterpret_cast<upstartEventData*>(user_data);
692+ auto reg = data->weakReg.lock();
693+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
694+ auto upstart = std::dynamic_pointer_cast<Upstart>(reg->impl->jobs);
695+ upstart->pauseEventEmitted(upstart->sig_appResumed, sparams, reg);
696+ }, /* callback */
697+ data, /* user data */
698+ [](gpointer user_data) {
699+ auto data = reinterpret_cast<upstartEventData*>(user_data);
700+ delete data;
701+ }); /* user data destroy */
702+
703+ return true;
704+ });
705+ });
706+
707+ return sig_appResumed;
708+}
709+
710 /** Initialize the CGManager connection, including a timeout to disconnect
711 as CGManager doesn't free resources entirely well. So it's better if
712 we connect and disconnect occationally */
713
714=== modified file 'libubuntu-app-launch/jobs-upstart.h'
715--- libubuntu-app-launch/jobs-upstart.h 2016-11-10 21:58:53 +0000
716+++ libubuntu-app-launch/jobs-upstart.h 2016-11-10 21:58:53 +0000
717@@ -54,6 +54,16 @@
718
719 virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) override;
720
721+ /* Signals to apps */
722+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStarted() override;
723+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStopped() override;
724+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
725+ appFailed() override;
726+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
727+ appPaused() override;
728+ virtual core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
729+ appResumed() override;
730+
731 std::vector<pid_t> pidsFromCgroup(const std::string& jobpath);
732
733 std::list<std::string> upstartInstancesForJob(const std::string& job);
734@@ -67,6 +77,31 @@
735 /** Getting the Upstart job path is relatively expensive in
736 that it requires a DBus call. Worth keeping a cache of. */
737 std::map<std::string, std::string> upstartJobPathCache_;
738+
739+ guint handle_appStarted{0}; /**< GDBus signal watcher handle for app started signal */
740+ guint handle_appStopped{0}; /**< GDBus signal watcher handle for app stopped signal */
741+ guint handle_appFailed{0}; /**< GDBus signal watcher handle for app failed signal */
742+ guint handle_appPaused{0}; /**< GDBus signal watcher handle for app paused signal */
743+ guint handle_appResumed{0}; /**< GDBus signal watcher handle for app resumed signal */
744+
745+ std::once_flag flag_appStarted; /**< Variable to track to see if signal handlers are installed for application
746+ started */
747+ std::once_flag flag_appStopped; /**< Variable to track to see if signal handlers are installed for application
748+ stopped */
749+ std::once_flag
750+ flag_appFailed; /**< Variable to track to see if signal handlers are installed for application failed */
751+ std::once_flag
752+ flag_appPaused; /**< Variable to track to see if signal handlers are installed for application paused */
753+ std::once_flag flag_appResumed; /**< Variable to track to see if signal handlers are installed for application
754+ resumed */
755+
756+ void upstartEventEmitted(core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& signal,
757+ std::shared_ptr<GVariant> params,
758+ const std::shared_ptr<Registry>& reg);
759+ void pauseEventEmitted(
760+ core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& signal,
761+ const std::shared_ptr<GVariant>& params,
762+ const std::shared_ptr<Registry>& reg);
763 };
764
765 } // namespace manager
766
767=== modified file 'libubuntu-app-launch/registry-impl.cpp'
768--- libubuntu-app-launch/registry-impl.cpp 2016-11-10 21:58:53 +0000
769+++ libubuntu-app-launch/registry-impl.cpp 2016-11-10 21:58:53 +0000
770@@ -36,23 +36,6 @@
771 zgLog_.reset();
772 jobs.reset();
773
774- auto dohandle = [&](guint& handle) {
775- if (handle != 0)
776- {
777- g_dbus_connection_signal_unsubscribe(_dbus.get(), handle);
778- handle = 0;
779- }
780- };
781-
782- dohandle(handle_appStarted);
783- dohandle(handle_appStopped);
784- dohandle(handle_appFailed);
785- dohandle(handle_appPaused);
786- dohandle(handle_appResumed);
787- dohandle(handle_managerSignalFocus);
788- dohandle(handle_managerSignalResume);
789- dohandle(handle_managerSignalStarting);
790-
791 if (_dbus)
792 g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr);
793 _dbus.reset();
794@@ -301,194 +284,6 @@
795 return _iconFinders[basePath];
796 }
797
798-/** Structure to track the data needed for upstart events. This cleans
799- up the lifecycle as we're passing this as a pointer through the
800- GLib calls. */
801-struct upstartEventData
802-{
803- /** Keeping a weak pointer because the handle is held by
804- the registry implementation. */
805- std::weak_ptr<Registry> weakReg;
806-};
807-
808-/** Take the GVariant of parameters and turn them into an application and
809- and instance. Easier to read in the smaller function */
810-std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> Registry::Impl::managerParams(
811- const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg)
812-{
813- std::shared_ptr<Application> app;
814- std::shared_ptr<Application::Instance> instance;
815-
816- const gchar* cappid = nullptr;
817- g_variant_get(params.get(), "(&s)", &cappid);
818-
819- auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
820- app = ubuntu::app_launch::Application::create(appid, reg);
821-
822- return std::make_tuple(app, instance);
823-}
824-
825-/** Used to store data for manager based signal handlers. Has a link to the
826- registry and the callback to use in a C++ style. */
827-struct managerEventData
828-{
829- /* Keeping a weak pointer because the handle is held by
830- the registry implementation. */
831- std::weak_ptr<Registry> weakReg;
832- std::function<void(const std::shared_ptr<Registry>& reg,
833- const std::shared_ptr<Application>& app,
834- const std::shared_ptr<Application::Instance>& instance,
835- const std::shared_ptr<GDBusConnection>&,
836- const std::string&,
837- const std::shared_ptr<GVariant>&)>
838- func;
839-};
840-
841-/** Register for a signal for the manager. All of the signals needed this same
842- code so it got pulled out into a function. Takes the same of the signal, the registry
843- that we're using and a function to call after we've messaged all the parameters
844- into being something C++-ish. */
845-guint Registry::Impl::managerSignalHelper(const std::shared_ptr<Registry>& reg,
846- const std::string& signalname,
847- std::function<void(const std::shared_ptr<Registry>& reg,
848- const std::shared_ptr<Application>& app,
849- const std::shared_ptr<Application::Instance>& instance,
850- const std::shared_ptr<GDBusConnection>&,
851- const std::string&,
852- const std::shared_ptr<GVariant>&)> responsefunc)
853-{
854- managerEventData* focusdata = new managerEventData{reg, responsefunc};
855-
856- return g_dbus_connection_signal_subscribe(
857- reg->impl->_dbus.get(), /* bus */
858- nullptr, /* sender */
859- "com.canonical.UbuntuAppLaunch", /* interface */
860- signalname.c_str(), /* signal */
861- "/", /* path */
862- nullptr, /* arg0 */
863- G_DBUS_SIGNAL_FLAGS_NONE,
864- [](GDBusConnection* cconn, const gchar* csender, const gchar*, const gchar*, const gchar*, GVariant* params,
865- gpointer user_data) -> void {
866- auto data = reinterpret_cast<managerEventData*>(user_data);
867- auto reg = data->weakReg.lock();
868-
869- /* If we're still conneted and the manager has been cleared
870- we'll just be a no-op */
871- if (!reg->impl->manager_)
872- {
873- return;
874- }
875-
876- auto vparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
877- auto conn = std::shared_ptr<GDBusConnection>(reinterpret_cast<GDBusConnection*>(g_object_ref(cconn)),
878- [](GDBusConnection* con) { g_clear_object(&con); });
879- std::string sender = csender;
880- std::shared_ptr<Application> app;
881- std::shared_ptr<Application::Instance> instance;
882-
883- std::tie(app, instance) = managerParams(vparams, reg);
884-
885- data->func(reg, app, instance, conn, sender, vparams);
886- },
887- focusdata,
888- [](gpointer user_data) {
889- auto data = reinterpret_cast<managerEventData*>(user_data);
890- delete data;
891- }); /* user data destroy */
892-}
893-
894-/** Set the manager for the registry. This includes tracking the pointer
895- as well as setting up the signals to call back into the manager. The
896- signals are only setup once per registry even if the manager is cleared
897- and changed again. They will just be no-op's in those cases.
898-*/
899-void Registry::Impl::setManager(std::shared_ptr<Registry::Manager> manager, std::shared_ptr<Registry> reg)
900-{
901- if (reg->impl->manager_)
902- {
903- throw std::runtime_error("Already have a manager and trying to set another");
904- }
905-
906- g_debug("Setting a new manager");
907- reg->impl->manager_ = manager;
908-
909- std::call_once(reg->impl->flag_managerSignals, [reg]() {
910- if (!reg->impl->thread.executeOnThread<bool>([reg]() {
911- reg->impl->handle_managerSignalFocus = managerSignalHelper(
912- reg, "UnityFocusRequest",
913- [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
914- const std::shared_ptr<Application::Instance>& instance,
915- const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
916- const std::shared_ptr<GVariant>& params) {
917- /* Nothing to do today */
918- reg->impl->manager_->focusRequest(app, instance, [](bool response) {
919- /* NOTE: We have no clue what thread this is gonna be
920- executed on, but since we're just talking to the GDBus
921- thread it isn't an issue today. Be careful in changing
922- this code. */
923- });
924- });
925- reg->impl->handle_managerSignalStarting = managerSignalHelper(
926- reg, "UnityStartingBroadcast",
927- [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
928- const std::shared_ptr<Application::Instance>& instance,
929- const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
930- const std::shared_ptr<GVariant>& params) {
931-
932- reg->impl->manager_->startingRequest(app, instance, [conn, sender, params](bool response) {
933- /* NOTE: We have no clue what thread this is gonna be
934- executed on, but since we're just talking to the GDBus
935- thread it isn't an issue today. Be careful in changing
936- this code. */
937- if (response)
938- {
939- g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
940- "/", /* path */
941- "com.canonical.UbuntuAppLaunch", /* interface */
942- "UnityStartingSignal", /* signal */
943- params.get(), /* params, the same */
944- nullptr); /* error */
945- }
946- });
947- });
948- reg->impl->handle_managerSignalResume = managerSignalHelper(
949- reg, "UnityResumeRequest",
950- [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
951- const std::shared_ptr<Application::Instance>& instance,
952- const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
953- const std::shared_ptr<GVariant>& params) {
954- reg->impl->manager_->resumeRequest(app, instance, [conn, sender, params](bool response) {
955- /* NOTE: We have no clue what thread this is gonna be
956- executed on, but since we're just talking to the GDBus
957- thread it isn't an issue today. Be careful in changing
958- this code. */
959- if (response)
960- {
961- g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
962- "/", /* path */
963- "com.canonical.UbuntuAppLaunch", /* interface */
964- "UnityResumeResponse", /* signal */
965- params.get(), /* params, the same */
966- nullptr); /* error */
967- }
968- });
969- });
970-
971- return true;
972- }))
973- {
974- g_warning("Unable to install manager signals");
975- }
976- });
977-}
978-
979-/** Clear the manager pointer */
980-void Registry::Impl::clearManager()
981-{
982- g_debug("Clearing the manager");
983- manager_.reset();
984-}
985-
986 /** App start watching, if we're registered for the signal we
987 can't wait on it. We are making this static right now because
988 we need it to go across the C and C++ APIs smoothly, and those
989@@ -510,301 +305,5 @@
990 return watchingAppStarting_;
991 }
992
993-/** Regex to parse the JOB environment variable from Upstart */
994-std::regex jobenv_regex{"^JOB=(application\\-(?:click|snap|legacy))$"};
995-/** Regex to parse the INSTANCE environment variable from Upstart */
996-std::regex instanceenv_regex{"^INSTANCE=(.*?)(?:\\-([0-9]*))?+$"};
997-
998-/** Core of most of the events that come from Upstart directly. Includes parsing of the
999- Upstart event environment and calling the appropriate signal with the right Application
1000- object and eventually its instance */
1001-void Registry::Impl::upstartEventEmitted(
1002- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& signal,
1003- std::shared_ptr<GVariant> params,
1004- const std::shared_ptr<Registry>& reg)
1005-{
1006- std::string jobname;
1007- std::string sappid;
1008- std::string instance;
1009-
1010- gchar* env = nullptr;
1011- GVariant* envs = g_variant_get_child_value(params.get(), 1);
1012- GVariantIter iter;
1013- g_variant_iter_init(&iter, envs);
1014-
1015- while (g_variant_iter_loop(&iter, "s", &env))
1016- {
1017- std::smatch match;
1018- std::string senv = env;
1019-
1020- if (std::regex_match(senv, match, jobenv_regex))
1021- {
1022- jobname = match[1].str();
1023- }
1024- else if (std::regex_match(senv, match, instanceenv_regex))
1025- {
1026- sappid = match[1].str();
1027- instance = match[2].str();
1028- }
1029- }
1030-
1031- g_variant_unref(envs);
1032-
1033- if (jobname.empty())
1034- {
1035- return;
1036- }
1037-
1038- g_debug("Upstart Event for job '%s' appid '%s' instance '%s'", jobname.c_str(), sappid.c_str(), instance.c_str());
1039-
1040- auto appid = AppID::find(reg, sappid);
1041- auto app = Application::create(appid, reg);
1042-
1043- // TODO: Figure otu creating instances
1044-
1045- signal(app, {});
1046-}
1047-
1048-/** Grab the signal object for application startup. If we're not already listing for
1049- those signals this sets up a listener for them. */
1050-core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::Impl::appStarted(
1051- const std::shared_ptr<Registry>& reg)
1052-{
1053- std::call_once(flag_appStarted, [reg]() {
1054- reg->impl->thread.executeOnThread<bool>([reg]() {
1055- upstartEventData* data = new upstartEventData{reg};
1056-
1057- reg->impl->handle_appStarted = g_dbus_connection_signal_subscribe(
1058- reg->impl->_dbus.get(), /* bus */
1059- nullptr, /* sender */
1060- DBUS_INTERFACE_UPSTART, /* interface */
1061- "EventEmitted", /* signal */
1062- DBUS_PATH_UPSTART, /* path */
1063- "started", /* arg0 */
1064- G_DBUS_SIGNAL_FLAGS_NONE,
1065- [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1066- gpointer user_data) -> void {
1067- auto data = reinterpret_cast<upstartEventData*>(user_data);
1068- auto reg = data->weakReg.lock();
1069- auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
1070- reg->impl->upstartEventEmitted(reg->impl->sig_appStarted, sparams, reg);
1071- }, /* callback */
1072- data, /* user data */
1073- [](gpointer user_data) {
1074- auto data = reinterpret_cast<upstartEventData*>(user_data);
1075- delete data;
1076- }); /* user data destroy */
1077-
1078- return true;
1079- });
1080- });
1081-
1082- return sig_appStarted;
1083-}
1084-
1085-/** Grab the signal object for application stopping. If we're not already listing for
1086- those signals this sets up a listener for them. */
1087-core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::Impl::appStopped(
1088- const std::shared_ptr<Registry>& reg)
1089-{
1090- std::call_once(flag_appStopped, [reg]() {
1091- reg->impl->thread.executeOnThread<bool>([reg]() {
1092- upstartEventData* data = new upstartEventData{reg};
1093-
1094- reg->impl->handle_appStopped = g_dbus_connection_signal_subscribe(
1095- reg->impl->_dbus.get(), /* bus */
1096- nullptr, /* sender */
1097- DBUS_INTERFACE_UPSTART, /* interface */
1098- "EventEmitted", /* signal */
1099- DBUS_PATH_UPSTART, /* path */
1100- "stopped", /* arg0 */
1101- G_DBUS_SIGNAL_FLAGS_NONE,
1102- [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1103- gpointer user_data) -> void {
1104- auto data = reinterpret_cast<upstartEventData*>(user_data);
1105- auto reg = data->weakReg.lock();
1106- auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
1107- reg->impl->upstartEventEmitted(reg->impl->sig_appStopped, sparams, reg);
1108- }, /* callback */
1109- data, /* user data */
1110- [](gpointer user_data) {
1111- auto data = reinterpret_cast<upstartEventData*>(user_data);
1112- delete data;
1113- }); /* user data destroy */
1114-
1115- return true;
1116- });
1117- });
1118-
1119- return sig_appStopped;
1120-}
1121-
1122-/** Grab the signal object for application failing. If we're not already listing for
1123- those signals this sets up a listener for them. */
1124-core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
1125- Registry::Impl::appFailed(const std::shared_ptr<Registry>& reg)
1126-{
1127- std::call_once(flag_appFailed, [reg]() {
1128- reg->impl->thread.executeOnThread<bool>([reg]() {
1129- upstartEventData* data = new upstartEventData{reg};
1130-
1131- reg->impl->handle_appFailed = g_dbus_connection_signal_subscribe(
1132- reg->impl->_dbus.get(), /* bus */
1133- nullptr, /* sender */
1134- "com.canonical.UbuntuAppLaunch", /* interface */
1135- "ApplicationFailed", /* signal */
1136- "/", /* path */
1137- nullptr, /* arg0 */
1138- G_DBUS_SIGNAL_FLAGS_NONE,
1139- [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1140- gpointer user_data) -> void {
1141- auto data = reinterpret_cast<upstartEventData*>(user_data);
1142- auto reg = data->weakReg.lock();
1143-
1144- const gchar* sappid = NULL;
1145- const gchar* typestr = NULL;
1146-
1147- Registry::FailureType type = Registry::FailureType::CRASH;
1148- g_variant_get(params, "(&s&s)", &sappid, &typestr);
1149-
1150- if (g_strcmp0("crash", typestr) == 0)
1151- {
1152- type = Registry::FailureType::CRASH;
1153- }
1154- else if (g_strcmp0("start-failure", typestr) == 0)
1155- {
1156- type = Registry::FailureType::START_FAILURE;
1157- }
1158- else
1159- {
1160- g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
1161- }
1162-
1163- auto appid = AppID::find(reg, sappid);
1164- auto app = Application::create(appid, reg);
1165-
1166- /* TODO: Instance issues */
1167-
1168- reg->impl->sig_appFailed(app, {}, type);
1169- }, /* callback */
1170- data, /* user data */
1171- [](gpointer user_data) {
1172- auto data = reinterpret_cast<upstartEventData*>(user_data);
1173- delete data;
1174- }); /* user data destroy */
1175-
1176- return true;
1177- });
1178- });
1179-
1180- return sig_appFailed;
1181-}
1182-
1183-/** Core handler for pause and resume events. Includes turning the GVariant
1184- pid list into a std::vector and getting the application object. */
1185-void Registry::Impl::pauseEventEmitted(
1186- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& signal,
1187- const std::shared_ptr<GVariant>& params,
1188- const std::shared_ptr<Registry>& reg)
1189-{
1190- std::vector<pid_t> pids;
1191- GVariant* vappid = g_variant_get_child_value(params.get(), 0);
1192- GVariant* vpids = g_variant_get_child_value(params.get(), 1);
1193- guint64 pid;
1194- GVariantIter thispid;
1195- g_variant_iter_init(&thispid, vpids);
1196-
1197- while (g_variant_iter_loop(&thispid, "t", &pid))
1198- {
1199- pids.emplace_back(pid);
1200- }
1201-
1202- auto cappid = g_variant_get_string(vappid, NULL);
1203- auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
1204- auto app = Application::create(appid, reg);
1205-
1206- /* TODO: Instance */
1207- signal(app, {}, pids);
1208-
1209- g_variant_unref(vappid);
1210- g_variant_unref(vpids);
1211-
1212- return;
1213-}
1214-
1215-/** Grab the signal object for application paused. If we're not already listing for
1216- those signals this sets up a listener for them. */
1217-core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1218- Registry::Impl::appPaused(const std::shared_ptr<Registry>& reg)
1219-{
1220- std::call_once(flag_appPaused, [&]() {
1221- reg->impl->thread.executeOnThread<bool>([reg]() {
1222- upstartEventData* data = new upstartEventData{reg};
1223-
1224- reg->impl->handle_appPaused = g_dbus_connection_signal_subscribe(
1225- reg->impl->_dbus.get(), /* bus */
1226- nullptr, /* sender */
1227- "com.canonical.UbuntuAppLaunch", /* interface */
1228- "ApplicationPaused", /* signal */
1229- "/", /* path */
1230- nullptr, /* arg0 */
1231- G_DBUS_SIGNAL_FLAGS_NONE,
1232- [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1233- gpointer user_data) -> void {
1234- auto data = reinterpret_cast<upstartEventData*>(user_data);
1235- auto reg = data->weakReg.lock();
1236- auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
1237- reg->impl->pauseEventEmitted(reg->impl->sig_appPaused, sparams, reg);
1238- }, /* callback */
1239- data, /* user data */
1240- [](gpointer user_data) {
1241- auto data = reinterpret_cast<upstartEventData*>(user_data);
1242- delete data;
1243- }); /* user data destroy */
1244-
1245- return true;
1246- });
1247- });
1248-
1249- return sig_appPaused;
1250-}
1251-
1252-/** Grab the signal object for application resumed. If we're not already listing for
1253- those signals this sets up a listener for them. */
1254-core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1255- Registry::Impl::appResumed(const std::shared_ptr<Registry>& reg)
1256-{
1257- std::call_once(flag_appResumed, [&]() {
1258- reg->impl->thread.executeOnThread<bool>([reg]() {
1259- upstartEventData* data = new upstartEventData{reg};
1260-
1261- reg->impl->handle_appResumed = g_dbus_connection_signal_subscribe(
1262- reg->impl->_dbus.get(), /* bus */
1263- nullptr, /* sender */
1264- "com.canonical.UbuntuAppLaunch", /* interface */
1265- "ApplicationResumed", /* signal */
1266- "/", /* path */
1267- nullptr, /* arg0 */
1268- G_DBUS_SIGNAL_FLAGS_NONE,
1269- [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1270- gpointer user_data) -> void {
1271- auto data = reinterpret_cast<upstartEventData*>(user_data);
1272- auto reg = data->weakReg.lock();
1273- auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
1274- reg->impl->pauseEventEmitted(reg->impl->sig_appResumed, sparams, reg);
1275- }, /* callback */
1276- data, /* user data */
1277- [](gpointer user_data) {
1278- auto data = reinterpret_cast<upstartEventData*>(user_data);
1279- delete data;
1280- }); /* user data destroy */
1281-
1282- return true;
1283- });
1284- });
1285-
1286- return sig_appResumed;
1287-}
1288-
1289 } // namespace app_launch
1290 } // namespace ubuntu
1291
1292=== modified file 'libubuntu-app-launch/registry-impl.h'
1293--- libubuntu-app-launch/registry-impl.h 2016-11-10 21:58:53 +0000
1294+++ libubuntu-app-launch/registry-impl.h 2016-11-10 21:58:53 +0000
1295@@ -72,23 +72,11 @@
1296
1297 std::shared_ptr<IconFinder> getIconFinder(std::string basePath);
1298
1299- void zgSendEvent(AppID appid, const std::string& eventtype);
1300+ virtual void zgSendEvent(AppID appid, const std::string& eventtype);
1301
1302 static std::string printJson(std::shared_ptr<JsonObject> jsonobj);
1303 static std::string printJson(std::shared_ptr<JsonNode> jsonnode);
1304
1305- /* Signals to discover what is happening to apps */
1306- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStarted(
1307- const std::shared_ptr<Registry>& reg);
1308- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStopped(
1309- const std::shared_ptr<Registry>& reg);
1310- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType>& appFailed(
1311- const std::shared_ptr<Registry>& reg);
1312- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& appPaused(
1313- const std::shared_ptr<Registry>& reg);
1314- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& appResumed(
1315- const std::shared_ptr<Registry>& reg);
1316-
1317 /* Signal Hints */
1318 /* NOTE: Static because we don't have registry instances in the C
1319 code right now. We want these to not be static in the future */
1320@@ -96,65 +84,11 @@
1321 static bool isWatchingAppStarting();
1322
1323 private:
1324- Registry* _registry; /**< The Registry that we're spawned from */
1325- std::shared_ptr<Registry::Manager> manager_; /**< Application manager if registered */
1326+ Registry* _registry; /**< The Registry that we're spawned from */
1327
1328 std::shared_ptr<ClickDB> _clickDB; /**< Shared instance of the Click Database */
1329 std::shared_ptr<ClickUser> _clickUser; /**< Click database filtered by the current user */
1330
1331- /** Signal object for applications started */
1332- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> sig_appStarted;
1333- /** Signal object for applications stopped */
1334- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> sig_appStopped;
1335- /** Signal object for applications failed */
1336- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> sig_appFailed;
1337- /** Signal object for applications paused */
1338- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>
1339- sig_appPaused;
1340- /** Signal object for applications resumed */
1341- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>
1342- sig_appResumed;
1343-
1344- guint handle_appStarted{0}; /**< GDBus signal watcher handle for app started signal */
1345- guint handle_appStopped{0}; /**< GDBus signal watcher handle for app stopped signal */
1346- guint handle_appFailed{0}; /**< GDBus signal watcher handle for app failed signal */
1347- guint handle_appPaused{0}; /**< GDBus signal watcher handle for app paused signal */
1348- guint handle_appResumed{0}; /**< GDBus signal watcher handle for app resumed signal */
1349- guint handle_managerSignalFocus{0}; /**< GDBus signal watcher handle for app focused signal */
1350- guint handle_managerSignalResume{0}; /**< GDBus signal watcher handle for app resumed signal */
1351- guint handle_managerSignalStarting{0}; /**< GDBus signal watcher handle for app starting signal */
1352-
1353- std::once_flag flag_appStarted; /**< Variable to track to see if signal handlers are installed for application
1354- started */
1355- std::once_flag flag_appStopped; /**< Variable to track to see if signal handlers are installed for application
1356- stopped */
1357- std::once_flag
1358- flag_appFailed; /**< Variable to track to see if signal handlers are installed for application failed */
1359- std::once_flag
1360- flag_appPaused; /**< Variable to track to see if signal handlers are installed for application paused */
1361- std::once_flag flag_appResumed; /**< Variable to track to see if signal handlers are installed for application
1362- resumed */
1363- std::once_flag flag_managerSignals; /**< Variable to track to see if signal handlers are installed for the manager
1364- signals of focused, resumed and starting */
1365-
1366- void upstartEventEmitted(core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& signal,
1367- std::shared_ptr<GVariant> params,
1368- const std::shared_ptr<Registry>& reg);
1369- void pauseEventEmitted(
1370- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& signal,
1371- const std::shared_ptr<GVariant>& params,
1372- const std::shared_ptr<Registry>& reg);
1373- static std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> managerParams(
1374- const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg);
1375- static guint managerSignalHelper(const std::shared_ptr<Registry>& reg,
1376- const std::string& signalname,
1377- std::function<void(const std::shared_ptr<Registry>& reg,
1378- const std::shared_ptr<Application>& app,
1379- const std::shared_ptr<Application::Instance>& instance,
1380- const std::shared_ptr<GDBusConnection>&,
1381- const std::string&,
1382- const std::shared_ptr<GVariant>&)> responsefunc);
1383-
1384 void initClick();
1385
1386 /** Shared instance of the Zeitgeist Log */
1387
1388=== modified file 'libubuntu-app-launch/registry.cpp'
1389--- libubuntu-app-launch/registry.cpp 2016-11-10 21:58:53 +0000
1390+++ libubuntu-app-launch/registry.cpp 2016-11-10 21:58:53 +0000
1391@@ -80,14 +80,29 @@
1392 return list;
1393 }
1394
1395+/* Quick little helper to bundle up standard code */
1396+inline void setJobs(const std::shared_ptr<Registry>& registry)
1397+{
1398+ if (!registry->impl->jobs)
1399+ {
1400+ registry->impl->jobs = jobs::manager::Base::determineFactory(registry);
1401+ }
1402+}
1403+
1404 void Registry::setManager(std::shared_ptr<Manager> manager, std::shared_ptr<Registry> registry)
1405 {
1406- Registry::Impl::setManager(manager, registry);
1407+ setJobs(registry);
1408+ registry->impl->jobs->setManager(manager);
1409 }
1410
1411 void Registry::clearManager()
1412 {
1413- impl->clearManager();
1414+ if (!impl->jobs)
1415+ {
1416+ return;
1417+ }
1418+
1419+ impl->jobs->clearManager();
1420 }
1421
1422 std::shared_ptr<Registry> defaultRegistry;
1423@@ -109,31 +124,36 @@
1424 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::appStarted(
1425 const std::shared_ptr<Registry>& reg)
1426 {
1427- return reg->impl->appStarted(reg);
1428+ setJobs(reg);
1429+ return reg->impl->jobs->appStarted();
1430 }
1431
1432 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::appStopped(
1433 const std::shared_ptr<Registry>& reg)
1434 {
1435- return reg->impl->appStopped(reg);
1436+ setJobs(reg);
1437+ return reg->impl->jobs->appStopped();
1438 }
1439
1440 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>&
1441 Registry::appFailed(const std::shared_ptr<Registry>& reg)
1442 {
1443- return reg->impl->appFailed(reg);
1444+ setJobs(reg);
1445+ return reg->impl->jobs->appFailed();
1446 }
1447
1448 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1449 Registry::appPaused(const std::shared_ptr<Registry>& reg)
1450 {
1451- return reg->impl->appPaused(reg);
1452+ setJobs(reg);
1453+ return reg->impl->jobs->appPaused();
1454 }
1455
1456 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>&
1457 Registry::appResumed(const std::shared_ptr<Registry>& reg)
1458 {
1459- return reg->impl->appResumed(reg);
1460+ setJobs(reg);
1461+ return reg->impl->jobs->appResumed();
1462 }
1463
1464 } // namespace app_launch
1465
1466=== modified file 'tests/CMakeLists.txt'
1467--- tests/CMakeLists.txt 2016-11-10 21:58:53 +0000
1468+++ tests/CMakeLists.txt 2016-11-10 21:58:53 +0000
1469@@ -12,23 +12,21 @@
1470
1471 include_directories(${GTEST_INCLUDE_DIR})
1472
1473-add_library (gtest STATIC
1474- ${GTEST_SOURCE_DIR}/gtest-all.cc
1475- ${GTEST_SOURCE_DIR}/gtest_main.cc)
1476+add_subdirectory("/usr/src/gmock" gmock)
1477
1478 # Helper test
1479
1480 add_executable (helper-test helper-test.cc)
1481 add_definitions ( -DCMAKE_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" )
1482 add_definitions ( -DCMAKE_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}" )
1483-target_link_libraries (helper-test helpers gtest ${GTEST_LIBS} ${DBUSTEST_LIBRARIES})
1484+target_link_libraries (helper-test helpers gtest_main ${GTEST_LIBS} ${DBUSTEST_LIBRARIES})
1485
1486 add_test (helper-test helper-test)
1487
1488 # Helper test
1489
1490 add_executable (helper-handshake-test helper-handshake-test.cc)
1491-target_link_libraries (helper-handshake-test helpers gtest ${GTEST_LIBS})
1492+target_link_libraries (helper-handshake-test helpers gtest_main ${GTEST_LIBS})
1493
1494 add_test (helper-handshake-test helper-handshake-test)
1495
1496@@ -45,13 +43,13 @@
1497 add_executable (libual-test
1498 libual-test.cc
1499 mir-mock.cpp)
1500-target_link_libraries (libual-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
1501+target_link_libraries (libual-test gtest_main ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
1502
1503 add_executable (libual-cpp-test
1504 libual-cpp-test.cc
1505 ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/glib-thread.cpp
1506 mir-mock.cpp)
1507-target_link_libraries (libual-cpp-test gtest ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
1508+target_link_libraries (libual-cpp-test gtest_main ${GTEST_LIBS} ${LIBUPSTART_LIBRARIES} ${DBUSTEST_LIBRARIES} ubuntu-launcher)
1509
1510 add_executable (data-spew
1511 data-spew.c)
1512@@ -63,13 +61,21 @@
1513 add_test (NAME libual-test COMMAND libual-test)
1514 add_test (NAME libual-cpp-test COMMAND libual-cpp-test)
1515
1516+# Jobs Base Test
1517+
1518+add_executable (jobs-base-test
1519+ jobs-base-test.cpp)
1520+target_link_libraries (jobs-base-test gmock_main ${GTEST_LIBS} launcher-static)
1521+
1522+add_test(NAME jobs-base-test COMMAND jobs-base-test)
1523+
1524 # Snapd Info Test
1525
1526 if(CURL_FOUND)
1527 add_definitions ( -DSNAPD_TEST_SOCKET="/tmp/snapd-test-socket" )
1528 add_executable (snapd-info-test
1529 snapd-info-test.cpp)
1530-target_link_libraries (snapd-info-test gtest ${GTEST_LIBS} launcher-static)
1531+target_link_libraries (snapd-info-test gtest_main ${GTEST_LIBS} launcher-static)
1532 add_test (NAME snapd-info-test COMMAND snapd-info-test)
1533 endif()
1534
1535@@ -77,7 +83,7 @@
1536
1537 add_executable (list-apps
1538 list-apps.cpp)
1539-target_link_libraries (list-apps gtest ${GTEST_LIBS} launcher-static)
1540+target_link_libraries (list-apps gtest_main ${GTEST_LIBS} launcher-static)
1541 add_test (NAME list-apps COMMAND ${CMAKE_CURRENT_BINARY_DIR}/list-apps)
1542
1543 # Application Info Desktop
1544@@ -85,7 +91,7 @@
1545 add_executable (application-info-desktop-test
1546 application-info-desktop.cpp
1547 )
1548-target_link_libraries (application-info-desktop-test gtest ${GTEST_LIBS} launcher-static)
1549+target_link_libraries (application-info-desktop-test gtest_main ${GTEST_LIBS} launcher-static)
1550
1551 add_test (NAME application-info-desktop-test COMMAND application-info-desktop-test)
1552
1553@@ -97,7 +103,7 @@
1554
1555 #sources
1556 ${CMAKE_SOURCE_DIR}/libubuntu-app-launch/application-icon-finder.cpp)
1557-target_link_libraries (application-icon-finder-test gtest ${GTEST_LIBS} ubuntu-launcher)
1558+target_link_libraries (application-icon-finder-test gtest_main ${GTEST_LIBS} ubuntu-launcher)
1559
1560 add_test (NAME application-icon-finder-test COMMAND application-icon-finder-test)
1561
1562@@ -109,7 +115,7 @@
1563
1564 add_executable (failure-test
1565 failure-test.cc)
1566-target_link_libraries (failure-test gtest ${GTEST_LIBS} ubuntu-launcher)
1567+target_link_libraries (failure-test gtest_main ${GTEST_LIBS} ubuntu-launcher)
1568 add_test (failure-test failure-test)
1569
1570 # ZG Test
1571@@ -118,7 +124,7 @@
1572
1573 add_executable (zg-test
1574 zg-test.cc)
1575-target_link_libraries (zg-test gtest ${GTEST_LIBS} ${DBUSTEST_LIBRARIES} ${GIO2_LIBRARIES})
1576+target_link_libraries (zg-test gtest_main ${GTEST_LIBS} ${DBUSTEST_LIBRARIES} ${GIO2_LIBRARIES})
1577 add_test (zg-test zg-test)
1578
1579 # Exec Line Exec Test
1580@@ -130,7 +136,7 @@
1581
1582 add_executable (exec-util-test
1583 exec-util-test.cc)
1584-target_link_libraries (exec-util-test gtest ubuntu-launcher ${GTEST_LIBS} ${DBUSTEST_LIBRARIES} ${GIO2_LIBRARIES})
1585+target_link_libraries (exec-util-test gtest_main ubuntu-launcher ${GTEST_LIBS} ${DBUSTEST_LIBRARIES} ${GIO2_LIBRARIES})
1586 add_test (exec-util-test exec-util-test)
1587
1588 # CGroup Reap Test
1589@@ -139,7 +145,7 @@
1590
1591 add_executable (cgroup-reap-test
1592 cgroup-reap-test.cc)
1593-target_link_libraries (cgroup-reap-test gtest ${GTEST_LIBS} ${DBUSTEST_LIBRARIES} ${GIO2_LIBRARIES})
1594+target_link_libraries (cgroup-reap-test gtest_main ${GTEST_LIBS} ${DBUSTEST_LIBRARIES} ${GIO2_LIBRARIES})
1595 add_test (cgroup-reap-test cgroup-reap-test)
1596
1597 # Desktop Hook Test
1598@@ -162,6 +168,7 @@
1599 libual-cpp-test.cc
1600 list-apps.cpp
1601 eventually-fixture.h
1602+ jobs-base-test.cpp
1603 snapd-info-test.cpp
1604 snapd-mock.h
1605 zg-test.cc
1606
1607=== added file 'tests/jobs-base-test.cpp'
1608--- tests/jobs-base-test.cpp 1970-01-01 00:00:00 +0000
1609+++ tests/jobs-base-test.cpp 2016-11-10 21:58:53 +0000
1610@@ -0,0 +1,364 @@
1611+/*
1612+ * Copyright © 2016 Canonical Ltd.
1613+ *
1614+ * This program is free software: you can redistribute it and/or modify it
1615+ * under the terms of the GNU General Public License version 3, as published
1616+ * by the Free Software Foundation.
1617+ *
1618+ * This program is distributed in the hope that it will be useful, but
1619+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1620+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1621+ * PURPOSE. See the GNU General Public License for more details.
1622+ *
1623+ * You should have received a copy of the GNU General Public License along
1624+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1625+ *
1626+ * Authors:
1627+ * Ted Gould <ted.gould@canonical.com>
1628+ */
1629+
1630+#include "jobs-base.h"
1631+#include "appid.h"
1632+#include "registry-impl.h"
1633+#include "registry.h"
1634+
1635+#include "eventually-fixture.h"
1636+#include <gmock/gmock.h>
1637+#include <gtest/gtest.h>
1638+
1639+class RegistryImplMock : public ubuntu::app_launch::Registry::Impl
1640+{
1641+public:
1642+ RegistryImplMock(ubuntu::app_launch::Registry* reg)
1643+ : ubuntu::app_launch::Registry::Impl(reg)
1644+ {
1645+ }
1646+
1647+ MOCK_METHOD2(zgSendEvent, void(ubuntu::app_launch::AppID, const std::string& eventtype));
1648+};
1649+
1650+class RegistryMock : public ubuntu::app_launch::Registry
1651+{
1652+public:
1653+ RegistryMock()
1654+ {
1655+ impl = std::unique_ptr<RegistryImplMock>(new RegistryImplMock(this));
1656+ }
1657+};
1658+
1659+class instanceMock : public ubuntu::app_launch::jobs::instance::Base
1660+{
1661+public:
1662+ instanceMock(const ubuntu::app_launch::AppID& appId,
1663+ const std::string& job,
1664+ const std::string& instance,
1665+ const std::vector<ubuntu::app_launch::Application::URL>& urls,
1666+ const std::shared_ptr<ubuntu::app_launch::Registry>& registry)
1667+ : ubuntu::app_launch::jobs::instance::Base(appId, job, instance, urls, registry)
1668+ {
1669+ }
1670+
1671+ MOCK_METHOD0(primaryPid, pid_t());
1672+ MOCK_METHOD0(logPath, std::string());
1673+ MOCK_METHOD0(pids, std::vector<pid_t>());
1674+
1675+ MOCK_METHOD0(stop, void());
1676+};
1677+
1678+class SpewMaster
1679+{
1680+public:
1681+ SpewMaster()
1682+ : thread(
1683+ [this]() {
1684+ gint spewstdout = 0;
1685+ std::array<const gchar*, 2> spewline{SPEW_UTILITY, nullptr};
1686+ ASSERT_TRUE(g_spawn_async_with_pipes(NULL, /* directory */
1687+ (char**)spewline.data(), /* command line */
1688+ NULL, /* environment */
1689+ G_SPAWN_DEFAULT, /* flags */
1690+ NULL, /* child setup */
1691+ NULL, /* child setup */
1692+ &pid_, /* pid */
1693+ NULL, /* stdin */
1694+ &spewstdout, /* stdout */
1695+ NULL, /* stderr */
1696+ NULL)); /* error */
1697+
1698+ spewoutchan = g_io_channel_unix_new(spewstdout);
1699+ g_io_channel_set_flags(spewoutchan, G_IO_FLAG_NONBLOCK, NULL);
1700+
1701+ iosource = g_io_create_watch(spewoutchan, G_IO_IN);
1702+ g_source_set_callback(iosource, (GSourceFunc)datain, this, nullptr);
1703+ g_source_attach(iosource, g_main_context_get_thread_default());
1704+
1705+ /* Setup our OOM adjust file */
1706+ gchar* procdir = g_strdup_printf(CMAKE_BINARY_DIR "/jobs-base-proc/%d", pid_);
1707+ ASSERT_EQ(0, g_mkdir_with_parents(procdir, 0700));
1708+ oomadjfile = g_strdup_printf("%s/oom_score_adj", procdir);
1709+ g_free(procdir);
1710+ ASSERT_TRUE(g_file_set_contents(oomadjfile, "0", -1, NULL));
1711+ },
1712+ [this]() {
1713+ /* Clean up */
1714+ gchar* killstr = g_strdup_printf("kill -9 %d", pid_);
1715+ ASSERT_TRUE(g_spawn_command_line_sync(killstr, NULL, NULL, NULL, NULL));
1716+ g_free(killstr);
1717+
1718+ g_source_destroy(iosource);
1719+ g_io_channel_unref(spewoutchan);
1720+ g_clear_pointer(&oomadjfile, g_free);
1721+ })
1722+ {
1723+ datacnt_ = 0;
1724+ }
1725+
1726+ ~SpewMaster()
1727+ {
1728+ }
1729+
1730+ std::string oomScore()
1731+ {
1732+ gchar* oomvalue = nullptr;
1733+ g_file_get_contents(oomadjfile, &oomvalue, nullptr, nullptr);
1734+ if (oomvalue != nullptr)
1735+ {
1736+ return std::string(oomvalue);
1737+ }
1738+ else
1739+ {
1740+ return {};
1741+ }
1742+ }
1743+
1744+ GPid pid()
1745+ {
1746+ return pid_;
1747+ }
1748+
1749+ gsize dataCnt()
1750+ {
1751+ g_debug("Data Count for %d: %d", pid_, int(datacnt_));
1752+ return datacnt_;
1753+ }
1754+
1755+ void reset()
1756+ {
1757+ bool endofqueue = thread.executeOnThread<bool>([this]() {
1758+ while (G_IO_STATUS_AGAIN == g_io_channel_flush(spewoutchan, nullptr))
1759+ ;
1760+ return true; /* the main loop has processed */
1761+ });
1762+ g_debug("Reset %d", pid_);
1763+ if (endofqueue)
1764+ datacnt_ = 0;
1765+ else
1766+ g_warning("Unable to clear mainloop on reset");
1767+ }
1768+
1769+ std::atomic<gsize> datacnt_;
1770+
1771+private:
1772+ GPid pid_ = 0;
1773+ gchar* oomadjfile = nullptr;
1774+ GIOChannel* spewoutchan = nullptr;
1775+ GSource* iosource = nullptr;
1776+ GLib::ContextThread thread;
1777+
1778+ static gboolean datain(GIOChannel* source, GIOCondition cond, gpointer data)
1779+ {
1780+ auto spew = static_cast<SpewMaster*>(data);
1781+ gchar* str = NULL;
1782+ gsize len = 0;
1783+ GError* error = NULL;
1784+
1785+ g_io_channel_read_line(source, &str, &len, NULL, &error);
1786+ g_free(str);
1787+
1788+ if (error != NULL)
1789+ {
1790+ g_warning("Unable to read from channel: %s", error->message);
1791+ g_error_free(error);
1792+ }
1793+
1794+ spew->datacnt_ += len;
1795+
1796+ return TRUE;
1797+ }
1798+};
1799+
1800+class JobBaseTest : public EventuallyFixture
1801+{
1802+protected:
1803+ std::shared_ptr<RegistryMock> registry;
1804+
1805+ virtual void SetUp()
1806+ {
1807+ registry = std::make_shared<RegistryMock>();
1808+ }
1809+
1810+ virtual void TearDown()
1811+ {
1812+ registry.reset();
1813+ }
1814+
1815+ ubuntu::app_launch::AppID simpleAppID()
1816+ {
1817+ return {ubuntu::app_launch::AppID::Package::from_raw("package"),
1818+ ubuntu::app_launch::AppID::AppName::from_raw("appname"),
1819+ ubuntu::app_launch::AppID::Version::from_raw("version")};
1820+ }
1821+
1822+ std::shared_ptr<instanceMock> simpleInstance()
1823+ {
1824+ return std::make_shared<instanceMock>(simpleAppID(), "application-job", "1234567890",
1825+ std::vector<ubuntu::app_launch::Application::URL>{}, registry);
1826+ }
1827+};
1828+
1829+TEST_F(JobBaseTest, InitTest)
1830+{
1831+ auto instance = simpleInstance();
1832+
1833+ instance.reset();
1834+}
1835+
1836+TEST_F(JobBaseTest, isRunning)
1837+{
1838+ auto instance = simpleInstance();
1839+
1840+ EXPECT_CALL(*instance, primaryPid()).WillOnce(testing::Return(0));
1841+
1842+ EXPECT_FALSE(instance->isRunning());
1843+
1844+ EXPECT_CALL(*instance, primaryPid()).WillOnce(testing::Return(100));
1845+
1846+ EXPECT_TRUE(instance->isRunning());
1847+}
1848+
1849+TEST_F(JobBaseTest, pauseResume)
1850+{
1851+ g_setenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH", CMAKE_BINARY_DIR "/jobs-base-proc", TRUE);
1852+
1853+ /* Setup some spew */
1854+ SpewMaster spew;
1855+ std::vector<pid_t> pids{spew.pid()};
1856+
1857+ /* Build our instance */
1858+ auto instance = simpleInstance();
1859+ EXPECT_CALL(*instance, pids()).WillRepeatedly(testing::Return(pids));
1860+
1861+ /* Setup registry */
1862+ EXPECT_CALL(dynamic_cast<RegistryImplMock&>(*registry->impl), zgSendEvent(simpleAppID(), ZEITGEIST_ZG_LEAVE_EVENT))
1863+ .WillOnce(testing::Return());
1864+
1865+ /* Make sure it is running */
1866+ EXPECT_EVENTUALLY_NE(0, spew.datacnt_);
1867+
1868+ /*** Do Pause ***/
1869+ instance->pause();
1870+
1871+ spew.reset();
1872+ pause(100); // give spew a chance to send data if it is running
1873+
1874+ EXPECT_EQ(0, spew.dataCnt());
1875+
1876+ EXPECT_EQ(std::to_string(int(ubuntu::app_launch::oom::paused())), spew.oomScore());
1877+
1878+ /* Setup for Resume */
1879+ EXPECT_CALL(dynamic_cast<RegistryImplMock&>(*registry->impl), zgSendEvent(simpleAppID(), ZEITGEIST_ZG_ACCESS_EVENT))
1880+ .WillOnce(testing::Return());
1881+
1882+ spew.reset();
1883+ EXPECT_EQ(0, spew.dataCnt());
1884+
1885+ /*** Do Resume ***/
1886+ instance->resume();
1887+
1888+ EXPECT_EVENTUALLY_NE(0, spew.datacnt_);
1889+
1890+ EXPECT_EQ(std::to_string(int(ubuntu::app_launch::oom::focused())), spew.oomScore());
1891+}
1892+
1893+TEST_F(JobBaseTest, pauseResumeNone)
1894+{
1895+ std::vector<pid_t> pids{};
1896+
1897+ /* Build our instance */
1898+ auto instance = simpleInstance();
1899+ EXPECT_CALL(*instance, pids()).WillRepeatedly(testing::Return(pids));
1900+
1901+ /* Setup registry */
1902+ EXPECT_CALL(dynamic_cast<RegistryImplMock&>(*registry->impl), zgSendEvent(simpleAppID(), ZEITGEIST_ZG_LEAVE_EVENT))
1903+ .WillOnce(testing::Return());
1904+
1905+ /*** Do Pause ***/
1906+ instance->pause();
1907+
1908+ /* Setup for Resume */
1909+ EXPECT_CALL(dynamic_cast<RegistryImplMock&>(*registry->impl), zgSendEvent(simpleAppID(), ZEITGEIST_ZG_ACCESS_EVENT))
1910+ .WillOnce(testing::Return());
1911+
1912+ /*** Do Resume ***/
1913+ instance->resume();
1914+}
1915+
1916+TEST_F(JobBaseTest, pauseResumeMany)
1917+{
1918+ g_setenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH", CMAKE_BINARY_DIR "/jobs-base-proc", TRUE);
1919+
1920+ /* Setup some A TON OF spew */
1921+ std::array<SpewMaster, 50> spews;
1922+ std::vector<pid_t> pids(50);
1923+ std::transform(spews.begin(), spews.end(), pids.begin(), [](SpewMaster& spew) { return spew.pid(); });
1924+
1925+ /* Build our instance */
1926+ auto instance = simpleInstance();
1927+ EXPECT_CALL(*instance, pids()).WillRepeatedly(testing::Return(pids));
1928+
1929+ /* Setup registry */
1930+ EXPECT_CALL(dynamic_cast<RegistryImplMock&>(*registry->impl), zgSendEvent(simpleAppID(), ZEITGEIST_ZG_LEAVE_EVENT))
1931+ .WillOnce(testing::Return());
1932+
1933+ /* Make sure it is running */
1934+ for (auto& spew : spews)
1935+ {
1936+ EXPECT_EVENTUALLY_NE(0, spew.datacnt_);
1937+ }
1938+
1939+ /*** Do Pause ***/
1940+ instance->pause();
1941+
1942+ for (auto& spew : spews)
1943+ {
1944+ spew.reset();
1945+ }
1946+ pause(100); // give spew a chance to send data if it is running
1947+
1948+ for (auto& spew : spews)
1949+ {
1950+ EXPECT_EQ(0, spew.dataCnt());
1951+
1952+ EXPECT_EQ(std::to_string(int(ubuntu::app_launch::oom::paused())), spew.oomScore());
1953+ }
1954+
1955+ /* Setup for Resume */
1956+ EXPECT_CALL(dynamic_cast<RegistryImplMock&>(*registry->impl), zgSendEvent(simpleAppID(), ZEITGEIST_ZG_ACCESS_EVENT))
1957+ .WillOnce(testing::Return());
1958+
1959+ for (auto& spew : spews)
1960+ {
1961+ spew.reset();
1962+ EXPECT_EQ(0, spew.dataCnt());
1963+ }
1964+
1965+ /*** Do Resume ***/
1966+ instance->resume();
1967+
1968+ for (auto& spew : spews)
1969+ {
1970+ EXPECT_EVENTUALLY_NE(0, spew.datacnt_);
1971+
1972+ EXPECT_EQ(std::to_string(int(ubuntu::app_launch::oom::focused())), spew.oomScore());
1973+ }
1974+}

Subscribers

People subscribed via source and target branches