Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2019 Endless Mobile, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib.h>
26 : : #include <gio/gio.h>
27 : : #include <libmalcontent/session-limits.h>
28 : : #include <libmalcontent/manager.h>
29 : : #include <libglib-testing/dbus-queue.h>
30 : : #include <locale.h>
31 : : #include <string.h>
32 : : #include "accounts-service-iface.h"
33 : : #include "accounts-service-extension-iface.h"
34 : :
35 : :
36 : : /* Helper function to convert a constant time in seconds to microseconds,
37 : : * avoiding issues with integer constants being too small for the multiplication
38 : : * by using explicit typing. */
39 : : static guint64
40 : 100 : usec (guint64 sec)
41 : : {
42 : 100 : return sec * G_USEC_PER_SEC;
43 : : }
44 : :
45 : : /* Test that the #GType definitions for various types work. */
46 : : static void
47 : 2 : test_session_limits_types (void)
48 : : {
49 : 2 : g_type_ensure (mct_session_limits_get_type ());
50 : 2 : g_type_ensure (mct_session_limits_builder_get_type ());
51 : 2 : }
52 : :
53 : : /* Test that ref() and unref() work on an #MctSessionLimits. */
54 : : static void
55 : 2 : test_session_limits_refs (void)
56 : : {
57 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
58 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
59 : :
60 : : /* Use an empty #MctSessionLimits. */
61 : 2 : limits = mct_session_limits_builder_end (&builder);
62 : :
63 : 2 : g_assert_nonnull (limits);
64 : :
65 : : /* Call check_time_remaining() to check that the limits object hasn’t been
66 : : * finalised. */
67 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
68 : 2 : mct_session_limits_ref (limits);
69 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
70 : 2 : mct_session_limits_unref (limits);
71 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
72 : :
73 : : /* Final ref is dropped by g_autoptr(). */
74 : 2 : }
75 : :
76 : : /* Check error handling when passing an invalid time for @now_usecs to
77 : : * mct_session_limits_check_time_remaining(). */
78 : : static void
79 : 2 : test_session_limits_check_time_remaining_invalid_time (void)
80 : : {
81 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
82 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
83 : : guint64 time_remaining_secs;
84 : : gboolean time_limit_enabled;
85 : :
86 : : /* Use an empty #MctSessionLimits. */
87 : 2 : limits = mct_session_limits_builder_end (&builder);
88 : :
89 : : /* Pass an invalid time to mct_session_limits_check_time_remaining(). */
90 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, G_MAXUINT64, &time_remaining_secs, &time_limit_enabled));
91 : 2 : g_assert_cmpuint (time_remaining_secs, ==, 0);
92 : 2 : g_assert_true (time_limit_enabled);
93 : 2 : }
94 : :
95 : : /* Basic test of mct_session_limits_serialize() on session limits. */
96 : : static void
97 : 2 : test_session_limits_serialize (void)
98 : : {
99 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
100 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
101 : 2 : g_autoptr(GVariant) serialized = NULL;
102 : :
103 : : /* Use an empty #MctSessionLimits. */
104 : 2 : limits = mct_session_limits_builder_end (&builder);
105 : :
106 : : /* We can’t assert anything about the serialisation format, since it’s opaque. */
107 : 2 : serialized = mct_session_limits_serialize (limits);
108 : 2 : g_assert_nonnull (serialized);
109 : 2 : }
110 : :
111 : : /* Basic test of mct_session_limits_deserialize() on various current and historic
112 : : * serialised app filter variants. */
113 : : static void
114 : 2 : test_session_limits_deserialize (void)
115 : : {
116 : : /* These are all opaque. Older versions should be kept around to test
117 : : * backwards compatibility. */
118 : 2 : const gchar *valid_session_limits[] =
119 : : {
120 : : "@a{sv} {}",
121 : : "{ 'LimitType': <@u 0> }",
122 : : "{ 'LimitType': <@u 1>, 'DailySchedule': <(@u 0, @u 100)> }",
123 : : "{ 'DailySchedule': <(@u 0, @u 100)> }",
124 : : };
125 : :
126 [ + + ]: 10 : for (gsize i = 0; i < G_N_ELEMENTS (valid_session_limits); i++)
127 : : {
128 : 8 : g_autoptr(GVariant) serialized = NULL;
129 : 8 : g_autoptr(MctSessionLimits) limits = NULL;
130 : 8 : g_autoptr(GError) local_error = NULL;
131 : :
132 : 8 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, valid_session_limits[i]);
133 : :
134 : 8 : serialized = g_variant_parse (NULL, valid_session_limits[i], NULL, NULL, NULL);
135 : 8 : g_assert (serialized != NULL);
136 : :
137 : 8 : limits = mct_session_limits_deserialize (serialized, 1, &local_error);
138 : 8 : g_assert_no_error (local_error);
139 : 8 : g_assert_nonnull (limits);
140 : : }
141 : 2 : }
142 : :
143 : : /* Test of mct_session_limits_deserialize() on various invalid variants. */
144 : : static void
145 : 2 : test_session_limits_deserialize_invalid (void)
146 : : {
147 : 2 : const gchar *invalid_session_limits[] =
148 : : {
149 : : "false",
150 : : "()",
151 : : "{ 'LimitType': <@u 100> }",
152 : : "{ 'DailySchedule': <(@u 100, @u 0)> }",
153 : : "{ 'DailySchedule': <(@u 0, @u 4294967295)> }",
154 : : };
155 : :
156 [ + + ]: 12 : for (gsize i = 0; i < G_N_ELEMENTS (invalid_session_limits); i++)
157 : : {
158 : 10 : g_autoptr(GVariant) serialized = NULL;
159 : 10 : g_autoptr(MctSessionLimits) limits = NULL;
160 : 10 : g_autoptr(GError) local_error = NULL;
161 : :
162 : 10 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, invalid_session_limits[i]);
163 : :
164 : 10 : serialized = g_variant_parse (NULL, invalid_session_limits[i], NULL, NULL, NULL);
165 : 10 : g_assert (serialized != NULL);
166 : :
167 : 10 : limits = mct_session_limits_deserialize (serialized, 1, &local_error);
168 : 10 : g_assert_error (local_error, MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_DATA);
169 : 10 : g_assert_null (limits);
170 : : }
171 : 2 : }
172 : :
173 : : /* Fixture for tests which use an #MctSessionLimitsBuilder. The builder can
174 : : * either be heap- or stack-allocated. @builder will always be a valid pointer
175 : : * to it.
176 : : */
177 : : typedef struct
178 : : {
179 : : MctSessionLimitsBuilder *builder;
180 : : MctSessionLimitsBuilder stack_builder;
181 : : } BuilderFixture;
182 : :
183 : : static void
184 : 4 : builder_set_up_stack (BuilderFixture *fixture,
185 : : gconstpointer test_data)
186 : : {
187 : 4 : mct_session_limits_builder_init (&fixture->stack_builder);
188 : 4 : fixture->builder = &fixture->stack_builder;
189 : 4 : }
190 : :
191 : : static void
192 : 4 : builder_tear_down_stack (BuilderFixture *fixture,
193 : : gconstpointer test_data)
194 : : {
195 : 4 : mct_session_limits_builder_clear (&fixture->stack_builder);
196 : 4 : fixture->builder = NULL;
197 : 4 : }
198 : :
199 : : static void
200 : 4 : builder_set_up_stack2 (BuilderFixture *fixture,
201 : : gconstpointer test_data)
202 : : {
203 : 4 : MctSessionLimitsBuilder local_builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
204 : 4 : memcpy (&fixture->stack_builder, &local_builder, sizeof (local_builder));
205 : 4 : fixture->builder = &fixture->stack_builder;
206 : 4 : }
207 : :
208 : : static void
209 : 4 : builder_tear_down_stack2 (BuilderFixture *fixture,
210 : : gconstpointer test_data)
211 : : {
212 : 4 : mct_session_limits_builder_clear (&fixture->stack_builder);
213 : 4 : fixture->builder = NULL;
214 : 4 : }
215 : :
216 : : static void
217 : 4 : builder_set_up_heap (BuilderFixture *fixture,
218 : : gconstpointer test_data)
219 : : {
220 : 4 : fixture->builder = mct_session_limits_builder_new ();
221 : 4 : }
222 : :
223 : : static void
224 : 4 : builder_tear_down_heap (BuilderFixture *fixture,
225 : : gconstpointer test_data)
226 : : {
227 [ + - ]: 4 : g_clear_pointer (&fixture->builder, mct_session_limits_builder_free);
228 : 4 : }
229 : :
230 : : /* Test building a non-empty #MctSessionLimits using an
231 : : * #MctSessionLimitsBuilder. */
232 : : static void
233 : 6 : test_session_limits_builder_non_empty (BuilderFixture *fixture,
234 : : gconstpointer test_data)
235 : : {
236 : 6 : g_autoptr(MctSessionLimits) limits = NULL;
237 : 6 : g_autofree const gchar **sections = NULL;
238 : :
239 : 6 : mct_session_limits_builder_set_daily_schedule (fixture->builder, 100, 8 * 60 * 60);
240 : :
241 : 6 : limits = mct_session_limits_builder_end (fixture->builder);
242 : :
243 : 6 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
244 : 6 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (99), NULL, NULL));
245 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (100), NULL, NULL));
246 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60 - 1), NULL, NULL));
247 : 6 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60), NULL, NULL));
248 : 6 : }
249 : :
250 : : /* Test building an empty #MctSessionLimits using an #MctSessionLimitsBuilder. */
251 : : static void
252 : 6 : test_session_limits_builder_empty (BuilderFixture *fixture,
253 : : gconstpointer test_data)
254 : : {
255 : 6 : g_autoptr(MctSessionLimits) limits = NULL;
256 : 6 : g_autofree const gchar **sections = NULL;
257 : :
258 : 6 : limits = mct_session_limits_builder_end (fixture->builder);
259 : :
260 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
261 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (99), NULL, NULL));
262 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (100), NULL, NULL));
263 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60 - 1), NULL, NULL));
264 : 6 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60), NULL, NULL));
265 : 6 : }
266 : :
267 : : /* Check that copying a cleared #MctSessionLimitsBuilder works, and the copy can
268 : : * then be initialised and used to build a limits object. */
269 : : static void
270 : 2 : test_session_limits_builder_copy_empty (void)
271 : : {
272 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
273 : 2 : g_autoptr(MctSessionLimitsBuilder) builder_copy = NULL;
274 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
275 : :
276 : 2 : mct_session_limits_builder_clear (builder);
277 : 2 : builder_copy = mct_session_limits_builder_copy (builder);
278 : :
279 : 2 : mct_session_limits_builder_init (builder_copy);
280 : 2 : mct_session_limits_builder_set_daily_schedule (builder_copy, 100, 8 * 60 * 60);
281 : 2 : limits = mct_session_limits_builder_end (builder_copy);
282 : :
283 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
284 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (99), NULL, NULL));
285 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (100), NULL, NULL));
286 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60 - 1), NULL, NULL));
287 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60), NULL, NULL));
288 : 2 : }
289 : :
290 : : /* Check that copying a filled #MctSessionLimitsBuilder works, and the copy can
291 : : * be used to build a limits object. */
292 : : static void
293 : 2 : test_session_limits_builder_copy_full (void)
294 : : {
295 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
296 : 2 : g_autoptr(MctSessionLimitsBuilder) builder_copy = NULL;
297 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
298 : :
299 : 2 : mct_session_limits_builder_set_daily_schedule (builder, 100, 8 * 60 * 60);
300 : 2 : builder_copy = mct_session_limits_builder_copy (builder);
301 : 2 : limits = mct_session_limits_builder_end (builder_copy);
302 : :
303 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
304 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (99), NULL, NULL));
305 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (100), NULL, NULL));
306 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60 - 1), NULL, NULL));
307 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (8 * 60 * 60), NULL, NULL));
308 : 2 : }
309 : :
310 : : /* Check that overriding an already-set limit in a #MctSessionLimitsBuilder
311 : : * removes all trace of it. In this test, override with a ‘none’ limit. */
312 : : static void
313 : 2 : test_session_limits_builder_override_none (void)
314 : : {
315 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
316 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
317 : :
318 : : /* Set up some schedule. */
319 : 2 : mct_session_limits_builder_set_daily_schedule (builder, 100, 8 * 60 * 60);
320 : :
321 : : /* Override it. */
322 : 2 : mct_session_limits_builder_set_none (builder);
323 : 2 : limits = mct_session_limits_builder_end (builder);
324 : :
325 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (0), NULL, NULL));
326 : 2 : }
327 : :
328 : : /* Check that overriding an already-set limit in a #MctSessionLimitsBuilder
329 : : * removes all trace of it. In this test, override with a ‘daily schedule’
330 : : * limit. */
331 : : static void
332 : 2 : test_session_limits_builder_override_daily_schedule (void)
333 : : {
334 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
335 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
336 : :
337 : : /* Set up some schedule. */
338 : 2 : mct_session_limits_builder_set_daily_schedule (builder, 100, 8 * 60 * 60);
339 : :
340 : : /* Override it. */
341 : 2 : mct_session_limits_builder_set_daily_schedule (builder, 200, 7 * 60 * 60);
342 : 2 : limits = mct_session_limits_builder_end (builder);
343 : :
344 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (150), NULL, NULL));
345 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, usec (4 * 60 * 60), NULL, NULL));
346 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, usec (7 * 60 * 60 + 30 * 60), NULL, NULL));
347 : 2 : }
348 : :
349 : : /* Fixture for tests which interact with the accountsservice over D-Bus. The
350 : : * D-Bus service is mocked up using @queue, which allows us to reply to D-Bus
351 : : * calls from the code under test from within the test process.
352 : : *
353 : : * It exports one user object (for UID 500) and the manager object. The method
354 : : * return values from UID 500 are up to the test in question, so it could be an
355 : : * administrator, or non-administrator, have a restrictive or permissive app
356 : : * limits, etc.
357 : : */
358 : : typedef struct
359 : : {
360 : : GtDBusQueue *queue; /* (owned) */
361 : : uid_t valid_uid;
362 : : uid_t missing_uid;
363 : : MctManager *manager; /* (owned) */
364 : : } BusFixture;
365 : :
366 : : static void
367 : 16 : bus_set_up (BusFixture *fixture,
368 : : gconstpointer test_data)
369 : : {
370 : 15 : g_autoptr(GError) local_error = NULL;
371 : 31 : g_autofree gchar *object_path = NULL;
372 : :
373 : 16 : fixture->valid_uid = 500; /* arbitrarily chosen */
374 : 16 : fixture->missing_uid = 501; /* must be different from valid_uid and not exported */
375 : 16 : fixture->queue = gt_dbus_queue_new ();
376 : :
377 : 16 : gt_dbus_queue_connect (fixture->queue, &local_error);
378 : 15 : g_assert_no_error (local_error);
379 : :
380 : 15 : gt_dbus_queue_own_name (fixture->queue, "org.freedesktop.Accounts");
381 : :
382 : 15 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", fixture->valid_uid);
383 : 15 : gt_dbus_queue_export_object (fixture->queue,
384 : : object_path,
385 : : (GDBusInterfaceInfo *) &com_endlessm_parental_controls_session_limits_interface,
386 : : &local_error);
387 : 15 : g_assert_no_error (local_error);
388 : :
389 : 15 : gt_dbus_queue_export_object (fixture->queue,
390 : : "/org/freedesktop/Accounts",
391 : : (GDBusInterfaceInfo *) &org_freedesktop_accounts_interface,
392 : : &local_error);
393 : 15 : g_assert_no_error (local_error);
394 : :
395 : 15 : fixture->manager = mct_manager_new (gt_dbus_queue_get_client_connection (fixture->queue));
396 : 15 : }
397 : :
398 : : static void
399 : 15 : bus_tear_down (BusFixture *fixture,
400 : : gconstpointer test_data)
401 : : {
402 [ + - ]: 15 : g_clear_object (&fixture->manager);
403 : 15 : gt_dbus_queue_disconnect (fixture->queue, TRUE);
404 [ + - ]: 15 : g_clear_pointer (&fixture->queue, gt_dbus_queue_free);
405 : 15 : }
406 : :
407 : : /* Helper #GAsyncReadyCallback which returns the #GAsyncResult in its @user_data. */
408 : : static void
409 : 8 : async_result_cb (GObject *obj,
410 : : GAsyncResult *result,
411 : : gpointer user_data)
412 : : {
413 : 8 : GAsyncResult **result_out = (GAsyncResult **) user_data;
414 : :
415 : 8 : g_assert_null (*result_out);
416 : 8 : *result_out = g_object_ref (result);
417 : 8 : }
418 : :
419 : : /* Generic mock accountsservice implementation which returns the properties
420 : : * given in #GetSessionLimitsData.properties if queried for a UID matching
421 : : * #GetSessionLimitsData.expected_uid. Intended to be used for writing
422 : : * ‘successful’ mct_manager_get_session_limits() tests returning a variety of
423 : : * values. */
424 : : typedef struct
425 : : {
426 : : uid_t expected_uid;
427 : : const gchar *properties;
428 : : } GetSessionLimitsData;
429 : :
430 : : /* This is run in a worker thread. */
431 : : static void
432 : 3 : get_session_limits_server_cb (GtDBusQueue *queue,
433 : : gpointer user_data)
434 : : {
435 : 3 : const GetSessionLimitsData *data = user_data;
436 : 3 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
437 : 3 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
438 : 3 : g_autofree gchar *object_path = NULL;
439 : 3 : g_autoptr(GVariant) properties_variant = NULL;
440 : :
441 : : /* Handle the FindUserById() call. */
442 : : gint64 user_id;
443 : 3 : invocation1 =
444 : 3 : gt_dbus_queue_assert_pop_message (queue,
445 : : "/org/freedesktop/Accounts",
446 : : "org.freedesktop.Accounts",
447 : : "FindUserById", "(x)", &user_id);
448 : 3 : g_assert_cmpint (user_id, ==, data->expected_uid);
449 : :
450 : 3 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
451 : 3 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
452 : :
453 : : /* Handle the Properties.GetAll() call and return some arbitrary, valid values
454 : : * for the given user. */
455 : : const gchar *property_interface;
456 : 3 : invocation2 =
457 : 3 : gt_dbus_queue_assert_pop_message (queue,
458 : : object_path,
459 : : "org.freedesktop.DBus.Properties",
460 : : "GetAll", "(&s)", &property_interface);
461 : 3 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
462 : :
463 : 3 : properties_variant = g_variant_ref_sink (g_variant_new_parsed (data->properties));
464 : 3 : g_dbus_method_invocation_return_value (invocation2,
465 : : g_variant_new_tuple (&properties_variant, 1));
466 : 3 : }
467 : :
468 : : /* Test that getting an #MctSessionLimits from the mock D-Bus service works. The
469 : : * @test_data is a boolean value indicating whether to do the call
470 : : * synchronously (%FALSE) or asynchronously (%TRUE).
471 : : *
472 : : * The mock D-Bus replies are generated in get_session_limits_server_cb(), which
473 : : * is used for both synchronous and asynchronous calls. */
474 : : static void
475 : 2 : test_session_limits_bus_get (BusFixture *fixture,
476 : : gconstpointer test_data)
477 : : {
478 : 2 : g_autoptr(MctSessionLimits) session_limits = NULL;
479 : 2 : g_autoptr(GError) local_error = NULL;
480 : : guint64 time_remaining_secs;
481 : : gboolean time_limit_enabled;
482 : 2 : gboolean test_async = GPOINTER_TO_UINT (test_data);
483 : 2 : const GetSessionLimitsData get_session_limits_data =
484 : : {
485 : 2 : .expected_uid = fixture->valid_uid,
486 : : .properties = "{"
487 : : "'LimitType': <@u 1>,"
488 : : "'DailySchedule': <(@u 100, @u 8000)>"
489 : : "}"
490 : : };
491 : :
492 : 2 : gt_dbus_queue_set_server_func (fixture->queue, get_session_limits_server_cb,
493 : : (gpointer) &get_session_limits_data);
494 : :
495 [ + + ]: 2 : if (test_async)
496 : : {
497 : 2 : g_autoptr(GAsyncResult) result = NULL;
498 : :
499 : 1 : mct_manager_get_session_limits_async (fixture->manager,
500 : : fixture->valid_uid,
501 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
502 : : async_result_cb, &result);
503 : :
504 [ + + ]: 5 : while (result == NULL)
505 : 4 : g_main_context_iteration (NULL, TRUE);
506 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result, &local_error);
507 : : }
508 : : else
509 : : {
510 : 1 : session_limits = mct_manager_get_session_limits (fixture->manager,
511 : : fixture->valid_uid,
512 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
513 : : &local_error);
514 : : }
515 : :
516 : 2 : g_assert_no_error (local_error);
517 : 2 : g_assert_nonnull (session_limits);
518 : :
519 : : /* Check the session limits properties. */
520 : 2 : g_assert_cmpuint (mct_session_limits_get_user_id (session_limits), ==, fixture->valid_uid);
521 : 2 : g_assert_true (mct_session_limits_is_enabled (session_limits));
522 : 2 : g_assert_false (mct_session_limits_check_time_remaining (session_limits, usec (0),
523 : : &time_remaining_secs, &time_limit_enabled));
524 : 2 : g_assert_true (time_limit_enabled);
525 : 2 : g_assert_true (mct_session_limits_check_time_remaining (session_limits, usec (2000),
526 : : &time_remaining_secs, &time_limit_enabled));
527 : 2 : g_assert_cmpuint (time_remaining_secs, ==, 8000 - 2000);
528 : 2 : g_assert_true (time_limit_enabled);
529 : 2 : }
530 : :
531 : : /* Test that getting an #MctSessionLimits from the mock D-Bus service works. The
532 : : * @test_data is a boolean value indicating whether to do the call
533 : : * synchronously (%FALSE) or asynchronously (%TRUE).
534 : : *
535 : : * The mock D-Bus replies are generated in get_session_limits_server_cb(), which
536 : : * is used for both synchronous and asynchronous calls. */
537 : : static void
538 : 1 : test_session_limits_bus_get_none (BusFixture *fixture,
539 : : gconstpointer test_data)
540 : : {
541 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
542 : 1 : g_autoptr(GError) local_error = NULL;
543 : : guint64 time_remaining_secs;
544 : : gboolean time_limit_enabled;
545 : 1 : gboolean test_async = GPOINTER_TO_UINT (test_data);
546 : 1 : const GetSessionLimitsData get_session_limits_data =
547 : : {
548 : 1 : .expected_uid = fixture->valid_uid,
549 : : .properties = "{"
550 : : "'LimitType': <@u 0>,"
551 : : "'DailySchedule': <(@u 0, @u 86400)>"
552 : : "}"
553 : : };
554 : :
555 : 1 : gt_dbus_queue_set_server_func (fixture->queue, get_session_limits_server_cb,
556 : : (gpointer) &get_session_limits_data);
557 : :
558 [ - + ]: 1 : if (test_async)
559 : : {
560 : 0 : g_autoptr(GAsyncResult) result = NULL;
561 : :
562 : 0 : mct_manager_get_session_limits_async (fixture->manager,
563 : : fixture->valid_uid,
564 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
565 : : async_result_cb, &result);
566 : :
567 [ # # ]: 0 : while (result == NULL)
568 : 0 : g_main_context_iteration (NULL, TRUE);
569 : 0 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result, &local_error);
570 : : }
571 : : else
572 : : {
573 : 1 : session_limits = mct_manager_get_session_limits (fixture->manager,
574 : : fixture->valid_uid,
575 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
576 : : &local_error);
577 : : }
578 : :
579 : 1 : g_assert_no_error (local_error);
580 : 1 : g_assert_nonnull (session_limits);
581 : :
582 : : /* Check the session limits properties. */
583 : 1 : g_assert_cmpuint (mct_session_limits_get_user_id (session_limits), ==, fixture->valid_uid);
584 : 1 : g_assert_false (mct_session_limits_is_enabled (session_limits));
585 : 1 : g_assert_true (mct_session_limits_check_time_remaining (session_limits, usec (0),
586 : : &time_remaining_secs, &time_limit_enabled));
587 : 1 : g_assert_false (time_limit_enabled);
588 : 1 : g_assert_true (mct_session_limits_check_time_remaining (session_limits, usec (2000),
589 : : &time_remaining_secs, &time_limit_enabled));
590 : 1 : g_assert_false (time_limit_enabled);
591 : 1 : }
592 : :
593 : : /* Test that mct_manager_get_session_limits() returns an appropriate error if the
594 : : * mock D-Bus service reports that the given user cannot be found.
595 : : *
596 : : * The mock D-Bus replies are generated inline. */
597 : : static void
598 : 1 : test_session_limits_bus_get_error_invalid_user (BusFixture *fixture,
599 : : gconstpointer test_data)
600 : : {
601 : 1 : g_autoptr(GAsyncResult) result = NULL;
602 : 1 : g_autoptr(GError) local_error = NULL;
603 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
604 : 1 : g_autofree gchar *error_message = NULL;
605 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
606 : :
607 : 1 : mct_manager_get_session_limits_async (fixture->manager,
608 : : fixture->missing_uid,
609 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
610 : : async_result_cb, &result);
611 : :
612 : : /* Handle the FindUserById() call and claim the user doesn’t exist. */
613 : : gint64 user_id;
614 : 1 : invocation =
615 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
616 : : "/org/freedesktop/Accounts",
617 : : "org.freedesktop.Accounts",
618 : : "FindUserById", "(x)", &user_id);
619 : 1 : g_assert_cmpint (user_id, ==, fixture->missing_uid);
620 : :
621 : 1 : error_message = g_strdup_printf ("Failed to look up user with uid %u.", fixture->missing_uid);
622 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
623 : : "org.freedesktop.Accounts.Error.Failed",
624 : : error_message);
625 : :
626 : : /* Get the get_session_limits() result. */
627 [ + + ]: 2 : while (result == NULL)
628 : 1 : g_main_context_iteration (NULL, TRUE);
629 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
630 : : &local_error);
631 : :
632 : 1 : g_assert_error (local_error,
633 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER);
634 : 1 : g_assert_null (session_limits);
635 : 1 : }
636 : :
637 : : /* Test that mct_manager_get_session_limits() returns an appropriate error if the
638 : : * mock D-Bus service reports that the properties of the given user can’t be
639 : : * accessed due to permissions.
640 : : *
641 : : * The mock D-Bus replies are generated inline. */
642 : : static void
643 : 1 : test_session_limits_bus_get_error_permission_denied (BusFixture *fixture,
644 : : gconstpointer test_data)
645 : : {
646 : 1 : g_autoptr(GAsyncResult) result = NULL;
647 : 1 : g_autoptr(GError) local_error = NULL;
648 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
649 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
650 : 1 : g_autofree gchar *object_path = NULL;
651 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
652 : :
653 : 1 : mct_manager_get_session_limits_async (fixture->manager,
654 : : fixture->valid_uid,
655 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
656 : : async_result_cb, &result);
657 : :
658 : : /* Handle the FindUserById() call. */
659 : : gint64 user_id;
660 : 1 : invocation1 =
661 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
662 : : "/org/freedesktop/Accounts",
663 : : "org.freedesktop.Accounts",
664 : : "FindUserById", "(x)", &user_id);
665 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
666 : :
667 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
668 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
669 : :
670 : : /* Handle the Properties.GetAll() call and return a permission denied error. */
671 : : const gchar *property_interface;
672 : 1 : invocation2 =
673 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
674 : : object_path,
675 : : "org.freedesktop.DBus.Properties",
676 : : "GetAll", "(&s)", &property_interface);
677 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
678 : :
679 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
680 : : "org.freedesktop.Accounts.Error.PermissionDenied",
681 : : "Not authorized");
682 : :
683 : : /* Get the get_session_limits() result. */
684 [ + + ]: 2 : while (result == NULL)
685 : 1 : g_main_context_iteration (NULL, TRUE);
686 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
687 : : &local_error);
688 : :
689 : 1 : g_assert_error (local_error,
690 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
691 : 1 : g_assert_null (session_limits);
692 : 1 : }
693 : :
694 : : /* Test that mct_manager_get_session_limits() returns an appropriate error if
695 : : * the mock D-Bus service replies with no session limits properties (implying
696 : : * that it hasn’t sent the property values because of permissions).
697 : : *
698 : : * The mock D-Bus replies are generated inline. */
699 : : static void
700 : 1 : test_session_limits_bus_get_error_permission_denied_missing (BusFixture *fixture,
701 : : gconstpointer test_data)
702 : : {
703 : 1 : g_autoptr(GAsyncResult) result = NULL;
704 : 1 : g_autoptr(GError) local_error = NULL;
705 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
706 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
707 : 1 : g_autofree gchar *object_path = NULL;
708 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
709 : :
710 : 1 : mct_manager_get_session_limits_async (fixture->manager,
711 : : fixture->valid_uid,
712 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
713 : : async_result_cb, &result);
714 : :
715 : : /* Handle the FindUserById() call. */
716 : : gint64 user_id;
717 : 1 : invocation1 =
718 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
719 : : "/org/freedesktop/Accounts",
720 : : "org.freedesktop.Accounts",
721 : : "FindUserById", "(x)", &user_id);
722 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
723 : :
724 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
725 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
726 : :
727 : : /* Handle the Properties.GetAll() call and return an empty array due to not
728 : : * having permission to access the properties. The code actually keys off the
729 : : * presence of the LimitType property, since that was the first one to be
730 : : * added. */
731 : : const gchar *property_interface;
732 : 1 : invocation2 =
733 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
734 : : object_path,
735 : : "org.freedesktop.DBus.Properties",
736 : : "GetAll", "(&s)", &property_interface);
737 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
738 : :
739 : 1 : g_dbus_method_invocation_return_value (invocation2, g_variant_new ("(a{sv})", NULL));
740 : :
741 : : /* Get the get_session_limits() result. */
742 [ + + ]: 2 : while (result == NULL)
743 : 1 : g_main_context_iteration (NULL, TRUE);
744 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
745 : : &local_error);
746 : :
747 : 1 : g_assert_error (local_error,
748 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
749 : 1 : g_assert_null (session_limits);
750 : 1 : }
751 : :
752 : : /* Test that mct_manager_get_session_limits() returns an error if the mock D-Bus
753 : : * service reports an unrecognised error.
754 : : *
755 : : * The mock D-Bus replies are generated inline. */
756 : : static void
757 : 1 : test_session_limits_bus_get_error_unknown (BusFixture *fixture,
758 : : gconstpointer test_data)
759 : : {
760 : 1 : g_autoptr(GAsyncResult) result = NULL;
761 : 1 : g_autoptr(GError) local_error = NULL;
762 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
763 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
764 : :
765 : 1 : mct_manager_get_session_limits_async (fixture->manager,
766 : : fixture->valid_uid,
767 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
768 : : async_result_cb, &result);
769 : :
770 : : /* Handle the FindUserById() call and return a bogus error. */
771 : : gint64 user_id;
772 : 1 : invocation =
773 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
774 : : "/org/freedesktop/Accounts",
775 : : "org.freedesktop.Accounts",
776 : : "FindUserById", "(x)", &user_id);
777 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
778 : :
779 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
780 : : "org.freedesktop.Accounts.Error.NewAndInterestingError",
781 : : "This is a fake error message "
782 : : "which libmalcontent "
783 : : "will never have seen before, "
784 : : "but must still handle correctly");
785 : :
786 : : /* Get the get_session_limits() result. */
787 [ + + ]: 2 : while (result == NULL)
788 : 1 : g_main_context_iteration (NULL, TRUE);
789 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
790 : : &local_error);
791 : :
792 : : /* We don’t actually care what error is actually used here. */
793 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
794 : 1 : g_assert_null (session_limits);
795 : 1 : }
796 : :
797 : : /* Test that mct_manager_get_session_limits() returns an error if the mock D-Bus
798 : : * service reports an unknown interface, which means that parental controls are
799 : : * not installed properly.
800 : : *
801 : : * The mock D-Bus replies are generated inline. */
802 : : static void
803 : 1 : test_session_limits_bus_get_error_disabled (BusFixture *fixture,
804 : : gconstpointer test_data)
805 : : {
806 : 1 : g_autoptr(GAsyncResult) result = NULL;
807 : 1 : g_autoptr(GError) local_error = NULL;
808 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
809 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
810 : 1 : g_autofree gchar *object_path = NULL;
811 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
812 : :
813 : 1 : mct_manager_get_session_limits_async (fixture->manager,
814 : : fixture->valid_uid,
815 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
816 : : async_result_cb, &result);
817 : :
818 : : /* Handle the FindUserById() call. */
819 : : gint64 user_id;
820 : 1 : invocation1 =
821 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
822 : : "/org/freedesktop/Accounts",
823 : : "org.freedesktop.Accounts",
824 : : "FindUserById", "(x)", &user_id);
825 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
826 : :
827 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
828 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
829 : :
830 : : /* Handle the Properties.GetAll() call and return an InvalidArgs error. */
831 : : const gchar *property_interface;
832 : 1 : invocation2 =
833 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
834 : : object_path,
835 : : "org.freedesktop.DBus.Properties",
836 : : "GetAll", "(&s)", &property_interface);
837 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
838 : :
839 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
840 : : "org.freedesktop.DBus.Error.InvalidArgs",
841 : : "No such interface "
842 : : "“com.endlessm.ParentalControls.SessionLimits”");
843 : :
844 : : /* Get the get_session_limits() result. */
845 [ + + ]: 2 : while (result == NULL)
846 : 1 : g_main_context_iteration (NULL, TRUE);
847 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
848 : : &local_error);
849 : :
850 : 1 : g_assert_error (local_error,
851 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_DISABLED);
852 : 1 : g_assert_null (session_limits);
853 : 1 : }
854 : :
855 : : /* Generic mock accountsservice implementation which handles properties being
856 : : * set on a mock User object, and compares their values to the given
857 : : * `expected_*` ones.
858 : : *
859 : : * If @error_index is non-negative, it gives the index of a Set() call to return
860 : : * the given @dbus_error_name and @dbus_error_message from, rather than
861 : : * accepting the property value from the caller. If @error_index is negative,
862 : : * all Set() calls will be accepted. */
863 : : typedef struct
864 : : {
865 : : uid_t expected_uid;
866 : :
867 : : const gchar * const *expected_properties;
868 : :
869 : : /* All GVariants in text format: */
870 : : const gchar *expected_limit_type_value; /* (nullable) */
871 : : const gchar *expected_daily_schedule_value; /* (nullable) */
872 : :
873 : : gint error_index; /* -1 to return no error */
874 : : const gchar *dbus_error_name; /* NULL to return no error */
875 : : const gchar *dbus_error_message; /* NULL to return no error */
876 : : } SetSessionLimitsData;
877 : :
878 : : static const gchar *
879 : 5 : set_session_limits_data_get_expected_property_value (const SetSessionLimitsData *data,
880 : : const gchar *property_name)
881 : : {
882 [ + + ]: 5 : if (g_str_equal (property_name, "LimitType"))
883 : 2 : return data->expected_limit_type_value;
884 [ + - ]: 3 : else if (g_str_equal (property_name, "DailySchedule"))
885 : 3 : return data->expected_daily_schedule_value;
886 : : else
887 : : g_assert_not_reached ();
888 : : }
889 : :
890 : : /* This is run in a worker thread. */
891 : : static void
892 : 6 : set_session_limits_server_cb (GtDBusQueue *queue,
893 : : gpointer user_data)
894 : : {
895 : 6 : const SetSessionLimitsData *data = user_data;
896 : 6 : g_autoptr(GDBusMethodInvocation) find_invocation = NULL;
897 : 6 : g_autofree gchar *object_path = NULL;
898 : :
899 : 6 : g_assert ((data->error_index == -1) == (data->dbus_error_name == NULL));
900 : 6 : g_assert ((data->dbus_error_name == NULL) == (data->dbus_error_message == NULL));
901 : :
902 : : /* Handle the FindUserById() call. */
903 : : gint64 user_id;
904 : 6 : find_invocation =
905 : 6 : gt_dbus_queue_assert_pop_message (queue,
906 : : "/org/freedesktop/Accounts",
907 : : "org.freedesktop.Accounts",
908 : : "FindUserById", "(x)", &user_id);
909 : 6 : g_assert_cmpint (user_id, ==, data->expected_uid);
910 : :
911 : 6 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
912 : 6 : g_dbus_method_invocation_return_value (find_invocation, g_variant_new ("(o)", object_path));
913 : :
914 : : /* Handle the Properties.Set() calls. */
915 : : gsize i;
916 : :
917 [ + + ]: 11 : for (i = 0; data->expected_properties[i] != NULL; i++)
918 : : {
919 : : const gchar *property_interface;
920 : : const gchar *property_name;
921 [ + + ]: 9 : g_autoptr(GVariant) property_value = NULL;
922 [ + + ]: 9 : g_autoptr(GDBusMethodInvocation) property_invocation = NULL;
923 [ + + ]: 9 : g_autoptr(GVariant) expected_property_value = NULL;
924 : :
925 : 9 : property_invocation =
926 : 9 : gt_dbus_queue_assert_pop_message (queue,
927 : : object_path,
928 : : "org.freedesktop.DBus.Properties",
929 : : "Set", "(&s&sv)", &property_interface,
930 : : &property_name, &property_value);
931 : 9 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
932 : 9 : g_assert_cmpstr (property_name, ==, data->expected_properties[i]);
933 : :
934 [ + + + + ]: 9 : if (data->error_index >= 0 && (gsize) data->error_index == i)
935 : : {
936 : 4 : g_dbus_method_invocation_return_dbus_error (property_invocation,
937 : : data->dbus_error_name,
938 : : data->dbus_error_message);
939 : 4 : break;
940 : : }
941 : : else
942 : : {
943 : 5 : expected_property_value = g_variant_new_parsed (set_session_limits_data_get_expected_property_value (data, property_name));
944 : 5 : g_assert_cmpvariant (property_value, expected_property_value);
945 : :
946 : 5 : g_dbus_method_invocation_return_value (property_invocation, NULL);
947 : : }
948 : : }
949 : 6 : }
950 : :
951 : : /* Test that setting an #MctSessionLimits on the mock D-Bus service works. The
952 : : * @test_data is a boolean value indicating whether to do the call
953 : : * synchronously (%FALSE) or asynchronously (%TRUE).
954 : : *
955 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(), which
956 : : * is used for both synchronous and asynchronous calls. */
957 : : static void
958 : 2 : test_session_limits_bus_set (BusFixture *fixture,
959 : : gconstpointer test_data)
960 : : {
961 : : gboolean success;
962 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
963 : 2 : g_autoptr(MctSessionLimits) session_limits = NULL;
964 : 2 : g_autoptr(GError) local_error = NULL;
965 : 2 : gboolean test_async = GPOINTER_TO_UINT (test_data);
966 : 2 : const gchar *expected_properties[] =
967 : : {
968 : : "DailySchedule",
969 : : "LimitType",
970 : : NULL
971 : : };
972 : 2 : const SetSessionLimitsData set_session_limits_data =
973 : : {
974 : 2 : .expected_uid = fixture->valid_uid,
975 : : .expected_properties = expected_properties,
976 : : .expected_limit_type_value = "@u 1",
977 : : .expected_daily_schedule_value = "(@u 100, @u 4000)",
978 : : .error_index = -1,
979 : : };
980 : :
981 : : /* Build a session limits object. */
982 : 2 : mct_session_limits_builder_set_daily_schedule (&builder, 100, 4000);
983 : :
984 : 2 : session_limits = mct_session_limits_builder_end (&builder);
985 : :
986 : : /* Set the mock service function and set the limits. */
987 : 2 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
988 : : (gpointer) &set_session_limits_data);
989 : :
990 [ + + ]: 2 : if (test_async)
991 : : {
992 : 1 : g_autoptr(GAsyncResult) result = NULL;
993 : :
994 : 1 : mct_manager_set_session_limits_async (fixture->manager,
995 : : fixture->valid_uid, session_limits,
996 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
997 : : async_result_cb, &result);
998 : :
999 [ + + ]: 6 : while (result == NULL)
1000 : 5 : g_main_context_iteration (NULL, TRUE);
1001 : 1 : success = mct_manager_set_session_limits_finish (fixture->manager, result,
1002 : : &local_error);
1003 : : }
1004 : : else
1005 : : {
1006 : 1 : success = mct_manager_set_session_limits (fixture->manager,
1007 : : fixture->valid_uid, session_limits,
1008 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1009 : : &local_error);
1010 : : }
1011 : :
1012 : 2 : g_assert_no_error (local_error);
1013 : 2 : g_assert_true (success);
1014 : 2 : }
1015 : :
1016 : : /* Test that mct_manager_set_session_limits() returns an appropriate error if
1017 : : * the mock D-Bus service reports that the given user cannot be found.
1018 : : *
1019 : : * The mock D-Bus replies are generated inline. */
1020 : : static void
1021 : 1 : test_session_limits_bus_set_error_invalid_user (BusFixture *fixture,
1022 : : gconstpointer test_data)
1023 : : {
1024 : : gboolean success;
1025 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1026 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1027 : 1 : g_autoptr(GAsyncResult) result = NULL;
1028 : 1 : g_autoptr(GError) local_error = NULL;
1029 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
1030 : 1 : g_autofree gchar *error_message = NULL;
1031 : :
1032 : : /* Use the default session limits. */
1033 : 1 : session_limits = mct_session_limits_builder_end (&builder);
1034 : :
1035 : 1 : mct_manager_set_session_limits_async (fixture->manager,
1036 : : fixture->missing_uid, session_limits,
1037 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1038 : : async_result_cb, &result);
1039 : :
1040 : : /* Handle the FindUserById() call and claim the user doesn’t exist. */
1041 : : gint64 user_id;
1042 : 1 : invocation =
1043 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1044 : : "/org/freedesktop/Accounts",
1045 : : "org.freedesktop.Accounts",
1046 : : "FindUserById", "(x)", &user_id);
1047 : 1 : g_assert_cmpint (user_id, ==, fixture->missing_uid);
1048 : :
1049 : 1 : error_message = g_strdup_printf ("Failed to look up user with uid %u.", fixture->missing_uid);
1050 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
1051 : : "org.freedesktop.Accounts.Error.Failed",
1052 : : error_message);
1053 : :
1054 : : /* Get the set_session_limits() result. */
1055 [ + + ]: 2 : while (result == NULL)
1056 : 1 : g_main_context_iteration (NULL, TRUE);
1057 : 1 : success = mct_manager_set_session_limits_finish (fixture->manager, result,
1058 : : &local_error);
1059 : :
1060 : 1 : g_assert_error (local_error,
1061 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER);
1062 : 1 : g_assert_false (success);
1063 : 1 : }
1064 : :
1065 : : /* Test that mct_manager_set_session_limits() returns an appropriate error if the
1066 : : * mock D-Bus service replies with a permission denied error when setting
1067 : : * properties.
1068 : : *
1069 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(). */
1070 : : static void
1071 : 1 : test_session_limits_bus_set_error_permission_denied (BusFixture *fixture,
1072 : : gconstpointer test_data)
1073 : : {
1074 : : gboolean success;
1075 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1076 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1077 : 1 : g_autoptr(GError) local_error = NULL;
1078 : 1 : const gchar *expected_properties[] =
1079 : : {
1080 : : "LimitType",
1081 : : NULL
1082 : : };
1083 : 1 : const SetSessionLimitsData set_session_limits_data =
1084 : : {
1085 : 1 : .expected_uid = fixture->valid_uid,
1086 : : .expected_properties = expected_properties,
1087 : : .error_index = 0,
1088 : : .dbus_error_name = "org.freedesktop.Accounts.Error.PermissionDenied",
1089 : : .dbus_error_message = "Not authorized",
1090 : : };
1091 : :
1092 : : /* Use the default session limits. */
1093 : 1 : session_limits = mct_session_limits_builder_end (&builder);
1094 : :
1095 : 1 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1096 : : (gpointer) &set_session_limits_data);
1097 : :
1098 : 1 : success = mct_manager_set_session_limits (fixture->manager,
1099 : : fixture->valid_uid, session_limits,
1100 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1101 : : &local_error);
1102 : :
1103 : 1 : g_assert_error (local_error,
1104 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1105 : 1 : g_assert_false (success);
1106 : 1 : }
1107 : :
1108 : : /* Test that mct_manager_set_session_limits() returns an error if the mock D-Bus
1109 : : * service reports an unrecognised error.
1110 : : *
1111 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(). */
1112 : : static void
1113 : 1 : test_session_limits_bus_set_error_unknown (BusFixture *fixture,
1114 : : gconstpointer test_data)
1115 : : {
1116 : : gboolean success;
1117 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1118 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1119 : 1 : g_autoptr(GError) local_error = NULL;
1120 : 1 : const gchar *expected_properties[] =
1121 : : {
1122 : : "LimitType",
1123 : : NULL
1124 : : };
1125 : 1 : const SetSessionLimitsData set_session_limits_data =
1126 : : {
1127 : 1 : .expected_uid = fixture->valid_uid,
1128 : : .expected_properties = expected_properties,
1129 : : .error_index = 0,
1130 : : .dbus_error_name = "org.freedesktop.Accounts.Error.NewAndInterestingError",
1131 : : .dbus_error_message = "This is a fake error message which "
1132 : : "libmalcontent will never have seen "
1133 : : "before, but must still handle correctly",
1134 : : };
1135 : :
1136 : : /* Use the default session limits. */
1137 : 1 : session_limits = mct_session_limits_builder_end (&builder);
1138 : :
1139 : 1 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1140 : : (gpointer) &set_session_limits_data);
1141 : :
1142 : 1 : success = mct_manager_set_session_limits (fixture->manager,
1143 : : fixture->valid_uid, session_limits,
1144 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1145 : : &local_error);
1146 : :
1147 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
1148 : 1 : g_assert_false (success);
1149 : 1 : }
1150 : :
1151 : : /* Test that mct_manager_set_session_limits() returns an error if the mock D-Bus
1152 : : * service reports an InvalidArgs error with a given one of its Set() calls.
1153 : : *
1154 : : * @test_data contains a property index encoded with GINT_TO_POINTER(),
1155 : : * indicating which Set() call to return the error on, since the calls are made
1156 : : * in series.
1157 : : *
1158 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(). */
1159 : : static void
1160 : 2 : test_session_limits_bus_set_error_invalid_property (BusFixture *fixture,
1161 : : gconstpointer test_data)
1162 : : {
1163 : : gboolean success;
1164 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1165 : 2 : g_autoptr(MctSessionLimits) session_limits = NULL;
1166 : 2 : g_autoptr(GError) local_error = NULL;
1167 : 2 : const gchar *expected_properties[] =
1168 : : {
1169 : : "DailySchedule",
1170 : : "LimitType",
1171 : : NULL
1172 : : };
1173 : 2 : const SetSessionLimitsData set_session_limits_data =
1174 : : {
1175 : 2 : .expected_uid = fixture->valid_uid,
1176 : : .expected_properties = expected_properties,
1177 : : .expected_limit_type_value = "@u 1",
1178 : : .expected_daily_schedule_value = "(@u 100, @u 3000)",
1179 : 2 : .error_index = GPOINTER_TO_INT (test_data),
1180 : : .dbus_error_name = "org.freedesktop.DBus.Error.InvalidArgs",
1181 : : .dbus_error_message = "Mumble mumble something wrong with the limits value",
1182 : : };
1183 : :
1184 : : /* Build a session limits object. */
1185 : 2 : mct_session_limits_builder_set_daily_schedule (&builder, 100, 3000);
1186 : :
1187 : 2 : session_limits = mct_session_limits_builder_end (&builder);
1188 : :
1189 : 2 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1190 : : (gpointer) &set_session_limits_data);
1191 : :
1192 : 2 : success = mct_manager_set_session_limits (fixture->manager,
1193 : : fixture->valid_uid, session_limits,
1194 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1195 : : &local_error);
1196 : :
1197 : 2 : g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
1198 : 2 : g_assert_false (success);
1199 : 2 : }
1200 : :
1201 : : int
1202 : 2 : main (int argc,
1203 : : char **argv)
1204 : : {
1205 : 2 : setlocale (LC_ALL, "");
1206 : 2 : g_test_init (&argc, &argv, NULL);
1207 : :
1208 : 2 : g_test_add_func ("/session-limits/types", test_session_limits_types);
1209 : 2 : g_test_add_func ("/session-limits/refs", test_session_limits_refs);
1210 : 2 : g_test_add_func ("/session-limits/check-time-remaining/invalid-time",
1211 : : test_session_limits_check_time_remaining_invalid_time);
1212 : :
1213 : 2 : g_test_add_func ("/session-limits/serialize", test_session_limits_serialize);
1214 : 2 : g_test_add_func ("/session-limits/deserialize", test_session_limits_deserialize);
1215 : 2 : g_test_add_func ("/session-limits/deserialize/invalid", test_session_limits_deserialize_invalid);
1216 : :
1217 : 2 : g_test_add ("/session-limits/builder/stack/non-empty", BuilderFixture, NULL,
1218 : : builder_set_up_stack, test_session_limits_builder_non_empty,
1219 : : builder_tear_down_stack);
1220 : 2 : g_test_add ("/session-limits/builder/stack/empty", BuilderFixture, NULL,
1221 : : builder_set_up_stack, test_session_limits_builder_empty,
1222 : : builder_tear_down_stack);
1223 : 2 : g_test_add ("/session-limits/builder/stack2/non-empty", BuilderFixture, NULL,
1224 : : builder_set_up_stack2, test_session_limits_builder_non_empty,
1225 : : builder_tear_down_stack2);
1226 : 2 : g_test_add ("/session-limits/builder/stack2/empty", BuilderFixture, NULL,
1227 : : builder_set_up_stack2, test_session_limits_builder_empty,
1228 : : builder_tear_down_stack2);
1229 : 2 : g_test_add ("/session-limits/builder/heap/non-empty", BuilderFixture, NULL,
1230 : : builder_set_up_heap, test_session_limits_builder_non_empty,
1231 : : builder_tear_down_heap);
1232 : 2 : g_test_add ("/session-limits/builder/heap/empty", BuilderFixture, NULL,
1233 : : builder_set_up_heap, test_session_limits_builder_empty,
1234 : : builder_tear_down_heap);
1235 : 2 : g_test_add_func ("/session-limits/builder/copy/empty",
1236 : : test_session_limits_builder_copy_empty);
1237 : 2 : g_test_add_func ("/session-limits/builder/copy/full",
1238 : : test_session_limits_builder_copy_full);
1239 : 2 : g_test_add_func ("/session-limits/builder/override/none",
1240 : : test_session_limits_builder_override_none);
1241 : 2 : g_test_add_func ("/session-limits/builder/override/daily-schedule",
1242 : : test_session_limits_builder_override_daily_schedule);
1243 : :
1244 : 2 : g_test_add ("/session-limits/bus/get/async", BusFixture, GUINT_TO_POINTER (TRUE),
1245 : : bus_set_up, test_session_limits_bus_get, bus_tear_down);
1246 : 2 : g_test_add ("/session-limits/bus/get/sync", BusFixture, GUINT_TO_POINTER (FALSE),
1247 : : bus_set_up, test_session_limits_bus_get, bus_tear_down);
1248 : 2 : g_test_add ("/session-limits/bus/get/none", BusFixture, NULL,
1249 : : bus_set_up, test_session_limits_bus_get_none, bus_tear_down);
1250 : :
1251 : 2 : g_test_add ("/session-limits/bus/get/error/invalid-user", BusFixture, NULL,
1252 : : bus_set_up, test_session_limits_bus_get_error_invalid_user, bus_tear_down);
1253 : 2 : g_test_add ("/session-limits/bus/get/error/permission-denied", BusFixture, NULL,
1254 : : bus_set_up, test_session_limits_bus_get_error_permission_denied, bus_tear_down);
1255 : 2 : g_test_add ("/session-limits/bus/get/error/permission-denied-missing", BusFixture, NULL,
1256 : : bus_set_up, test_session_limits_bus_get_error_permission_denied_missing, bus_tear_down);
1257 : 2 : g_test_add ("/session-limits/bus/get/error/unknown", BusFixture, NULL,
1258 : : bus_set_up, test_session_limits_bus_get_error_unknown, bus_tear_down);
1259 : 2 : g_test_add ("/session-limits/bus/get/error/disabled", BusFixture, NULL,
1260 : : bus_set_up, test_session_limits_bus_get_error_disabled, bus_tear_down);
1261 : :
1262 : 2 : g_test_add ("/session-limits/bus/set/async", BusFixture, GUINT_TO_POINTER (TRUE),
1263 : : bus_set_up, test_session_limits_bus_set, bus_tear_down);
1264 : 2 : g_test_add ("/session-limits/bus/set/sync", BusFixture, GUINT_TO_POINTER (FALSE),
1265 : : bus_set_up, test_session_limits_bus_set, bus_tear_down);
1266 : :
1267 : 2 : g_test_add ("/session-limits/bus/set/error/invalid-user", BusFixture, NULL,
1268 : : bus_set_up, test_session_limits_bus_set_error_invalid_user, bus_tear_down);
1269 : 2 : g_test_add ("/session-limits/bus/set/error/permission-denied", BusFixture, NULL,
1270 : : bus_set_up, test_session_limits_bus_set_error_permission_denied, bus_tear_down);
1271 : 2 : g_test_add ("/session-limits/bus/set/error/unknown", BusFixture, NULL,
1272 : : bus_set_up, test_session_limits_bus_set_error_unknown, bus_tear_down);
1273 : 2 : g_test_add ("/session-limits/bus/set/error/invalid-property/daily-schedule",
1274 : : BusFixture, GINT_TO_POINTER (0), bus_set_up,
1275 : : test_session_limits_bus_set_error_invalid_property, bus_tear_down);
1276 : 2 : g_test_add ("/session-limits/bus/set/error/invalid-property/limit-type",
1277 : : BusFixture, GINT_TO_POINTER (1), bus_set_up,
1278 : : test_session_limits_bus_set_error_invalid_property, bus_tear_down);
1279 : :
1280 : 2 : return g_test_run ();
1281 : : }
|