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 : : * Copyright 2025 GNOME Foundation, Inc.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General Public
19 : : * License along with this library; if not, write to the Free Software
20 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 : : *
22 : : * Authors:
23 : : * - Philip Withnall <withnall@endlessm.com>
24 : : */
25 : :
26 : : #include "config.h"
27 : :
28 : : #include <glib.h>
29 : : #include <gio/gio.h>
30 : : #include <libmalcontent/session-limits.h>
31 : : #include <libmalcontent/manager.h>
32 : : #include <libglib-testing/dbus-queue.h>
33 : : #include <locale.h>
34 : : #include <string.h>
35 : : #include "accounts-service-iface.h"
36 : : #include "accounts-service-extension-iface.h"
37 : :
38 : :
39 : : /* Test that the #GType definitions for various types work. */
40 : : static void
41 : 2 : test_session_limits_types (void)
42 : : {
43 : 2 : g_type_ensure (mct_session_limits_get_type ());
44 : 2 : g_type_ensure (mct_session_limits_builder_get_type ());
45 : 2 : }
46 : :
47 : : /* Test that ref() and unref() work on an #MctSessionLimits. */
48 : : static void
49 : 2 : test_session_limits_refs (void)
50 : : {
51 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
52 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
53 : 4 : g_autoptr(GDateTime) dummy_dt = g_date_time_new_from_unix_utc (0);
54 : :
55 : : /* Use an empty #MctSessionLimits. */
56 : 2 : limits = mct_session_limits_builder_end (&builder);
57 : :
58 : 2 : g_assert_nonnull (limits);
59 : :
60 : : /* Call check_time_remaining() to check that the limits object hasn’t been
61 : : * finalised. */
62 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, dummy_dt, 0, NULL, NULL));
63 : 2 : mct_session_limits_ref (limits);
64 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, dummy_dt, 0, NULL, NULL));
65 : 2 : mct_session_limits_unref (limits);
66 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, dummy_dt, 0, NULL, NULL));
67 : :
68 : : /* Final ref is dropped by g_autoptr(). */
69 : 2 : }
70 : :
71 : : /* Check error handling when passing an invalid time for @now_secs to
72 : : * mct_session_limits_check_time_remaining(). */
73 : : static void
74 : 2 : test_session_limits_check_time_remaining_invalid_time (void)
75 : : {
76 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
77 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
78 : : guint64 time_remaining_secs;
79 : : gboolean time_limit_enabled;
80 : :
81 : : /* Use an empty #MctSessionLimits. */
82 : 2 : limits = mct_session_limits_builder_end (&builder);
83 : :
84 : : /* Pass an invalid time to mct_session_limits_check_time_remaining(). */
85 : 4 : g_autoptr(GDateTime) invalid_dt = g_date_time_new_from_unix_utc (G_MAXUINT64);
86 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, invalid_dt, 0, &time_remaining_secs, &time_limit_enabled));
87 : 2 : g_assert_cmpuint (time_remaining_secs, ==, 0);
88 : 2 : g_assert_true (time_limit_enabled);
89 : 2 : }
90 : :
91 : : /* Check calculation of time remaining via
92 : : * mct_session_limits_check_time_remaining() with a daily schedule limit. */
93 : : static void
94 : 2 : test_session_limits_check_time_remaining_daily_schedule (void)
95 : : {
96 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
97 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
98 : :
99 : 2 : mct_session_limits_builder_set_daily_schedule (&builder, TRUE, 100, 1000);
100 : 2 : limits = mct_session_limits_builder_end (&builder);
101 : :
102 : : /* Try different calls to mct_session_limits_check_time_remaining(). */
103 : : const struct
104 : : {
105 : : const char *now_iso8601;
106 : : gboolean expected_has_time_remaining;
107 : : gboolean expected_time_remaining_secs;
108 : : }
109 : 2 : vectors[] =
110 : : {
111 : : { "2025-07-12T00:00:00Z", FALSE, 0 },
112 : : { "2025-07-12T00:01:39Z", FALSE, 0 },
113 : : { "2025-07-12T00:01:40Z", TRUE, 900 },
114 : : { "2025-07-12T00:16:39Z", TRUE, 1 },
115 : : { "2025-07-12T00:16:40Z", FALSE, 0 },
116 : : { "2025-07-12T00:17:00Z", FALSE, 0 },
117 : : };
118 : :
119 [ + + ]: 14 : for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
120 : : {
121 : : uint64_t time_remaining_secs;
122 : : gboolean has_time_remaining, time_limit_enabled;
123 : 24 : g_autoptr(GDateTime) now_dt = g_date_time_new_from_iso8601 (vectors[i].now_iso8601, NULL);
124 : :
125 : 12 : g_test_message ("Vector %zu: %s", i, vectors[i].now_iso8601);
126 : :
127 : 12 : has_time_remaining = mct_session_limits_check_time_remaining (limits, now_dt, 0, &time_remaining_secs, &time_limit_enabled);
128 : 12 : g_assert_true (has_time_remaining == vectors[i].expected_has_time_remaining);
129 : 12 : g_assert_cmpuint (time_remaining_secs, ==, vectors[i].expected_time_remaining_secs);
130 : 12 : g_assert_true (time_limit_enabled);
131 : : }
132 : 2 : }
133 : :
134 : : /* Check calculation of time remaining via
135 : : * mct_session_limits_check_time_remaining() with a daily schedule limit. */
136 : : static void
137 : 3 : test_session_limits_check_time_remaining_daily_schedule_timezone (void)
138 : : {
139 : : /* Change the timezone to one with a non-zero UTC offset (any time of year).
140 : : * This needs to be done in a subprocess so we’re not changing environment
141 : : * variables at runtime. */
142 [ + + ]: 3 : if (!g_test_subprocess ())
143 : : {
144 : 4 : g_auto(GStrv) envp = g_get_environ ();
145 : 2 : envp = g_environ_setenv (g_steal_pointer (&envp), "TZ", "Europe/Warsaw", TRUE);
146 : 2 : g_test_trap_subprocess_with_envp (NULL, (const char * const *) envp, 0, G_TEST_SUBPROCESS_DEFAULT);
147 : 2 : g_test_trap_assert_passed ();
148 : : }
149 : : else
150 : : {
151 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
152 : 1 : g_autoptr(MctSessionLimits) limits = NULL;
153 : : guint64 time_remaining_secs;
154 : : gboolean time_limit_enabled;
155 : 2 : g_autoptr(GTimeZone) local_tz = g_time_zone_new_local ();
156 : 1 : g_autoptr(GDateTime) now_dt = NULL;
157 : :
158 : : /* Check the timezone loaded properly. */
159 : 1 : g_assert_cmpstr (g_time_zone_get_identifier (local_tz), ==, "Europe/Warsaw");
160 : :
161 : 1 : mct_session_limits_builder_set_daily_schedule (&builder, TRUE, 8 * 60 * 60, 20 * 60 * 60);
162 : 1 : limits = mct_session_limits_builder_end (&builder);
163 : :
164 : 1 : now_dt = g_date_time_new_from_iso8601 ("2025-08-21T16:00:00", local_tz);
165 : 1 : g_assert_true (g_date_time_get_timezone (now_dt) == local_tz);
166 : 1 : g_assert_true (mct_session_limits_check_time_remaining (limits, now_dt, 0, &time_remaining_secs, &time_limit_enabled));
167 : 1 : g_assert_cmpuint (time_remaining_secs, ==, 4 * 60 * 60);
168 : 1 : g_assert_true (time_limit_enabled);
169 : : }
170 : 3 : }
171 : :
172 : : /* Check calculation of time remaining via
173 : : * mct_session_limits_check_time_remaining() with a daily limit. */
174 : : static void
175 : 2 : test_session_limits_check_time_remaining_daily_limit (void)
176 : : {
177 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
178 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
179 : :
180 : 2 : mct_session_limits_builder_set_daily_limit (&builder, TRUE, 100);
181 : 2 : limits = mct_session_limits_builder_end (&builder);
182 : :
183 : : /* Try different calls to mct_session_limits_check_time_remaining(). */
184 : : const struct
185 : : {
186 : : const char *now_iso8601;
187 : : uint64_t active_session_time_today_secs;
188 : : gboolean expected_has_time_remaining;
189 : : gboolean expected_time_remaining_secs;
190 : : }
191 : 2 : vectors[] =
192 : : {
193 : : { "2025-07-12T00:00:00Z", 0, TRUE, 100 },
194 : : { "2025-07-12T01:00:00Z", 0, TRUE, 100 },
195 : : { "2025-07-12T01:00:50Z", 50, TRUE, 50 },
196 : : { "2025-07-12T01:01:39Z", 99, TRUE, 1 },
197 : : { "2025-07-12T01:01:40Z", 100, FALSE, 0 },
198 : : { "2025-07-12T02:00:00Z", 100, FALSE, 0 },
199 : : };
200 : :
201 [ + + ]: 14 : for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
202 : : {
203 : : uint64_t time_remaining_secs;
204 : : gboolean has_time_remaining, time_limit_enabled;
205 : 24 : g_autoptr(GDateTime) now_dt = g_date_time_new_from_iso8601 (vectors[i].now_iso8601, NULL);
206 : :
207 : 12 : g_test_message ("Vector %zu: %s", i, vectors[i].now_iso8601);
208 : :
209 : 12 : has_time_remaining = mct_session_limits_check_time_remaining (limits, now_dt, vectors[i].active_session_time_today_secs, &time_remaining_secs, &time_limit_enabled);
210 : 12 : g_assert_true (has_time_remaining == vectors[i].expected_has_time_remaining);
211 : 12 : g_assert_cmpuint (time_remaining_secs, ==, vectors[i].expected_time_remaining_secs);
212 : 12 : g_assert_true (time_limit_enabled);
213 : : }
214 : 2 : }
215 : :
216 : : /* Check calculation of time remaining via
217 : : * mct_session_limits_check_time_remaining() with multiple types of limit. */
218 : : static void
219 : 2 : test_session_limits_check_time_remaining_multiple (void)
220 : : {
221 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
222 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
223 : :
224 : : /* Session limits here are: 4h of computer use between 08:00 and 18:00. */
225 : 2 : mct_session_limits_builder_set_daily_schedule (&builder, TRUE, 8 * 60 * 60, 18 * 60 * 60);
226 : 2 : mct_session_limits_builder_set_daily_limit (&builder, TRUE, 4 * 60 * 60);
227 : 2 : limits = mct_session_limits_builder_end (&builder);
228 : :
229 : : /* Try different calls to mct_session_limits_check_time_remaining(). */
230 : : const struct
231 : : {
232 : : const char *now_iso8601;
233 : : uint64_t active_session_time_today_secs;
234 : : gboolean expected_has_time_remaining;
235 : : gboolean expected_time_remaining_secs;
236 : : }
237 : 2 : vectors[] =
238 : : {
239 : : { "2025-07-12T00:00:00Z", 0, FALSE, 0 },
240 : : { "2025-07-12T08:00:00Z", 0, TRUE, 4 * 60 * 60 },
241 : : { "2025-07-12T12:00:00Z", 2 * 60 * 60, TRUE, 2 * 60 * 60 },
242 : : { "2025-07-12T17:00:00Z", 2 * 60 * 60, TRUE, 1 * 60 * 60 },
243 : : { "2025-07-12T18:00:00Z", 3 * 60 * 60, FALSE, 0 },
244 : : };
245 : :
246 [ + + ]: 12 : for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
247 : : {
248 : : uint64_t time_remaining_secs;
249 : : gboolean has_time_remaining, time_limit_enabled;
250 : 20 : g_autoptr(GDateTime) now_dt = g_date_time_new_from_iso8601 (vectors[i].now_iso8601, NULL);
251 : :
252 : 10 : g_test_message ("Vector %zu: %s", i, vectors[i].now_iso8601);
253 : :
254 : 10 : has_time_remaining = mct_session_limits_check_time_remaining (limits, now_dt, vectors[i].active_session_time_today_secs, &time_remaining_secs, &time_limit_enabled);
255 : 10 : g_assert_true (has_time_remaining == vectors[i].expected_has_time_remaining);
256 : 10 : g_assert_cmpuint (time_remaining_secs, ==, vectors[i].expected_time_remaining_secs);
257 : 10 : g_assert_true (time_limit_enabled);
258 : : }
259 : 2 : }
260 : :
261 : : /* Basic test of mct_session_limits_serialize() on session limits. */
262 : : static void
263 : 2 : test_session_limits_serialize (void)
264 : : {
265 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
266 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
267 : 2 : g_autoptr(GVariant) serialized = NULL;
268 : :
269 : : /* Use an empty #MctSessionLimits. */
270 : 2 : limits = mct_session_limits_builder_end (&builder);
271 : :
272 : : /* We can’t assert anything about the serialisation format, since it’s opaque. */
273 : 2 : serialized = mct_session_limits_serialize (limits);
274 : 2 : g_assert_nonnull (serialized);
275 : 2 : }
276 : :
277 : : /* Basic test of mct_session_limits_deserialize() on various current and historic
278 : : * serialised app filter variants. */
279 : : static void
280 : 2 : test_session_limits_deserialize (void)
281 : : {
282 : : /* These are all opaque. Older versions should be kept around to test
283 : : * backwards compatibility. */
284 : 2 : const gchar *valid_session_limits[] =
285 : : {
286 : : "@a{sv} {}",
287 : : "{ 'LimitType': <@u 0> }",
288 : : "{ 'LimitType': <@u 1>, 'DailySchedule': <(@u 0, @u 100)> }",
289 : : "{ 'DailySchedule': <(@u 0, @u 100)> }",
290 : : "{ 'LimitType': <@u 2>, 'DailyLimit': <@u 100> }",
291 : : "{ 'DailyLimit': <@u 100> }",
292 : : };
293 : :
294 [ + + ]: 14 : for (gsize i = 0; i < G_N_ELEMENTS (valid_session_limits); i++)
295 : : {
296 : 12 : g_autoptr(GVariant) serialized = NULL;
297 : 12 : g_autoptr(MctSessionLimits) limits = NULL;
298 : 12 : g_autoptr(GError) local_error = NULL;
299 : :
300 : 12 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, valid_session_limits[i]);
301 : :
302 : 12 : serialized = g_variant_parse (NULL, valid_session_limits[i], NULL, NULL, NULL);
303 : 12 : g_assert (serialized != NULL);
304 : :
305 : 12 : limits = mct_session_limits_deserialize (serialized, 1, &local_error);
306 : 12 : g_assert_no_error (local_error);
307 : 12 : g_assert_nonnull (limits);
308 : : }
309 : 2 : }
310 : :
311 : : /* Test of mct_session_limits_deserialize() on various invalid variants. */
312 : : static void
313 : 2 : test_session_limits_deserialize_invalid (void)
314 : : {
315 : 2 : const gchar *invalid_session_limits[] =
316 : : {
317 : : "false",
318 : : "()",
319 : : "{ 'LimitType': <@u 100> }",
320 : : "{ 'DailySchedule': <(@u 100, @u 0)> }",
321 : : "{ 'DailySchedule': <(@u 0, @u 4294967295)> }",
322 : : "{ 'DailyLimit': <@u 4294967295> }",
323 : : };
324 : :
325 [ + + ]: 14 : for (gsize i = 0; i < G_N_ELEMENTS (invalid_session_limits); i++)
326 : : {
327 : 12 : g_autoptr(GVariant) serialized = NULL;
328 : 12 : g_autoptr(MctSessionLimits) limits = NULL;
329 : 12 : g_autoptr(GError) local_error = NULL;
330 : :
331 : 12 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, invalid_session_limits[i]);
332 : :
333 : 12 : serialized = g_variant_parse (NULL, invalid_session_limits[i], NULL, NULL, NULL);
334 : 12 : g_assert (serialized != NULL);
335 : :
336 : 12 : limits = mct_session_limits_deserialize (serialized, 1, &local_error);
337 : 12 : g_assert_error (local_error, MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_DATA);
338 : 12 : g_assert_null (limits);
339 : : }
340 : 2 : }
341 : :
342 : : /* Test that mct_session_limits_deserialize() preserves limit values which have
343 : : * been set but not enforced (by not being listed in LimitType). This allows us
344 : : * to save user parental controls settings even when they’re not currently being
345 : : * enforced. */
346 : : static void
347 : 2 : test_session_limits_deserialize_set_limits_type (void)
348 : : {
349 : 2 : g_autoptr(GVariant) serialized = NULL;
350 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
351 : 2 : g_autoptr(GError) local_error = NULL;
352 : 2 : unsigned int start_time_secs = 0, end_time_secs = 0, daily_limit_secs = 0;
353 : :
354 : 2 : serialized = g_variant_parse (NULL, "{ 'LimitType': <@u 0>, 'DailySchedule': <(@u 123, @u 456)>, 'DailyLimit': <@u 666> }", NULL, NULL, NULL);
355 : 2 : g_assert (serialized != NULL);
356 : :
357 : 2 : limits = mct_session_limits_deserialize (serialized, 1, &local_error);
358 : 2 : g_assert_no_error (local_error);
359 : 2 : g_assert_nonnull (limits);
360 : :
361 : 2 : g_assert_false (mct_session_limits_is_enabled (limits));
362 : 2 : g_assert_false (mct_session_limits_get_daily_schedule (limits,
363 : : &start_time_secs,
364 : : &end_time_secs));
365 : 2 : g_assert_cmpuint (start_time_secs, ==, 123);
366 : 2 : g_assert_cmpuint (end_time_secs, ==, 456);
367 : :
368 : 2 : g_assert_false (mct_session_limits_get_daily_limit (limits, &daily_limit_secs));
369 : 2 : g_assert_cmpuint (daily_limit_secs, ==, 666);
370 : 2 : }
371 : :
372 : : /* Test that mct_session_limits_equal() returns the correct results on various
373 : : * session limits. */
374 : : static void
375 : 2 : test_session_limits_equal (void)
376 : : {
377 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
378 : : MctSessionLimits *equal_filters[2];
379 : 2 : const char *unequal_filters_serialised[] =
380 : : {
381 : : "@a{sv} {}",
382 : : "{ 'LimitType': <@u 1>, 'DailySchedule': <(@u 0, @u 100)> }",
383 : : "{ 'LimitType': <@u 1>, 'DailySchedule': <(@u 0, @u 101)> }",
384 : : "{ 'DailySchedule': <(@u 0, @u 100)> }",
385 : : "{ 'DailySchedule': <(@u 0, @u 101)> }",
386 : : "{ 'LimitType': <@u 2>, 'DailyLimit': <@u 100> }",
387 : : "{ 'DailyLimit': <@u 100> }",
388 : : };
389 : : MctSessionLimits *unequal_filters[G_N_ELEMENTS (unequal_filters_serialised)];
390 : :
391 : : /* Build a couple of filters which are identical. */
392 : 2 : equal_filters[0] = mct_session_limits_builder_end (&builder);
393 : :
394 : 2 : mct_session_limits_builder_init (&builder);
395 : 2 : equal_filters[1] = mct_session_limits_builder_end (&builder);
396 : :
397 : : /* And a load of filters which are not. */
398 [ + + ]: 16 : for (size_t i = 0; i < G_N_ELEMENTS (unequal_filters_serialised); i++)
399 : : {
400 : 14 : g_autoptr(GVariant) serialized = NULL;
401 : :
402 : 14 : serialized = g_variant_parse (NULL, unequal_filters_serialised[i], NULL, NULL, NULL);
403 : 14 : g_assert (serialized != NULL);
404 : :
405 : 14 : unequal_filters[i] = mct_session_limits_deserialize (serialized, 1, NULL);
406 : 14 : g_assert (unequal_filters[i] != NULL);
407 : : }
408 : :
409 : : /* Test the equality checks on them all. */
410 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (equal_filters); i++)
411 [ + + ]: 12 : for (size_t j = 0; j < G_N_ELEMENTS (equal_filters); j++)
412 : 8 : g_assert_true (mct_session_limits_equal (equal_filters[i], equal_filters[j]));
413 : :
414 [ + + ]: 16 : for (size_t i = 0; i < G_N_ELEMENTS (unequal_filters); i++)
415 : : {
416 [ + + ]: 42 : for (size_t j = 0; j < G_N_ELEMENTS (equal_filters); j++)
417 : 28 : g_assert_false (mct_session_limits_equal (unequal_filters[i], equal_filters[j]));
418 [ + + ]: 112 : for (size_t j = 0; j < G_N_ELEMENTS (unequal_filters); j++)
419 : : {
420 [ + + ]: 98 : if (i != j)
421 : 84 : g_assert_false (mct_session_limits_equal (unequal_filters[i], unequal_filters[j]));
422 : : else
423 : 14 : g_assert_true (mct_session_limits_equal (unequal_filters[i], unequal_filters[j]));
424 : : }
425 : : }
426 : :
427 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (equal_filters); i++)
428 : 4 : mct_session_limits_unref (equal_filters[i]);
429 [ + + ]: 16 : for (size_t i = 0; i < G_N_ELEMENTS (unequal_filters); i++)
430 : 14 : mct_session_limits_unref (unequal_filters[i]);
431 : 2 : }
432 : :
433 : : /* Fixture for tests which use an #MctSessionLimitsBuilder. The builder can
434 : : * either be heap- or stack-allocated. @builder will always be a valid pointer
435 : : * to it.
436 : : */
437 : : typedef struct
438 : : {
439 : : MctSessionLimitsBuilder *builder;
440 : : MctSessionLimitsBuilder stack_builder;
441 : : } BuilderFixture;
442 : :
443 : : static void
444 : 4 : builder_set_up_stack (BuilderFixture *fixture,
445 : : gconstpointer test_data)
446 : : {
447 : 4 : mct_session_limits_builder_init (&fixture->stack_builder);
448 : 4 : fixture->builder = &fixture->stack_builder;
449 : 4 : }
450 : :
451 : : static void
452 : 4 : builder_tear_down_stack (BuilderFixture *fixture,
453 : : gconstpointer test_data)
454 : : {
455 : 4 : mct_session_limits_builder_clear (&fixture->stack_builder);
456 : 4 : fixture->builder = NULL;
457 : 4 : }
458 : :
459 : : static void
460 : 4 : builder_set_up_stack2 (BuilderFixture *fixture,
461 : : gconstpointer test_data)
462 : : {
463 : 4 : MctSessionLimitsBuilder local_builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
464 : 4 : memcpy (&fixture->stack_builder, &local_builder, sizeof (local_builder));
465 : 4 : fixture->builder = &fixture->stack_builder;
466 : 4 : }
467 : :
468 : : static void
469 : 4 : builder_tear_down_stack2 (BuilderFixture *fixture,
470 : : gconstpointer test_data)
471 : : {
472 : 4 : mct_session_limits_builder_clear (&fixture->stack_builder);
473 : 4 : fixture->builder = NULL;
474 : 4 : }
475 : :
476 : : static void
477 : 4 : builder_set_up_heap (BuilderFixture *fixture,
478 : : gconstpointer test_data)
479 : : {
480 : 4 : fixture->builder = mct_session_limits_builder_new ();
481 : 4 : }
482 : :
483 : : static void
484 : 4 : builder_tear_down_heap (BuilderFixture *fixture,
485 : : gconstpointer test_data)
486 : : {
487 [ + - ]: 4 : g_clear_pointer (&fixture->builder, mct_session_limits_builder_free);
488 : 4 : }
489 : :
490 : : static void
491 : 56 : assert_session_limits_has_time_remaining (MctSessionLimits *limits,
492 : : uint64_t now_secs_local)
493 : : {
494 : : /* Force the user’s timezone to be UTC so we can simply work in seconds since
495 : : * midnight on the Unix epoch; this isn’t how the API is meant to be used, but
496 : : * we’re testing MctSessionLimitsBuilder here */
497 : 112 : g_autoptr(GDateTime) now_dt_local = g_date_time_new_from_unix_utc (now_secs_local);
498 : 56 : g_assert_true (mct_session_limits_check_time_remaining (limits, now_dt_local, 0, NULL, NULL));
499 : 56 : }
500 : :
501 : : static void
502 : 34 : assert_session_limits_has_no_time_remaining (MctSessionLimits *limits,
503 : : uint64_t now_secs_local)
504 : : {
505 : : /* Force the user’s timezone to be UTC so we can simply work in seconds since
506 : : * midnight on the Unix epoch; this isn’t how the API is meant to be used, but
507 : : * we’re testing MctSessionLimitsBuilder here */
508 : 68 : g_autoptr(GDateTime) now_dt_local = g_date_time_new_from_unix_utc (now_secs_local);
509 : 34 : g_assert_false (mct_session_limits_check_time_remaining (limits, now_dt_local, 0, NULL, NULL));
510 : 34 : }
511 : :
512 : : /* Test building a non-empty #MctSessionLimits using an
513 : : * #MctSessionLimitsBuilder. */
514 : : static void
515 : 6 : test_session_limits_builder_non_empty (BuilderFixture *fixture,
516 : : gconstpointer test_data)
517 : : {
518 : 6 : g_autoptr(MctSessionLimits) limits = NULL;
519 : 6 : g_autofree const gchar **sections = NULL;
520 : : unsigned int start, end;
521 : :
522 : 6 : mct_session_limits_builder_set_daily_schedule (fixture->builder, TRUE, 100, 8 * 60 * 60);
523 : :
524 : 6 : limits = mct_session_limits_builder_end (fixture->builder);
525 : :
526 : 6 : assert_session_limits_has_no_time_remaining (limits, 0);
527 : 6 : assert_session_limits_has_no_time_remaining (limits, 99);
528 : 6 : assert_session_limits_has_time_remaining (limits, 100);
529 : 6 : assert_session_limits_has_time_remaining (limits, 8 * 60 * 60 - 1);
530 : 6 : assert_session_limits_has_no_time_remaining (limits, 8 * 60 * 60);
531 : :
532 : 6 : g_assert_true (mct_session_limits_get_daily_schedule (limits, &start, &end));
533 : 6 : g_assert_cmpuint (start, ==, 100);
534 : 6 : g_assert_cmpuint (end, ==, 8 * 60 * 60);
535 : 6 : }
536 : :
537 : : /* Test building an empty #MctSessionLimits using an #MctSessionLimitsBuilder. */
538 : : static void
539 : 6 : test_session_limits_builder_empty (BuilderFixture *fixture,
540 : : gconstpointer test_data)
541 : : {
542 : 6 : g_autoptr(MctSessionLimits) limits = NULL;
543 : 6 : g_autofree const gchar **sections = NULL;
544 : :
545 : 6 : limits = mct_session_limits_builder_end (fixture->builder);
546 : :
547 : 6 : assert_session_limits_has_time_remaining (limits, 0);
548 : 6 : assert_session_limits_has_time_remaining (limits, 99);
549 : 6 : assert_session_limits_has_time_remaining (limits, 100);
550 : 6 : assert_session_limits_has_time_remaining (limits, 8 * 60 * 60 - 1);
551 : 6 : assert_session_limits_has_time_remaining (limits, 8 * 60 * 60);
552 : 6 : }
553 : :
554 : : /* Check that copying a cleared #MctSessionLimitsBuilder works, and the copy can
555 : : * then be initialised and used to build a limits object. */
556 : : static void
557 : 2 : test_session_limits_builder_copy_empty (void)
558 : : {
559 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
560 : 2 : g_autoptr(MctSessionLimitsBuilder) builder_copy = NULL;
561 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
562 : : unsigned int start, end;
563 : :
564 : 2 : mct_session_limits_builder_clear (builder);
565 : 2 : builder_copy = mct_session_limits_builder_copy (builder);
566 : :
567 : 2 : mct_session_limits_builder_init (builder_copy);
568 : 2 : mct_session_limits_builder_set_daily_schedule (builder_copy, TRUE, 100, 8 * 60 * 60);
569 : 2 : limits = mct_session_limits_builder_end (builder_copy);
570 : :
571 : 2 : assert_session_limits_has_no_time_remaining (limits, 0);
572 : 2 : assert_session_limits_has_no_time_remaining (limits, 99);
573 : 2 : assert_session_limits_has_time_remaining (limits, 100);
574 : 2 : assert_session_limits_has_time_remaining (limits, 8 * 60 * 60 - 1);
575 : 2 : assert_session_limits_has_no_time_remaining (limits, 8 * 60 * 60);
576 : :
577 : 2 : g_assert_true (mct_session_limits_get_daily_schedule (limits, &start, &end));
578 : 2 : g_assert_cmpuint (start, ==, 100);
579 : 2 : g_assert_cmpuint (end, ==, 8 * 60 * 60);
580 : 2 : }
581 : :
582 : : /* Check that copying a filled #MctSessionLimitsBuilder works, and the copy can
583 : : * be used to build a limits object. */
584 : : static void
585 : 2 : test_session_limits_builder_copy_full (void)
586 : : {
587 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
588 : 2 : g_autoptr(MctSessionLimitsBuilder) builder_copy = NULL;
589 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
590 : : unsigned int start, end;
591 : :
592 : 2 : mct_session_limits_builder_set_daily_schedule (builder, TRUE, 100, 8 * 60 * 60);
593 : 2 : builder_copy = mct_session_limits_builder_copy (builder);
594 : 2 : limits = mct_session_limits_builder_end (builder_copy);
595 : :
596 : 2 : assert_session_limits_has_no_time_remaining (limits, 0);
597 : 2 : assert_session_limits_has_no_time_remaining (limits, 99);
598 : 2 : assert_session_limits_has_time_remaining (limits, 100);
599 : 2 : assert_session_limits_has_time_remaining (limits, 8 * 60 * 60 - 1);
600 : 2 : assert_session_limits_has_no_time_remaining (limits, 8 * 60 * 60);
601 : :
602 : 2 : g_assert_true (mct_session_limits_get_daily_schedule (limits, &start, &end));
603 : 2 : g_assert_cmpuint (start, ==, 100);
604 : 2 : g_assert_cmpuint (end, ==, 8 * 60 * 60);
605 : 2 : }
606 : :
607 : : /* Check that overriding an already-set limit in a #MctSessionLimitsBuilder
608 : : * removes all trace of it. In this test, override with a ‘none’ limit. */
609 : : static void
610 : 2 : test_session_limits_builder_override_none (void)
611 : : {
612 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
613 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
614 : : unsigned int start, end;
615 : :
616 : : /* Set up some schedule. */
617 : 2 : mct_session_limits_builder_set_daily_schedule (builder, TRUE, 100, 8 * 60 * 60);
618 : :
619 : : /* Override it. */
620 : 2 : mct_session_limits_builder_set_none (builder);
621 : 2 : limits = mct_session_limits_builder_end (builder);
622 : :
623 : 2 : assert_session_limits_has_time_remaining (limits, 0);
624 : :
625 : 2 : g_assert_false (mct_session_limits_get_daily_schedule (limits, &start, &end));
626 : 2 : g_assert_cmpuint (start, ==, 0);
627 : 2 : g_assert_cmpuint (end, ==, 0);
628 : 2 : }
629 : :
630 : : /* Check that overriding an already-set limit in a #MctSessionLimitsBuilder
631 : : * removes all trace of it. In this test, override with a ‘daily schedule’
632 : : * limit. */
633 : : static void
634 : 2 : test_session_limits_builder_override_daily_schedule (void)
635 : : {
636 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
637 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
638 : : unsigned int start, end;
639 : :
640 : : /* Set up some schedule. */
641 : 2 : mct_session_limits_builder_set_daily_schedule (builder, TRUE, 100, 8 * 60 * 60);
642 : :
643 : : /* Override it. */
644 : 2 : mct_session_limits_builder_set_daily_schedule (builder, TRUE, 200, 7 * 60 * 60);
645 : 2 : limits = mct_session_limits_builder_end (builder);
646 : :
647 : 2 : assert_session_limits_has_no_time_remaining (limits, 150);
648 : 2 : assert_session_limits_has_time_remaining (limits, 4 * 60 * 60);
649 : 2 : assert_session_limits_has_no_time_remaining (limits, 7 * 60 * 60 + 30 * 60);
650 : :
651 : 2 : g_assert_true (mct_session_limits_get_daily_schedule (limits, &start, &end));
652 : 2 : g_assert_cmpuint (start, ==, 200);
653 : 2 : g_assert_cmpuint (end, ==, 7 * 60 * 60);
654 : 2 : }
655 : :
656 : : /* Check that overriding an already-set limit in a #MctSessionLimitsBuilder
657 : : * removes all trace of it. In this test, override with a ‘daily limit’
658 : : * limit. */
659 : : static void
660 : 2 : test_session_limits_builder_override_daily_limit (void)
661 : : {
662 : 4 : g_autoptr(MctSessionLimitsBuilder) builder = mct_session_limits_builder_new ();
663 : 2 : g_autoptr(MctSessionLimits) limits = NULL;
664 : : unsigned int limit;
665 : :
666 : : /* Set up some schedule. */
667 : 2 : mct_session_limits_builder_set_daily_schedule (builder, TRUE, 100, 8 * 60 * 60);
668 : :
669 : : /* Override it. */
670 : 2 : mct_session_limits_builder_set_daily_limit (builder, TRUE, 1000);
671 : 2 : limits = mct_session_limits_builder_end (builder);
672 : :
673 : 2 : assert_session_limits_has_time_remaining (limits, 150);
674 : :
675 : : /* actually test with UTC as in assert_session_limits_has_time_remaining(): */
676 : 4 : g_autoptr(GDateTime) now_dt_local1 = g_date_time_new_from_unix_utc (150);
677 : 2 : g_assert_true (mct_session_limits_check_time_remaining (limits, now_dt_local1, 150, NULL, NULL));
678 : 4 : g_autoptr(GDateTime) now_dt_local2 = g_date_time_new_from_unix_utc (1001);
679 : 2 : g_assert_false (mct_session_limits_check_time_remaining (limits, now_dt_local2, 1001, NULL, NULL));
680 : :
681 : 2 : g_assert_true (mct_session_limits_get_daily_limit (limits, &limit));
682 : 2 : g_assert_cmpuint (limit, ==, 1000);
683 : 2 : }
684 : :
685 : : /* Fixture for tests which interact with the accountsservice over D-Bus. The
686 : : * D-Bus service is mocked up using @queue, which allows us to reply to D-Bus
687 : : * calls from the code under test from within the test process.
688 : : *
689 : : * It exports one user object (for UID 500) and the manager object. The method
690 : : * return values from UID 500 are up to the test in question, so it could be an
691 : : * administrator, or non-administrator, have a restrictive or permissive app
692 : : * limits, etc.
693 : : */
694 : : typedef struct
695 : : {
696 : : GtDBusQueue *queue; /* (owned) */
697 : : uid_t valid_uid;
698 : : uid_t missing_uid;
699 : : MctManager *manager; /* (owned) */
700 : : } BusFixture;
701 : :
702 : : static void
703 : 17 : bus_set_up (BusFixture *fixture,
704 : : gconstpointer test_data)
705 : : {
706 : 16 : g_autoptr(GError) local_error = NULL;
707 : 33 : g_autofree gchar *object_path = NULL;
708 : :
709 : 17 : fixture->valid_uid = 500; /* arbitrarily chosen */
710 : 17 : fixture->missing_uid = 501; /* must be different from valid_uid and not exported */
711 : 17 : fixture->queue = gt_dbus_queue_new ();
712 : :
713 : 17 : gt_dbus_queue_connect (fixture->queue, &local_error);
714 : 16 : g_assert_no_error (local_error);
715 : :
716 : 16 : gt_dbus_queue_own_name (fixture->queue, "org.freedesktop.Accounts");
717 : :
718 : 16 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", fixture->valid_uid);
719 : 16 : gt_dbus_queue_export_object (fixture->queue,
720 : : object_path,
721 : : (GDBusInterfaceInfo *) &com_endlessm_parental_controls_session_limits_interface,
722 : : &local_error);
723 : 16 : g_assert_no_error (local_error);
724 : :
725 : 16 : gt_dbus_queue_export_object (fixture->queue,
726 : : "/org/freedesktop/Accounts",
727 : : (GDBusInterfaceInfo *) &org_freedesktop_accounts_interface,
728 : : &local_error);
729 : 16 : g_assert_no_error (local_error);
730 : :
731 : 16 : fixture->manager = mct_manager_new (gt_dbus_queue_get_client_connection (fixture->queue));
732 : 16 : }
733 : :
734 : : static void
735 : 16 : bus_tear_down (BusFixture *fixture,
736 : : gconstpointer test_data)
737 : : {
738 [ + - ]: 16 : g_clear_object (&fixture->manager);
739 : 16 : gt_dbus_queue_disconnect (fixture->queue, TRUE);
740 [ + - ]: 16 : g_clear_pointer (&fixture->queue, gt_dbus_queue_free);
741 : 16 : }
742 : :
743 : : /* Helper #GAsyncReadyCallback which returns the #GAsyncResult in its @user_data. */
744 : : static void
745 : 9 : async_result_cb (GObject *obj,
746 : : GAsyncResult *result,
747 : : gpointer user_data)
748 : : {
749 : 9 : GAsyncResult **result_out = (GAsyncResult **) user_data;
750 : :
751 : 9 : g_assert_null (*result_out);
752 : 9 : *result_out = g_object_ref (result);
753 : 9 : }
754 : :
755 : : /* Generic mock accountsservice implementation which returns the properties
756 : : * given in #GetSessionLimitsData.properties if queried for a UID matching
757 : : * #GetSessionLimitsData.expected_uid. Intended to be used for writing
758 : : * ‘successful’ mct_manager_get_session_limits() tests returning a variety of
759 : : * values. */
760 : : typedef struct
761 : : {
762 : : uid_t expected_uid;
763 : : const gchar *properties;
764 : : } GetSessionLimitsData;
765 : :
766 : : /* This is run in a worker thread. */
767 : : static void
768 : 3 : get_session_limits_server_cb (GtDBusQueue *queue,
769 : : gpointer user_data)
770 : : {
771 : 3 : const GetSessionLimitsData *data = user_data;
772 : 3 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
773 : 3 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
774 : 3 : g_autofree gchar *object_path = NULL;
775 : 3 : g_autoptr(GVariant) properties_variant = NULL;
776 : :
777 : : /* Handle the FindUserById() call. */
778 : : gint64 user_id;
779 : 3 : invocation1 =
780 : 3 : gt_dbus_queue_assert_pop_message (queue,
781 : : "/org/freedesktop/Accounts",
782 : : "org.freedesktop.Accounts",
783 : : "FindUserById", "(x)", &user_id);
784 : 3 : g_assert_cmpint (user_id, ==, data->expected_uid);
785 : :
786 : 3 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
787 : 3 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
788 : :
789 : : /* Handle the Properties.GetAll() call and return some arbitrary, valid values
790 : : * for the given user. */
791 : : const gchar *property_interface;
792 : 3 : invocation2 =
793 : 3 : gt_dbus_queue_assert_pop_message (queue,
794 : : object_path,
795 : : "org.freedesktop.DBus.Properties",
796 : : "GetAll", "(&s)", &property_interface);
797 : 3 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
798 : :
799 : 3 : properties_variant = g_variant_ref_sink (g_variant_new_parsed (data->properties));
800 : 3 : g_dbus_method_invocation_return_value (invocation2,
801 : : g_variant_new_tuple (&properties_variant, 1));
802 : 3 : }
803 : :
804 : : /* Test that getting an #MctSessionLimits from the mock D-Bus service works. The
805 : : * @test_data is a boolean value indicating whether to do the call
806 : : * synchronously (%FALSE) or asynchronously (%TRUE).
807 : : *
808 : : * The mock D-Bus replies are generated in get_session_limits_server_cb(), which
809 : : * is used for both synchronous and asynchronous calls. */
810 : : static void
811 : 2 : test_session_limits_bus_get (BusFixture *fixture,
812 : : gconstpointer test_data)
813 : : {
814 : 2 : g_autoptr(MctSessionLimits) session_limits = NULL;
815 : 2 : g_autoptr(GError) local_error = NULL;
816 : : guint64 time_remaining_secs;
817 : : gboolean time_limit_enabled;
818 : 2 : gboolean test_async = GPOINTER_TO_UINT (test_data);
819 : 2 : const GetSessionLimitsData get_session_limits_data =
820 : : {
821 : 2 : .expected_uid = fixture->valid_uid,
822 : : .properties = "{"
823 : : "'LimitType': <@u 1>,"
824 : : "'DailySchedule': <(@u 100, @u 8000)>"
825 : : "}"
826 : : };
827 : :
828 : 2 : gt_dbus_queue_set_server_func (fixture->queue, get_session_limits_server_cb,
829 : : (gpointer) &get_session_limits_data);
830 : :
831 [ + + ]: 2 : if (test_async)
832 : : {
833 : 2 : g_autoptr(GAsyncResult) result = NULL;
834 : :
835 : 1 : mct_manager_get_session_limits_async (fixture->manager,
836 : : fixture->valid_uid,
837 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
838 : : async_result_cb, &result);
839 : :
840 [ + + ]: 4 : while (result == NULL)
841 : 3 : g_main_context_iteration (NULL, TRUE);
842 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result, &local_error);
843 : : }
844 : : else
845 : : {
846 : 1 : session_limits = mct_manager_get_session_limits (fixture->manager,
847 : : fixture->valid_uid,
848 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
849 : : &local_error);
850 : : }
851 : :
852 : 2 : g_assert_no_error (local_error);
853 : 2 : g_assert_nonnull (session_limits);
854 : :
855 : : /* Check the session limits properties. */
856 : 2 : g_assert_cmpuint (mct_session_limits_get_user_id (session_limits), ==, fixture->valid_uid);
857 : 2 : g_assert_true (mct_session_limits_is_enabled (session_limits));
858 : 4 : g_autoptr(GDateTime) now_dt_local1 = g_date_time_new_from_unix_utc (0);
859 : 2 : g_assert_false (mct_session_limits_check_time_remaining (session_limits, now_dt_local1, 0,
860 : : &time_remaining_secs, &time_limit_enabled));
861 : 2 : g_assert_true (time_limit_enabled);
862 : :
863 : 4 : g_autoptr(GDateTime) now_dt_local2 = g_date_time_new_from_unix_utc (2000);
864 : 2 : g_assert_true (mct_session_limits_check_time_remaining (session_limits, now_dt_local2, 0,
865 : : &time_remaining_secs, &time_limit_enabled));
866 : 2 : g_assert_cmpuint (time_remaining_secs, ==, 8000 - 2000);
867 : 2 : g_assert_true (time_limit_enabled);
868 : 2 : }
869 : :
870 : : /* Test that getting an #MctSessionLimits from the mock D-Bus service works. The
871 : : * @test_data is a boolean value indicating whether to do the call
872 : : * synchronously (%FALSE) or asynchronously (%TRUE).
873 : : *
874 : : * The mock D-Bus replies are generated in get_session_limits_server_cb(), which
875 : : * is used for both synchronous and asynchronous calls. */
876 : : static void
877 : 1 : test_session_limits_bus_get_none (BusFixture *fixture,
878 : : gconstpointer test_data)
879 : : {
880 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
881 : 1 : g_autoptr(GError) local_error = NULL;
882 : : guint64 time_remaining_secs;
883 : : gboolean time_limit_enabled;
884 : 1 : gboolean test_async = GPOINTER_TO_UINT (test_data);
885 : 1 : const GetSessionLimitsData get_session_limits_data =
886 : : {
887 : 1 : .expected_uid = fixture->valid_uid,
888 : : .properties = "{"
889 : : "'LimitType': <@u 0>,"
890 : : "'DailySchedule': <(@u 0, @u 86400)>"
891 : : "}"
892 : : };
893 : :
894 : 1 : gt_dbus_queue_set_server_func (fixture->queue, get_session_limits_server_cb,
895 : : (gpointer) &get_session_limits_data);
896 : :
897 [ - + ]: 1 : if (test_async)
898 : : {
899 : 0 : g_autoptr(GAsyncResult) result = NULL;
900 : :
901 : 0 : mct_manager_get_session_limits_async (fixture->manager,
902 : : fixture->valid_uid,
903 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
904 : : async_result_cb, &result);
905 : :
906 [ # # ]: 0 : while (result == NULL)
907 : 0 : g_main_context_iteration (NULL, TRUE);
908 : 0 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result, &local_error);
909 : : }
910 : : else
911 : : {
912 : 1 : session_limits = mct_manager_get_session_limits (fixture->manager,
913 : : fixture->valid_uid,
914 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
915 : : &local_error);
916 : : }
917 : :
918 : 1 : g_assert_no_error (local_error);
919 : 1 : g_assert_nonnull (session_limits);
920 : :
921 : : /* Check the session limits properties. */
922 : 1 : g_assert_cmpuint (mct_session_limits_get_user_id (session_limits), ==, fixture->valid_uid);
923 : 1 : g_assert_false (mct_session_limits_is_enabled (session_limits));
924 : :
925 : 2 : g_autoptr(GDateTime) now_dt_local1 = g_date_time_new_from_unix_utc (0);
926 : 1 : g_assert_true (mct_session_limits_check_time_remaining (session_limits, now_dt_local1, 0,
927 : : &time_remaining_secs, &time_limit_enabled));
928 : 1 : g_assert_false (time_limit_enabled);
929 : :
930 : 2 : g_autoptr(GDateTime) now_dt_local2 = g_date_time_new_from_unix_utc (2000);
931 : 1 : g_assert_true (mct_session_limits_check_time_remaining (session_limits, now_dt_local2, 0,
932 : : &time_remaining_secs, &time_limit_enabled));
933 : 1 : g_assert_false (time_limit_enabled);
934 : 1 : }
935 : :
936 : : /* Test that mct_manager_get_session_limits() returns an appropriate error if the
937 : : * mock D-Bus service reports that the given user cannot be found.
938 : : *
939 : : * The mock D-Bus replies are generated inline. */
940 : : static void
941 : 1 : test_session_limits_bus_get_error_invalid_user (BusFixture *fixture,
942 : : gconstpointer test_data)
943 : : {
944 : 1 : g_autoptr(GAsyncResult) result = NULL;
945 : 1 : g_autoptr(GError) local_error = NULL;
946 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
947 : 1 : g_autofree gchar *error_message = NULL;
948 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
949 : :
950 : 1 : mct_manager_get_session_limits_async (fixture->manager,
951 : : fixture->missing_uid,
952 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
953 : : async_result_cb, &result);
954 : :
955 : : /* Handle the FindUserById() call and claim the user doesn’t exist. */
956 : : gint64 user_id;
957 : 1 : invocation =
958 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
959 : : "/org/freedesktop/Accounts",
960 : : "org.freedesktop.Accounts",
961 : : "FindUserById", "(x)", &user_id);
962 : 1 : g_assert_cmpint (user_id, ==, fixture->missing_uid);
963 : :
964 : 1 : error_message = g_strdup_printf ("Failed to look up user with uid %u.", fixture->missing_uid);
965 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
966 : : "org.freedesktop.Accounts.Error.Failed",
967 : : error_message);
968 : :
969 : : /* Get the get_session_limits() result. */
970 [ + + ]: 2 : while (result == NULL)
971 : 1 : g_main_context_iteration (NULL, TRUE);
972 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
973 : : &local_error);
974 : :
975 : 1 : g_assert_error (local_error,
976 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER);
977 : 1 : g_assert_null (session_limits);
978 : 1 : }
979 : :
980 : : /* Test that mct_manager_get_session_limits() returns an appropriate error if the
981 : : * mock D-Bus service reports that the properties of the given user can’t be
982 : : * accessed due to permissions.
983 : : *
984 : : * The mock D-Bus replies are generated inline. */
985 : : static void
986 : 1 : test_session_limits_bus_get_error_permission_denied (BusFixture *fixture,
987 : : gconstpointer test_data)
988 : : {
989 : 1 : g_autoptr(GAsyncResult) result = NULL;
990 : 1 : g_autoptr(GError) local_error = NULL;
991 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
992 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
993 : 1 : g_autofree gchar *object_path = NULL;
994 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
995 : :
996 : 1 : mct_manager_get_session_limits_async (fixture->manager,
997 : : fixture->valid_uid,
998 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
999 : : async_result_cb, &result);
1000 : :
1001 : : /* Handle the FindUserById() call. */
1002 : : gint64 user_id;
1003 : 1 : invocation1 =
1004 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1005 : : "/org/freedesktop/Accounts",
1006 : : "org.freedesktop.Accounts",
1007 : : "FindUserById", "(x)", &user_id);
1008 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1009 : :
1010 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1011 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1012 : :
1013 : : /* Handle the Properties.GetAll() call and return a permission denied error. */
1014 : : const gchar *property_interface;
1015 : 1 : invocation2 =
1016 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1017 : : object_path,
1018 : : "org.freedesktop.DBus.Properties",
1019 : : "GetAll", "(&s)", &property_interface);
1020 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
1021 : :
1022 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
1023 : : "org.freedesktop.Accounts.Error.PermissionDenied",
1024 : : "Not authorized");
1025 : :
1026 : : /* Get the get_session_limits() result. */
1027 [ + + ]: 2 : while (result == NULL)
1028 : 1 : g_main_context_iteration (NULL, TRUE);
1029 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
1030 : : &local_error);
1031 : :
1032 : 1 : g_assert_error (local_error,
1033 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1034 : 1 : g_assert_null (session_limits);
1035 : 1 : }
1036 : :
1037 : : /* Test that mct_manager_get_session_limits() returns an appropriate error if
1038 : : * the mock D-Bus service replies with no session limits properties (implying
1039 : : * that it hasn’t sent the property values because of permissions).
1040 : : *
1041 : : * The mock D-Bus replies are generated inline. */
1042 : : static void
1043 : 1 : test_session_limits_bus_get_error_permission_denied_missing (BusFixture *fixture,
1044 : : gconstpointer test_data)
1045 : : {
1046 : 1 : g_autoptr(GAsyncResult) result = NULL;
1047 : 1 : g_autoptr(GError) local_error = NULL;
1048 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
1049 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
1050 : 1 : g_autofree gchar *object_path = NULL;
1051 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1052 : :
1053 : 1 : mct_manager_get_session_limits_async (fixture->manager,
1054 : : fixture->valid_uid,
1055 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1056 : : async_result_cb, &result);
1057 : :
1058 : : /* Handle the FindUserById() call. */
1059 : : gint64 user_id;
1060 : 1 : invocation1 =
1061 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1062 : : "/org/freedesktop/Accounts",
1063 : : "org.freedesktop.Accounts",
1064 : : "FindUserById", "(x)", &user_id);
1065 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1066 : :
1067 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1068 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1069 : :
1070 : : /* Handle the Properties.GetAll() call and return an empty array due to not
1071 : : * having permission to access the properties. The code actually keys off the
1072 : : * presence of the LimitType property, since that was the first one to be
1073 : : * added. */
1074 : : const gchar *property_interface;
1075 : 1 : invocation2 =
1076 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1077 : : object_path,
1078 : : "org.freedesktop.DBus.Properties",
1079 : : "GetAll", "(&s)", &property_interface);
1080 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
1081 : :
1082 : 1 : g_dbus_method_invocation_return_value (invocation2, g_variant_new ("(a{sv})", NULL));
1083 : :
1084 : : /* Get the get_session_limits() result. */
1085 [ + + ]: 2 : while (result == NULL)
1086 : 1 : g_main_context_iteration (NULL, TRUE);
1087 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
1088 : : &local_error);
1089 : :
1090 : 1 : g_assert_error (local_error,
1091 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1092 : 1 : g_assert_null (session_limits);
1093 : 1 : }
1094 : :
1095 : : /* Test that mct_manager_get_session_limits() returns an error if the mock D-Bus
1096 : : * service reports an unrecognised error.
1097 : : *
1098 : : * The mock D-Bus replies are generated inline. */
1099 : : static void
1100 : 1 : test_session_limits_bus_get_error_unknown (BusFixture *fixture,
1101 : : gconstpointer test_data)
1102 : : {
1103 : 1 : g_autoptr(GAsyncResult) result = NULL;
1104 : 1 : g_autoptr(GError) local_error = NULL;
1105 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
1106 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1107 : :
1108 : 1 : mct_manager_get_session_limits_async (fixture->manager,
1109 : : fixture->valid_uid,
1110 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1111 : : async_result_cb, &result);
1112 : :
1113 : : /* Handle the FindUserById() call and return a bogus error. */
1114 : : gint64 user_id;
1115 : 1 : invocation =
1116 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1117 : : "/org/freedesktop/Accounts",
1118 : : "org.freedesktop.Accounts",
1119 : : "FindUserById", "(x)", &user_id);
1120 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1121 : :
1122 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
1123 : : "org.freedesktop.Accounts.Error.NewAndInterestingError",
1124 : : "This is a fake error message "
1125 : : "which libmalcontent "
1126 : : "will never have seen before, "
1127 : : "but must still handle correctly");
1128 : :
1129 : : /* Get the get_session_limits() result. */
1130 [ + + ]: 3 : while (result == NULL)
1131 : 2 : g_main_context_iteration (NULL, TRUE);
1132 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
1133 : : &local_error);
1134 : :
1135 : : /* We don’t actually care what error is actually used here. */
1136 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
1137 : 1 : g_assert_null (session_limits);
1138 : 1 : }
1139 : :
1140 : : /* Test that mct_manager_get_session_limits() returns an error if the mock D-Bus
1141 : : * service reports an unknown interface, which means that parental controls are
1142 : : * not installed properly.
1143 : : *
1144 : : * The mock D-Bus replies are generated inline. */
1145 : : static void
1146 : 1 : test_session_limits_bus_get_error_disabled (BusFixture *fixture,
1147 : : gconstpointer test_data)
1148 : : {
1149 : 1 : g_autoptr(GAsyncResult) result = NULL;
1150 : 1 : g_autoptr(GError) local_error = NULL;
1151 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
1152 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
1153 : 1 : g_autofree gchar *object_path = NULL;
1154 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1155 : :
1156 : 1 : mct_manager_get_session_limits_async (fixture->manager,
1157 : : fixture->valid_uid,
1158 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1159 : : async_result_cb, &result);
1160 : :
1161 : : /* Handle the FindUserById() call. */
1162 : : gint64 user_id;
1163 : 1 : invocation1 =
1164 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1165 : : "/org/freedesktop/Accounts",
1166 : : "org.freedesktop.Accounts",
1167 : : "FindUserById", "(x)", &user_id);
1168 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1169 : :
1170 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1171 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1172 : :
1173 : : /* Handle the Properties.GetAll() call and return an InvalidArgs error. */
1174 : : const gchar *property_interface;
1175 : 1 : invocation2 =
1176 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1177 : : object_path,
1178 : : "org.freedesktop.DBus.Properties",
1179 : : "GetAll", "(&s)", &property_interface);
1180 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
1181 : :
1182 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
1183 : : "org.freedesktop.DBus.Error.InvalidArgs",
1184 : : "No such interface "
1185 : : "“com.endlessm.ParentalControls.SessionLimits”");
1186 : :
1187 : : /* Get the get_session_limits() result. */
1188 [ + + ]: 2 : while (result == NULL)
1189 : 1 : g_main_context_iteration (NULL, TRUE);
1190 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
1191 : : &local_error);
1192 : :
1193 : 1 : g_assert_error (local_error,
1194 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_DISABLED);
1195 : 1 : g_assert_null (session_limits);
1196 : 1 : }
1197 : :
1198 : : /* Test that mct_manager_get_session_limits() returns an error if the mock D-Bus
1199 : : * service reports an unknown interface (via a different kind of error), which
1200 : : * means that parental controls are not installed properly.
1201 : : *
1202 : : * The mock D-Bus replies are generated inline. */
1203 : : static void
1204 : 1 : test_session_limits_bus_get_error_disabled2 (BusFixture *fixture,
1205 : : gconstpointer test_data)
1206 : : {
1207 : 1 : g_autoptr(GAsyncResult) result = NULL;
1208 : 1 : g_autoptr(GError) local_error = NULL;
1209 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
1210 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
1211 : 1 : g_autofree gchar *object_path = NULL;
1212 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1213 : :
1214 : 1 : mct_manager_get_session_limits_async (fixture->manager,
1215 : : fixture->valid_uid,
1216 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1217 : : async_result_cb, &result);
1218 : :
1219 : : /* Handle the FindUserById() call. */
1220 : : gint64 user_id;
1221 : 1 : invocation1 =
1222 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1223 : : "/org/freedesktop/Accounts",
1224 : : "org.freedesktop.Accounts",
1225 : : "FindUserById", "(x)", &user_id);
1226 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1227 : :
1228 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1229 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1230 : :
1231 : : /* Handle the Properties.GetAll() call and return an InvalidArgs error. */
1232 : : const gchar *property_interface;
1233 : 1 : invocation2 =
1234 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1235 : : object_path,
1236 : : "org.freedesktop.DBus.Properties",
1237 : : "GetAll", "(&s)", &property_interface);
1238 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
1239 : :
1240 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
1241 : : "org.freedesktop.Accounts.User.UnknownInterface",
1242 : : "no such interface com.endlessm.ParentalControls.SessionLimits");
1243 : :
1244 : : /* Get the get_session_limits() result. */
1245 [ + + ]: 2 : while (result == NULL)
1246 : 1 : g_main_context_iteration (NULL, TRUE);
1247 : 1 : session_limits = mct_manager_get_session_limits_finish (fixture->manager, result,
1248 : : &local_error);
1249 : :
1250 : 1 : g_assert_error (local_error,
1251 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_DISABLED);
1252 : 1 : g_assert_null (session_limits);
1253 : 1 : }
1254 : :
1255 : : /* Generic mock accountsservice implementation which handles properties being
1256 : : * set on a mock User object, and compares their values to the given
1257 : : * `expected_*` ones.
1258 : : *
1259 : : * If @error_index is non-negative, it gives the index of a Set() call to return
1260 : : * the given @dbus_error_name and @dbus_error_message from, rather than
1261 : : * accepting the property value from the caller. If @error_index is negative,
1262 : : * all Set() calls will be accepted. */
1263 : : typedef struct
1264 : : {
1265 : : uid_t expected_uid;
1266 : :
1267 : : const gchar * const *expected_properties;
1268 : :
1269 : : /* All GVariants in text format: */
1270 : : const gchar *expected_limit_type_value; /* (nullable) */
1271 : : const gchar *expected_daily_schedule_value; /* (nullable) */
1272 : :
1273 : : gint error_index; /* -1 to return no error */
1274 : : const gchar *dbus_error_name; /* NULL to return no error */
1275 : : const gchar *dbus_error_message; /* NULL to return no error */
1276 : : } SetSessionLimitsData;
1277 : :
1278 : : static const gchar *
1279 : 5 : set_session_limits_data_get_expected_property_value (const SetSessionLimitsData *data,
1280 : : const gchar *property_name)
1281 : : {
1282 [ + + ]: 5 : if (g_str_equal (property_name, "LimitType"))
1283 : 2 : return data->expected_limit_type_value;
1284 [ + - ]: 3 : else if (g_str_equal (property_name, "DailySchedule"))
1285 : 3 : return data->expected_daily_schedule_value;
1286 : : else
1287 : : g_assert_not_reached ();
1288 : : }
1289 : :
1290 : : /* This is run in a worker thread. */
1291 : : static void
1292 : 6 : set_session_limits_server_cb (GtDBusQueue *queue,
1293 : : gpointer user_data)
1294 : : {
1295 : 6 : const SetSessionLimitsData *data = user_data;
1296 : 6 : g_autoptr(GDBusMethodInvocation) find_invocation = NULL;
1297 : 6 : g_autofree gchar *object_path = NULL;
1298 : :
1299 : 6 : g_assert ((data->error_index == -1) == (data->dbus_error_name == NULL));
1300 : 6 : g_assert ((data->dbus_error_name == NULL) == (data->dbus_error_message == NULL));
1301 : :
1302 : : /* Handle the FindUserById() call. */
1303 : : gint64 user_id;
1304 : 6 : find_invocation =
1305 : 6 : gt_dbus_queue_assert_pop_message (queue,
1306 : : "/org/freedesktop/Accounts",
1307 : : "org.freedesktop.Accounts",
1308 : : "FindUserById", "(x)", &user_id);
1309 : 6 : g_assert_cmpint (user_id, ==, data->expected_uid);
1310 : :
1311 : 6 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1312 : 6 : g_dbus_method_invocation_return_value (find_invocation, g_variant_new ("(o)", object_path));
1313 : :
1314 : : /* Handle the Properties.Set() calls. */
1315 : : gsize i;
1316 : :
1317 [ + + ]: 11 : for (i = 0; data->expected_properties[i] != NULL; i++)
1318 : : {
1319 : : const gchar *property_interface;
1320 : : const gchar *property_name;
1321 [ + + ]: 9 : g_autoptr(GVariant) property_value = NULL;
1322 [ + + ]: 9 : g_autoptr(GDBusMethodInvocation) property_invocation = NULL;
1323 [ + + ]: 9 : g_autoptr(GVariant) expected_property_value = NULL;
1324 : :
1325 : 9 : property_invocation =
1326 : 9 : gt_dbus_queue_assert_pop_message (queue,
1327 : : object_path,
1328 : : "org.freedesktop.DBus.Properties",
1329 : : "Set", "(&s&sv)", &property_interface,
1330 : : &property_name, &property_value);
1331 : 9 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.SessionLimits");
1332 : 9 : g_assert_cmpstr (property_name, ==, data->expected_properties[i]);
1333 : :
1334 [ + + + + ]: 9 : if (data->error_index >= 0 && (gsize) data->error_index == i)
1335 : : {
1336 : 4 : g_dbus_method_invocation_return_dbus_error (property_invocation,
1337 : 4 : data->dbus_error_name,
1338 : 4 : data->dbus_error_message);
1339 : 4 : break;
1340 : : }
1341 : : else
1342 : : {
1343 : 5 : expected_property_value = g_variant_new_parsed (set_session_limits_data_get_expected_property_value (data, property_name));
1344 : 5 : g_assert_cmpvariant (property_value, expected_property_value);
1345 : :
1346 : 5 : g_dbus_method_invocation_return_value (property_invocation, NULL);
1347 : : }
1348 : : }
1349 : 6 : }
1350 : :
1351 : : /* Test that setting an #MctSessionLimits on the mock D-Bus service works. The
1352 : : * @test_data is a boolean value indicating whether to do the call
1353 : : * synchronously (%FALSE) or asynchronously (%TRUE).
1354 : : *
1355 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(), which
1356 : : * is used for both synchronous and asynchronous calls. */
1357 : : static void
1358 : 2 : test_session_limits_bus_set (BusFixture *fixture,
1359 : : gconstpointer test_data)
1360 : : {
1361 : : gboolean success;
1362 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1363 : 2 : g_autoptr(MctSessionLimits) session_limits = NULL;
1364 : 2 : g_autoptr(GError) local_error = NULL;
1365 : 2 : gboolean test_async = GPOINTER_TO_UINT (test_data);
1366 : 2 : const gchar *expected_properties[] =
1367 : : {
1368 : : "DailySchedule",
1369 : : "LimitType",
1370 : : NULL
1371 : : };
1372 : 2 : const SetSessionLimitsData set_session_limits_data =
1373 : : {
1374 : 2 : .expected_uid = fixture->valid_uid,
1375 : : .expected_properties = expected_properties,
1376 : : .expected_limit_type_value = "@u 1",
1377 : : .expected_daily_schedule_value = "(@u 100, @u 4000)",
1378 : : .error_index = -1,
1379 : : };
1380 : :
1381 : : /* Build a session limits object. */
1382 : 2 : mct_session_limits_builder_set_daily_schedule (&builder, TRUE, 100, 4000);
1383 : :
1384 : 2 : session_limits = mct_session_limits_builder_end (&builder);
1385 : :
1386 : : /* Set the mock service function and set the limits. */
1387 : 2 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1388 : : (gpointer) &set_session_limits_data);
1389 : :
1390 [ + + ]: 2 : if (test_async)
1391 : : {
1392 : 1 : g_autoptr(GAsyncResult) result = NULL;
1393 : :
1394 : 1 : mct_manager_set_session_limits_async (fixture->manager,
1395 : : fixture->valid_uid, session_limits,
1396 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1397 : : async_result_cb, &result);
1398 : :
1399 [ + + ]: 5 : while (result == NULL)
1400 : 4 : g_main_context_iteration (NULL, TRUE);
1401 : 1 : success = mct_manager_set_session_limits_finish (fixture->manager, result,
1402 : : &local_error);
1403 : : }
1404 : : else
1405 : : {
1406 : 1 : success = mct_manager_set_session_limits (fixture->manager,
1407 : : fixture->valid_uid, session_limits,
1408 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1409 : : &local_error);
1410 : : }
1411 : :
1412 : 2 : g_assert_no_error (local_error);
1413 : 2 : g_assert_true (success);
1414 : 2 : }
1415 : :
1416 : : /* Test that mct_manager_set_session_limits() returns an appropriate error if
1417 : : * the mock D-Bus service reports that the given user cannot be found.
1418 : : *
1419 : : * The mock D-Bus replies are generated inline. */
1420 : : static void
1421 : 1 : test_session_limits_bus_set_error_invalid_user (BusFixture *fixture,
1422 : : gconstpointer test_data)
1423 : : {
1424 : : gboolean success;
1425 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1426 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1427 : 1 : g_autoptr(GAsyncResult) result = NULL;
1428 : 1 : g_autoptr(GError) local_error = NULL;
1429 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
1430 : 1 : g_autofree gchar *error_message = NULL;
1431 : :
1432 : : /* Use the default session limits. */
1433 : 1 : session_limits = mct_session_limits_builder_end (&builder);
1434 : :
1435 : 1 : mct_manager_set_session_limits_async (fixture->manager,
1436 : : fixture->missing_uid, session_limits,
1437 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1438 : : async_result_cb, &result);
1439 : :
1440 : : /* Handle the FindUserById() call and claim the user doesn’t exist. */
1441 : : gint64 user_id;
1442 : 1 : invocation =
1443 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1444 : : "/org/freedesktop/Accounts",
1445 : : "org.freedesktop.Accounts",
1446 : : "FindUserById", "(x)", &user_id);
1447 : 1 : g_assert_cmpint (user_id, ==, fixture->missing_uid);
1448 : :
1449 : 1 : error_message = g_strdup_printf ("Failed to look up user with uid %u.", fixture->missing_uid);
1450 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
1451 : : "org.freedesktop.Accounts.Error.Failed",
1452 : : error_message);
1453 : :
1454 : : /* Get the set_session_limits() result. */
1455 [ + + ]: 2 : while (result == NULL)
1456 : 1 : g_main_context_iteration (NULL, TRUE);
1457 : 1 : success = mct_manager_set_session_limits_finish (fixture->manager, result,
1458 : : &local_error);
1459 : :
1460 : 1 : g_assert_error (local_error,
1461 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER);
1462 : 1 : g_assert_false (success);
1463 : 1 : }
1464 : :
1465 : : /* Test that mct_manager_set_session_limits() returns an appropriate error if the
1466 : : * mock D-Bus service replies with a permission denied error when setting
1467 : : * properties.
1468 : : *
1469 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(). */
1470 : : static void
1471 : 1 : test_session_limits_bus_set_error_permission_denied (BusFixture *fixture,
1472 : : gconstpointer test_data)
1473 : : {
1474 : : gboolean success;
1475 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1476 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1477 : 1 : g_autoptr(GError) local_error = NULL;
1478 : 1 : const gchar *expected_properties[] =
1479 : : {
1480 : : "LimitType",
1481 : : NULL
1482 : : };
1483 : 1 : const SetSessionLimitsData set_session_limits_data =
1484 : : {
1485 : 1 : .expected_uid = fixture->valid_uid,
1486 : : .expected_properties = expected_properties,
1487 : : .error_index = 0,
1488 : : .dbus_error_name = "org.freedesktop.Accounts.Error.PermissionDenied",
1489 : : .dbus_error_message = "Not authorized",
1490 : : };
1491 : :
1492 : : /* Use the default session limits. */
1493 : 1 : session_limits = mct_session_limits_builder_end (&builder);
1494 : :
1495 : 1 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1496 : : (gpointer) &set_session_limits_data);
1497 : :
1498 : 1 : success = mct_manager_set_session_limits (fixture->manager,
1499 : : fixture->valid_uid, session_limits,
1500 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1501 : : &local_error);
1502 : :
1503 : 1 : g_assert_error (local_error,
1504 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1505 : 1 : g_assert_false (success);
1506 : 1 : }
1507 : :
1508 : : /* Test that mct_manager_set_session_limits() returns an error if the mock D-Bus
1509 : : * service reports an unrecognised error.
1510 : : *
1511 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(). */
1512 : : static void
1513 : 1 : test_session_limits_bus_set_error_unknown (BusFixture *fixture,
1514 : : gconstpointer test_data)
1515 : : {
1516 : : gboolean success;
1517 : 1 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1518 : 1 : g_autoptr(MctSessionLimits) session_limits = NULL;
1519 : 1 : g_autoptr(GError) local_error = NULL;
1520 : 1 : const gchar *expected_properties[] =
1521 : : {
1522 : : "LimitType",
1523 : : NULL
1524 : : };
1525 : 1 : const SetSessionLimitsData set_session_limits_data =
1526 : : {
1527 : 1 : .expected_uid = fixture->valid_uid,
1528 : : .expected_properties = expected_properties,
1529 : : .error_index = 0,
1530 : : .dbus_error_name = "org.freedesktop.Accounts.Error.NewAndInterestingError",
1531 : : .dbus_error_message = "This is a fake error message which "
1532 : : "libmalcontent will never have seen "
1533 : : "before, but must still handle correctly",
1534 : : };
1535 : :
1536 : : /* Use the default session limits. */
1537 : 1 : session_limits = mct_session_limits_builder_end (&builder);
1538 : :
1539 : 1 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1540 : : (gpointer) &set_session_limits_data);
1541 : :
1542 : 1 : success = mct_manager_set_session_limits (fixture->manager,
1543 : : fixture->valid_uid, session_limits,
1544 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1545 : : &local_error);
1546 : :
1547 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
1548 : 1 : g_assert_false (success);
1549 : 1 : }
1550 : :
1551 : : /* Test that mct_manager_set_session_limits() returns an error if the mock D-Bus
1552 : : * service reports an InvalidArgs error with a given one of its Set() calls.
1553 : : *
1554 : : * @test_data contains a property index encoded with GINT_TO_POINTER(),
1555 : : * indicating which Set() call to return the error on, since the calls are made
1556 : : * in series.
1557 : : *
1558 : : * The mock D-Bus replies are generated in set_session_limits_server_cb(). */
1559 : : static void
1560 : 2 : test_session_limits_bus_set_error_invalid_property (BusFixture *fixture,
1561 : : gconstpointer test_data)
1562 : : {
1563 : : gboolean success;
1564 : 2 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
1565 : 2 : g_autoptr(MctSessionLimits) session_limits = NULL;
1566 : 2 : g_autoptr(GError) local_error = NULL;
1567 : 2 : const gchar *expected_properties[] =
1568 : : {
1569 : : "DailySchedule",
1570 : : "LimitType",
1571 : : NULL
1572 : : };
1573 : 2 : const SetSessionLimitsData set_session_limits_data =
1574 : : {
1575 : 2 : .expected_uid = fixture->valid_uid,
1576 : : .expected_properties = expected_properties,
1577 : : .expected_limit_type_value = "@u 1",
1578 : : .expected_daily_schedule_value = "(@u 100, @u 3000)",
1579 : 2 : .error_index = GPOINTER_TO_INT (test_data),
1580 : : .dbus_error_name = "org.freedesktop.DBus.Error.InvalidArgs",
1581 : : .dbus_error_message = "Mumble mumble something wrong with the limits value",
1582 : : };
1583 : :
1584 : : /* Build a session limits object. */
1585 : 2 : mct_session_limits_builder_set_daily_schedule (&builder, TRUE, 100, 3000);
1586 : :
1587 : 2 : session_limits = mct_session_limits_builder_end (&builder);
1588 : :
1589 : 2 : gt_dbus_queue_set_server_func (fixture->queue, set_session_limits_server_cb,
1590 : : (gpointer) &set_session_limits_data);
1591 : :
1592 : 2 : success = mct_manager_set_session_limits (fixture->manager,
1593 : : fixture->valid_uid, session_limits,
1594 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1595 : : &local_error);
1596 : :
1597 : 2 : g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
1598 : 2 : g_assert_false (success);
1599 : 2 : }
1600 : :
1601 : : int
1602 : 3 : main (int argc,
1603 : : char **argv)
1604 : : {
1605 : 3 : setlocale (LC_ALL, "");
1606 : 3 : g_test_init (&argc, &argv, NULL);
1607 : :
1608 : 3 : g_test_add_func ("/session-limits/types", test_session_limits_types);
1609 : 3 : g_test_add_func ("/session-limits/refs", test_session_limits_refs);
1610 : 3 : g_test_add_func ("/session-limits/check-time-remaining/invalid-time",
1611 : : test_session_limits_check_time_remaining_invalid_time);
1612 : 3 : g_test_add_func ("/session-limits/check-time-remaining/daily-schedule",
1613 : : test_session_limits_check_time_remaining_daily_schedule);
1614 : 3 : g_test_add_func ("/session-limits/check-time-remaining/daily-schedule/timezone",
1615 : : test_session_limits_check_time_remaining_daily_schedule_timezone);
1616 : 3 : g_test_add_func ("/session-limits/check-time-remaining/daily-limit",
1617 : : test_session_limits_check_time_remaining_daily_limit);
1618 : 3 : g_test_add_func ("/session-limits/check-time-remaining/multiple",
1619 : : test_session_limits_check_time_remaining_multiple);
1620 : :
1621 : 3 : g_test_add_func ("/session-limits/serialize", test_session_limits_serialize);
1622 : 3 : g_test_add_func ("/session-limits/deserialize", test_session_limits_deserialize);
1623 : 3 : g_test_add_func ("/session-limits/deserialize/invalid", test_session_limits_deserialize_invalid);
1624 : 3 : g_test_add_func ("/session-limits/deserialize/set-limits-type", test_session_limits_deserialize_set_limits_type);
1625 : :
1626 : 3 : g_test_add_func ("/session-limits/equal", test_session_limits_equal);
1627 : :
1628 : 3 : g_test_add ("/session-limits/builder/stack/non-empty", BuilderFixture, NULL,
1629 : : builder_set_up_stack, test_session_limits_builder_non_empty,
1630 : : builder_tear_down_stack);
1631 : 3 : g_test_add ("/session-limits/builder/stack/empty", BuilderFixture, NULL,
1632 : : builder_set_up_stack, test_session_limits_builder_empty,
1633 : : builder_tear_down_stack);
1634 : 3 : g_test_add ("/session-limits/builder/stack2/non-empty", BuilderFixture, NULL,
1635 : : builder_set_up_stack2, test_session_limits_builder_non_empty,
1636 : : builder_tear_down_stack2);
1637 : 3 : g_test_add ("/session-limits/builder/stack2/empty", BuilderFixture, NULL,
1638 : : builder_set_up_stack2, test_session_limits_builder_empty,
1639 : : builder_tear_down_stack2);
1640 : 3 : g_test_add ("/session-limits/builder/heap/non-empty", BuilderFixture, NULL,
1641 : : builder_set_up_heap, test_session_limits_builder_non_empty,
1642 : : builder_tear_down_heap);
1643 : 3 : g_test_add ("/session-limits/builder/heap/empty", BuilderFixture, NULL,
1644 : : builder_set_up_heap, test_session_limits_builder_empty,
1645 : : builder_tear_down_heap);
1646 : 3 : g_test_add_func ("/session-limits/builder/copy/empty",
1647 : : test_session_limits_builder_copy_empty);
1648 : 3 : g_test_add_func ("/session-limits/builder/copy/full",
1649 : : test_session_limits_builder_copy_full);
1650 : 3 : g_test_add_func ("/session-limits/builder/override/none",
1651 : : test_session_limits_builder_override_none);
1652 : 3 : g_test_add_func ("/session-limits/builder/override/daily-schedule",
1653 : : test_session_limits_builder_override_daily_schedule);
1654 : 3 : g_test_add_func ("/session-limits/builder/override/daily-limit",
1655 : : test_session_limits_builder_override_daily_limit);
1656 : :
1657 : 3 : g_test_add ("/session-limits/bus/get/async", BusFixture, GUINT_TO_POINTER (TRUE),
1658 : : bus_set_up, test_session_limits_bus_get, bus_tear_down);
1659 : 3 : g_test_add ("/session-limits/bus/get/sync", BusFixture, GUINT_TO_POINTER (FALSE),
1660 : : bus_set_up, test_session_limits_bus_get, bus_tear_down);
1661 : 3 : g_test_add ("/session-limits/bus/get/none", BusFixture, NULL,
1662 : : bus_set_up, test_session_limits_bus_get_none, bus_tear_down);
1663 : :
1664 : 3 : g_test_add ("/session-limits/bus/get/error/invalid-user", BusFixture, NULL,
1665 : : bus_set_up, test_session_limits_bus_get_error_invalid_user, bus_tear_down);
1666 : 3 : g_test_add ("/session-limits/bus/get/error/permission-denied", BusFixture, NULL,
1667 : : bus_set_up, test_session_limits_bus_get_error_permission_denied, bus_tear_down);
1668 : 3 : g_test_add ("/session-limits/bus/get/error/permission-denied-missing", BusFixture, NULL,
1669 : : bus_set_up, test_session_limits_bus_get_error_permission_denied_missing, bus_tear_down);
1670 : 3 : g_test_add ("/session-limits/bus/get/error/unknown", BusFixture, NULL,
1671 : : bus_set_up, test_session_limits_bus_get_error_unknown, bus_tear_down);
1672 : 3 : g_test_add ("/session-limits/bus/get/error/disabled", BusFixture, NULL,
1673 : : bus_set_up, test_session_limits_bus_get_error_disabled, bus_tear_down);
1674 : 3 : g_test_add ("/session-limits/bus/get/error/disabled2", BusFixture, NULL,
1675 : : bus_set_up, test_session_limits_bus_get_error_disabled2, bus_tear_down);
1676 : :
1677 : 3 : g_test_add ("/session-limits/bus/set/async", BusFixture, GUINT_TO_POINTER (TRUE),
1678 : : bus_set_up, test_session_limits_bus_set, bus_tear_down);
1679 : 3 : g_test_add ("/session-limits/bus/set/sync", BusFixture, GUINT_TO_POINTER (FALSE),
1680 : : bus_set_up, test_session_limits_bus_set, bus_tear_down);
1681 : :
1682 : 3 : g_test_add ("/session-limits/bus/set/error/invalid-user", BusFixture, NULL,
1683 : : bus_set_up, test_session_limits_bus_set_error_invalid_user, bus_tear_down);
1684 : 3 : g_test_add ("/session-limits/bus/set/error/permission-denied", BusFixture, NULL,
1685 : : bus_set_up, test_session_limits_bus_set_error_permission_denied, bus_tear_down);
1686 : 3 : g_test_add ("/session-limits/bus/set/error/unknown", BusFixture, NULL,
1687 : : bus_set_up, test_session_limits_bus_set_error_unknown, bus_tear_down);
1688 : 3 : g_test_add ("/session-limits/bus/set/error/invalid-property/daily-schedule",
1689 : : BusFixture, GINT_TO_POINTER (0), bus_set_up,
1690 : : test_session_limits_bus_set_error_invalid_property, bus_tear_down);
1691 : 3 : g_test_add ("/session-limits/bus/set/error/invalid-property/limit-type",
1692 : : BusFixture, GINT_TO_POINTER (1), bus_set_up,
1693 : : test_session_limits_bus_set_error_invalid_property, bus_tear_down);
1694 : :
1695 : 3 : return g_test_run ();
1696 : : }
|