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