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