2
* Copyright 2013-2016 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Ted Gould <ted.gould@canonical.com>
18
* Xavi Garcia <xavi.garcia.mena@gmail.com>
24
#include <QCoreApplication>
27
#include <gtest/gtest.h>
29
#include <ubuntu-app-launch/registry.h>
30
#include <libdbustest/dbus-test.h>
33
#include <helper/backup-helper.h>
38
#include <ubuntu-app-launch.h>
42
class LibUAL : public ::testing::Test
45
DbusTestService* service = NULL;
46
DbusTestDbusMock* mock = NULL;
47
DbusTestDbusMock* cgmock = NULL;
48
GDBusConnection* bus = NULL;
49
std::string last_focus_appid;
50
std::string last_resume_appid;
51
guint resume_timeout = 0;
52
std::shared_ptr<ubuntu::app_launch::Registry> registry;
55
static void focus_cb(const gchar* appid, gpointer user_data)
57
g_debug("Focus Callback: %s", appid);
58
LibUAL* _this = static_cast<LibUAL*>(user_data);
59
_this->last_focus_appid = appid;
62
static void resume_cb(const gchar* appid, gpointer user_data)
64
g_debug("Resume Callback: %s", appid);
65
LibUAL* _this = static_cast<LibUAL*>(user_data);
66
_this->last_resume_appid = appid;
68
if (_this->resume_timeout > 0)
70
_this->pause(_this->resume_timeout);
75
/* Useful debugging stuff, but not on by default. You really want to
76
not get all this noise typically */
77
void debugConnection()
84
DbusTestBustle* bustle = dbus_test_bustle_new("test.bustle");
85
dbus_test_service_add_task(service, DBUS_TEST_TASK(bustle));
86
g_object_unref(bustle);
88
DbusTestProcess* monitor = dbus_test_process_new("dbus-monitor");
89
dbus_test_service_add_task(service, DBUS_TEST_TASK(monitor));
90
g_object_unref(monitor);
95
/* Click DB test mode */
96
g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
97
g_setenv("TEST_CLICK_USER", "test-user", TRUE);
99
gchar* linkfarmpath = g_build_filename(CMAKE_SOURCE_DIR, "link-farm", NULL);
100
g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", linkfarmpath, TRUE);
101
g_free(linkfarmpath);
103
g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
104
g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE);
105
g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE);
107
service = dbus_test_service_new(NULL);
111
mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");
113
DbusTestDbusMockObject* obj =
114
dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
116
dbus_test_dbus_mock_object_add_method(mock, obj, "EmitEvent", G_VARIANT_TYPE("(sasb)"), NULL, "", NULL);
118
dbus_test_dbus_mock_object_add_method(mock, obj, "GetJobByName", G_VARIANT_TYPE("s"), G_VARIANT_TYPE("o"),
119
"if args[0] == 'application-click':\n"
120
" ret = dbus.ObjectPath('/com/test/application_click')\n"
121
"elif args[0] == 'application-legacy':\n"
122
" ret = dbus.ObjectPath('/com/test/application_legacy')\n"
123
"elif args[0] == 'untrusted-helper':\n"
124
" ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",
127
dbus_test_dbus_mock_object_add_method(mock, obj, "SetEnv", G_VARIANT_TYPE("(assb)"), NULL, "", NULL);
130
DbusTestDbusMockObject* jobobj =
131
dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
133
dbus_test_dbus_mock_object_add_method(
134
mock, jobobj, "Start", G_VARIANT_TYPE("(asb)"), NULL,
135
"if args[0][0] == 'APP_ID=com.test.good_application_1.2.3':"
136
" raise dbus.exceptions.DBusException('Foo running', name='com.ubuntu.Upstart0_6.Error.AlreadyStarted')",
139
dbus_test_dbus_mock_object_add_method(mock, jobobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
141
dbus_test_dbus_mock_object_add_method(mock, jobobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"),
142
"ret = [ dbus.ObjectPath('/com/test/app_instance') ]", NULL);
144
DbusTestDbusMockObject* instobj =
145
dbus_test_dbus_mock_get_object(mock, "/com/test/app_instance", "com.ubuntu.Upstart0_6.Instance", NULL);
146
dbus_test_dbus_mock_object_add_property(mock, instobj, "name", G_VARIANT_TYPE_STRING,
147
g_variant_new_string("com.test.good_application_1.2.3"), NULL);
148
gchar* process_var = g_strdup_printf("[('main', %d)]", getpid());
149
dbus_test_dbus_mock_object_add_property(mock, instobj, "processes", G_VARIANT_TYPE("a(si)"),
150
g_variant_new_parsed(process_var), NULL);
154
DbusTestDbusMockObject* ljobobj =
155
dbus_test_dbus_mock_get_object(mock, "/com/test/application_legacy", "com.ubuntu.Upstart0_6.Job", NULL);
157
dbus_test_dbus_mock_object_add_method(mock, ljobobj, "Start", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
159
dbus_test_dbus_mock_object_add_method(mock, ljobobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
161
dbus_test_dbus_mock_object_add_method(mock, ljobobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"),
162
"ret = [ dbus.ObjectPath('/com/test/legacy_app_instance') ]", NULL);
164
DbusTestDbusMockObject* linstobj = dbus_test_dbus_mock_get_object(mock, "/com/test/legacy_app_instance",
165
"com.ubuntu.Upstart0_6.Instance", NULL);
166
dbus_test_dbus_mock_object_add_property(mock, linstobj, "name", G_VARIANT_TYPE_STRING,
167
g_variant_new_string("multiple-2342345"), NULL);
168
dbus_test_dbus_mock_object_add_property(mock, linstobj, "processes", G_VARIANT_TYPE("a(si)"),
169
g_variant_new_parsed("[('main', 5678)]"), NULL);
171
/* Untrusted Helper */
172
DbusTestDbusMockObject* uhelperobj =
173
dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
175
dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "Start", G_VARIANT_TYPE("(asb)"), NULL,
178
"import subprocess\n"
179
"target = open(\"/tmp/testHelper\", 'w')\n"
181
"for item in args[0]:\n"
182
" keyVal = str(item)\n"
183
" keyVal = keyVal.split(\"=\")\n"
184
" if len(keyVal) == 2:\n"
185
" os.environ[keyVal[0]] = keyVal[1]\n"
186
" if keyVal[0] == \"APP_URIS\":\n"
187
" exec_app = keyVal[1].replace(\"'\", '')\n"
188
"if (exec_app != \"\"):\n"
189
" proc = subprocess.Popen(exec_app, shell=True, stdout=subprocess.PIPE)\n"
195
dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "Stop", G_VARIANT_TYPE("(asb)"), NULL, "", NULL);
197
dbus_test_dbus_mock_object_add_method(mock, uhelperobj, "GetAllInstances", NULL, G_VARIANT_TYPE("ao"),
198
"ret = [ dbus.ObjectPath('/com/test/untrusted/helper/instance'), "
199
"dbus.ObjectPath('/com/test/untrusted/helper/multi_instance') ]",
202
DbusTestDbusMockObject* uhelperinstance = dbus_test_dbus_mock_get_object(
203
mock, "/com/test/untrusted/helper/instance", "com.ubuntu.Upstart0_6.Instance", NULL);
204
dbus_test_dbus_mock_object_add_property(mock, uhelperinstance, "name", G_VARIANT_TYPE_STRING,
205
g_variant_new_string("untrusted-type::com.foo_bar_43.23.12"), NULL);
207
DbusTestDbusMockObject* unhelpermulti = dbus_test_dbus_mock_get_object(
208
mock, "/com/test/untrusted/helper/multi_instance", "com.ubuntu.Upstart0_6.Instance", NULL);
209
dbus_test_dbus_mock_object_add_property(
210
mock, unhelpermulti, "name", G_VARIANT_TYPE_STRING,
211
g_variant_new_string("backup-helper:24034582324132:com.bar_foo_8432.13.1"), NULL);
213
/* Create the cgroup manager mock */
214
cgmock = dbus_test_dbus_mock_new("org.test.cgmock");
215
g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME", "org.test.cgmock", TRUE);
217
DbusTestDbusMockObject* cgobject = dbus_test_dbus_mock_get_object(cgmock, "/org/linuxcontainers/cgmanager",
218
"org.linuxcontainers.cgmanager0_0", NULL);
219
dbus_test_dbus_mock_object_add_method(cgmock, cgobject, "GetTasksRecursive", G_VARIANT_TYPE("(ss)"),
220
G_VARIANT_TYPE("ai"), "ret = [100, 200, 300]", NULL);
222
/* Put it together */
223
dbus_test_service_add_task(service, DBUS_TEST_TASK(mock));
224
dbus_test_service_add_task(service, DBUS_TEST_TASK(cgmock));
225
dbus_test_service_start_tasks(service);
227
bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
228
g_dbus_connection_set_exit_on_close(bus, FALSE);
229
g_object_add_weak_pointer(G_OBJECT(bus), (gpointer*)&bus);
231
/* Make sure we pretend the CG manager is just on our bus */
232
g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);
234
ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this));
235
ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this));
237
registry = std::make_shared<ubuntu::app_launch::Registry>();
240
virtual void TearDown()
244
ubuntu_app_launch_observer_delete_app_focus(focus_cb, this);
245
ubuntu_app_launch_observer_delete_app_resume(resume_cb, this);
247
g_clear_object(&mock);
248
g_clear_object(&cgmock);
249
g_clear_object(&service);
253
unsigned int cleartry = 0;
254
while (bus != NULL && cleartry < 100)
259
ASSERT_EQ(nullptr, bus);
262
GVariant* find_env(GVariant* env_array, const gchar* var)
265
GVariant* retval = nullptr;
267
for (i = 0; i < g_variant_n_children(env_array); i++)
269
GVariant* child = g_variant_get_child_value(env_array, i);
270
const gchar* envvar = g_variant_get_string(child, nullptr);
272
if (g_str_has_prefix(envvar, var))
274
if (retval != nullptr)
276
g_warning("Found the env var more than once!");
277
g_variant_unref(retval);
285
g_variant_unref(child);
291
gchar* envstr = g_variant_print(env_array, FALSE);
292
g_warning("Unable to find '%s' in '%s'", var, envstr);
299
bool check_env(GVariant* env_array, const gchar* var, const gchar* value)
302
GVariant* val = find_env(env_array, var);
308
const gchar* envvar = g_variant_get_string(val, nullptr);
310
gchar* combined = g_strdup_printf("%s=%s", var, value);
311
if (g_strcmp0(envvar, combined) == 0)
316
g_variant_unref(val);
321
void pause(guint time = 0)
325
GMainLoop* mainloop = g_main_loop_new(NULL, FALSE);
328
[](gpointer pmainloop) -> gboolean
330
g_main_loop_quit(static_cast<GMainLoop*>(pmainloop));
331
return G_SOURCE_REMOVE;
335
g_main_loop_run(mainloop);
337
g_main_loop_unref(mainloop);
340
while (g_main_pending())
342
g_main_iteration(TRUE);
347
TEST_F(LibUAL, StartHelper)
349
DbusTestDbusMockObject* obj =
350
dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
352
BackupHelper helper("com.test.multiple_first_1.2.3");
354
QSignalSpy spy(&helper, &BackupHelper::started);
359
auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
360
EXPECT_NE(nullptr, calls);
363
auto env = g_variant_get_child_value(calls->params, 0);
364
EXPECT_TRUE(check_env(env, "APP_ID", "com.test.multiple_first_1.2.3"));
366
check_env(env, "APP_URIS", "'/tmp/test2.py' '1999'"));
367
EXPECT_TRUE(check_env(env, "HELPER_TYPE", "backup-helper"));
368
EXPECT_FALSE(check_env(env, "INSTANCE_ID", NULL));
369
g_variant_unref(env);
371
DbusTestDbusMockObject* objUpstart =
372
dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
375
dbus_test_dbus_mock_object_emit_signal(
376
mock, objUpstart, "EventEmitted", G_VARIANT_TYPE("(sas)"),
377
g_variant_new_parsed("('started', ['JOB=untrusted-helper', 'INSTANCE=backup-helper::com.test.multiple_first_1.2.3'])"),
380
// we set a timeout of 5 seconds waiting for the signal to be emitted,
381
// which should never be reached
382
ASSERT_TRUE(spy.wait(5000));
384
// check that we've got exactly one signal
385
ASSERT_EQ(spy.count(), 1);
388
while (g_main_pending())
390
g_main_iteration(TRUE);
397
TEST_F(LibUAL, StopHelper)
399
DbusTestDbusMockObject* obj =
400
dbus_test_dbus_mock_get_object(mock, "/com/test/untrusted/helper", "com.ubuntu.Upstart0_6.Job", NULL);
402
BackupHelper helper("com.bar_foo_8432.13.1");
403
QSignalSpy spy(&helper, &BackupHelper::finished);
406
ASSERT_EQ(dbus_test_dbus_mock_object_check_method_call(mock, obj, "Stop", NULL, NULL), 1);
409
auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Stop", &len, NULL);
410
EXPECT_NE(nullptr, calls);
413
EXPECT_STREQ("Stop", calls->name);
414
EXPECT_EQ(2, g_variant_n_children(calls->params));
416
auto block = g_variant_get_child_value(calls->params, 1);
417
EXPECT_TRUE(g_variant_get_boolean(block));
418
g_variant_unref(block);
420
auto env = g_variant_get_child_value(calls->params, 0);
421
EXPECT_TRUE(check_env(env, "APP_ID", "com.bar_foo_8432.13.1"));
422
EXPECT_TRUE(check_env(env, "HELPER_TYPE", "backup-helper"));
423
EXPECT_TRUE(check_env(env, "INSTANCE_ID", "24034582324132"));
424
g_variant_unref(env);
426
ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
428
DbusTestDbusMockObject* objUpstart =
429
dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
431
dbus_test_dbus_mock_object_emit_signal(
432
mock, objUpstart, "EventEmitted", G_VARIANT_TYPE("(sas)"),
433
g_variant_new_parsed(
434
"('stopped', ['JOB=untrusted-helper', 'INSTANCE=backup-helper::com.bar_foo_8432.13.1'])"),
437
// we set a timeout of 5 seconds waiting for the signal to be emitted,
438
// which should never be reached
439
ASSERT_TRUE(spy.wait(5000));
441
// check that we've got exactly one signal
442
ASSERT_EQ(spy.count(), 1);
445
while (g_main_pending())
447
g_main_iteration(TRUE);
450
// ASSERT_EQ(stop_data.count, 1);
460
const gchar* instance;
461
} helper_observer_data_t;
463
static void helper_observer_cb(const gchar* appid, const gchar* instance, const gchar* type, gpointer user_data)
465
helper_observer_data_t* data = (helper_observer_data_t*)user_data;
467
if (g_strcmp0(data->appid, appid) == 0 && g_strcmp0(data->type, type) == 0 &&
468
g_strcmp0(data->instance, instance) == 0)
474
TEST_F(LibUAL, StartStopHelperObserver)
476
helper_observer_data_t start_data = {
477
.count = 0, .appid = "com.foo_foo_1.2.3", .type = "my-type-is-scorpio", .instance = nullptr};
478
helper_observer_data_t stop_data = {
479
.count = 0, .appid = "com.bar_bar_44.32", .type = "my-type-is-libra", .instance = "1234"};
481
ASSERT_TRUE(ubuntu_app_launch_observer_add_helper_started(helper_observer_cb, "my-type-is-scorpio", &start_data));
482
ASSERT_TRUE(ubuntu_app_launch_observer_add_helper_stop(helper_observer_cb, "my-type-is-libra", &stop_data));
484
DbusTestDbusMockObject* obj =
485
dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
488
dbus_test_dbus_mock_object_emit_signal(
489
mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
490
g_variant_new_parsed("('started', ['JOB=untrusted-helper', 'INSTANCE=my-type-is-scorpio::com.foo_foo_1.2.3'])"),
494
while (g_main_pending())
496
g_main_iteration(TRUE);
499
ASSERT_EQ(start_data.count, 1);
502
dbus_test_dbus_mock_object_emit_signal(
503
mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
504
g_variant_new_parsed(
505
"('stopped', ['JOB=untrusted-helper', 'INSTANCE=my-type-is-libra:1234:com.bar_bar_44.32'])"),
509
while (g_main_pending())
511
g_main_iteration(TRUE);
514
ASSERT_EQ(stop_data.count, 1);
519
ubuntu_app_launch_observer_delete_helper_started(helper_observer_cb, "my-type-is-scorpio", &start_data));
520
ASSERT_TRUE(ubuntu_app_launch_observer_delete_helper_stop(helper_observer_cb, "my-type-is-libra", &stop_data));
523
int main(int argc, char** argv)
525
QCoreApplication qt_app(argc, argv);
526
::testing::InitGoogleTest(&argc, argv);
527
return RUN_ALL_TESTS();