Line | Branch | Exec | Source |
---|---|---|---|
1 | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- | ||
2 | * | ||
3 | * Copyright © 2017, 2018 Endless Mobile, Inc. | ||
4 | * | ||
5 | * SPDX-License-Identifier: LGPL-2.1-or-later | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | * | ||
21 | * Authors: | ||
22 | * - Philip Withnall <withnall@endlessm.com> | ||
23 | */ | ||
24 | |||
25 | #include <glib.h> | ||
26 | #include <glib/gstdio.h> | ||
27 | #include <libgsystemservice/config-file.h> | ||
28 | #include <libgsystemservice/tests/config-file-resources.h> | ||
29 | #include <locale.h> | ||
30 | #include <string.h> | ||
31 | #include <sys/types.h> | ||
32 | #include <unistd.h> | ||
33 | |||
34 | typedef struct | ||
35 | { | ||
36 | gchar *tmp_dir; | ||
37 | gchar *key_file1_path; | ||
38 | gchar *key_file2_path; | ||
39 | gchar *key_file_nonexistent_path; | ||
40 | gchar *key_file_unreadable_path; | ||
41 | gchar *key_file_invalid_path; | ||
42 | |||
43 | GResource *default_resource; /* unowned */ | ||
44 | const gchar *default_path; | ||
45 | const gchar *default_path_invalid; | ||
46 | } Fixture; | ||
47 | |||
48 | /* Set up a temporary directory with various test configuration files in. */ | ||
49 | static void | ||
50 | 9 | setup (Fixture *fixture, | |
51 | gconstpointer user_data G_GNUC_UNUSED) | ||
52 | { | ||
53 | 18 | g_autoptr(GError) error = NULL; | |
54 | |||
55 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (g_test_subprocess ()) |
56 | { | ||
57 | ✗ | const char *tmp_dir = g_getenv ("LIBGSYSTEMSERVICE_TESTS_TMPDIR"); | |
58 | ✗ | g_assert_nonnull (tmp_dir); | |
59 | ✗ | fixture->tmp_dir = g_strdup (tmp_dir); | |
60 | } | ||
61 | else | ||
62 | { | ||
63 | 9 | fixture->tmp_dir = g_dir_make_tmp ("libgsystemservice-tests-config-XXXXXX", &error); | |
64 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | g_assert_no_error (error); |
65 | 9 | g_setenv ("LIBGSYSTEMSERVICE_TESTS_TMPDIR", fixture->tmp_dir, TRUE); | |
66 | } | ||
67 | |||
68 | 9 | fixture->key_file1_path = g_build_filename (fixture->tmp_dir, "key-file1", | |
69 | NULL); | ||
70 | 9 | g_file_set_contents (fixture->key_file1_path, | |
71 | "[Test]\n" | ||
72 | "File=1\n" | ||
73 | "File1=true\n" | ||
74 | "[Group1]\n", | ||
75 | -1, &error); | ||
76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | g_assert_no_error (error); |
77 | |||
78 | 9 | fixture->key_file2_path = g_build_filename (fixture->tmp_dir, "key-file2", | |
79 | NULL); | ||
80 | 9 | g_file_set_contents (fixture->key_file2_path, | |
81 | "[Test]\n" | ||
82 | "File=2\n" | ||
83 | "File2=true\n" | ||
84 | "[Group2]\n", | ||
85 | -1, &error); | ||
86 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | g_assert_no_error (error); |
87 | |||
88 | 9 | fixture->key_file_nonexistent_path = g_build_filename (fixture->tmp_dir, | |
89 | "key-file-nonexistent", | ||
90 | NULL); | ||
91 | |||
92 | 9 | fixture->key_file_unreadable_path = g_build_filename (fixture->tmp_dir, | |
93 | "key-file-unreadable", | ||
94 | NULL); | ||
95 | 9 | g_file_set_contents (fixture->key_file_unreadable_path, "[Test]\nFile=3", | |
96 | -1, &error); | ||
97 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | g_assert_no_error (error); |
98 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | g_assert_cmpint (g_chmod (fixture->key_file_unreadable_path, 0200), ==, 0); |
99 | |||
100 | 9 | fixture->key_file_invalid_path = g_build_filename (fixture->tmp_dir, | |
101 | "key-file-invalid", | ||
102 | NULL); | ||
103 | 9 | g_file_set_contents (fixture->key_file_invalid_path, "really not valid", -1, | |
104 | &error); | ||
105 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | g_assert_no_error (error); |
106 | |||
107 | 9 | fixture->default_resource = config_file_resources_get_resource (); | |
108 | 9 | fixture->default_path = "/org/gnome/libgsystemservice/config/config-test.conf"; | |
109 | 9 | fixture->default_path_invalid = "/org/gnome/libgsystemservice/config/config-test-invalid.conf"; | |
110 | 9 | } | |
111 | |||
112 | static void | ||
113 | 36 | unlink_and_free (gchar **path) | |
114 | { | ||
115 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | if (*path != NULL) |
116 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
|
36 | g_assert_cmpint (g_unlink (*path), ==, 0); |
117 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | g_clear_pointer (path, g_free); |
118 | 36 | } | |
119 | |||
120 | /* Inverse of setup(). */ | ||
121 | static void | ||
122 | 9 | teardown (Fixture *fixture, | |
123 | gconstpointer user_data G_GNUC_UNUSED) | ||
124 | { | ||
125 | 9 | unlink_and_free (&fixture->key_file_invalid_path); | |
126 | 9 | unlink_and_free (&fixture->key_file_unreadable_path); | |
127 | 9 | g_free (fixture->key_file_nonexistent_path); | |
128 | 9 | unlink_and_free (&fixture->key_file2_path); | |
129 | 9 | unlink_and_free (&fixture->key_file1_path); | |
130 | |||
131 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | g_assert_cmpint (g_rmdir (fixture->tmp_dir), ==, 0); |
132 | 9 | g_free (fixture->tmp_dir); | |
133 | 9 | } | |
134 | |||
135 | /* Test that loading a single configuration file works. */ | ||
136 | static void | ||
137 | 1 | test_config_file_load_one (Fixture *fixture, | |
138 | gconstpointer user_data G_GNUC_UNUSED) | ||
139 | { | ||
140 | 1 | g_autoptr(GError) error = NULL; | |
141 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
142 | 1 | const gchar * const paths[] = | |
143 | { | ||
144 | 1 | fixture->key_file1_path, | |
145 | NULL | ||
146 | }; | ||
147 | guint loaded_file; | ||
148 | |||
149 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
150 | |||
151 | 1 | loaded_file = gss_config_file_get_uint (config, "Test", "File", 0, G_MAXUINT, &error); | |
152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
153 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_cmpuint (loaded_file, ==, 1); |
154 | 1 | } | |
155 | |||
156 | /* Test that priority ordering of configuration files works. */ | ||
157 | static void | ||
158 | 1 | test_config_file_load_many (Fixture *fixture, | |
159 | gconstpointer user_data G_GNUC_UNUSED) | ||
160 | { | ||
161 | 1 | g_autoptr(GError) error = NULL; | |
162 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
163 | 1 | const gchar * const paths[] = | |
164 | { | ||
165 | 1 | fixture->key_file1_path, | |
166 | 1 | fixture->key_file2_path, | |
167 | NULL | ||
168 | }; | ||
169 | guint loaded_file; | ||
170 | |||
171 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
172 | |||
173 | 1 | loaded_file = gss_config_file_get_uint (config, "Test", "File", 0, G_MAXUINT, &error); | |
174 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_cmpuint (loaded_file, ==, 1); |
176 | 1 | } | |
177 | |||
178 | /* Test that error reporting from an unreadable file reports an error. */ | ||
179 | static void | ||
180 | 1 | test_config_file_unreadable (Fixture *fixture, | |
181 | gconstpointer user_data G_GNUC_UNUSED) | ||
182 | { | ||
183 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_autoptr(GError) error = NULL; |
184 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_autoptr(GssConfigFile) config = NULL; |
185 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_autofree gchar *temp = NULL; |
186 | 1 | const gchar * const paths[] = | |
187 | { | ||
188 | 1 | fixture->key_file_nonexistent_path, | |
189 | 1 | fixture->key_file_unreadable_path, | |
190 | 1 | fixture->key_file1_path, | |
191 | NULL | ||
192 | }; | ||
193 | |||
194 | /* If the test is run as root (or another user with CAP_DAC_OVERRIDE), the | ||
195 | * user can read any file anyway. */ | ||
196 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (g_file_get_contents (fixture->key_file_unreadable_path, &temp, NULL, NULL)) |
197 | { | ||
198 | 1 | g_test_skip ("Test cannot be run as a user with CAP_DAC_OVERRIDE or " | |
199 | "CAP_DAC_READ_SEARCH."); | ||
200 | 1 | return; | |
201 | } | ||
202 | |||
203 | ✗ | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
204 | |||
205 | ✗ | gss_config_file_get_uint (config, "Any", "Thing", 0, G_MAXUINT, &error); | |
206 | ✗ | g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES); | |
207 | } | ||
208 | |||
209 | /* Test that error reporting from an invalid file reports an error. */ | ||
210 | static void | ||
211 | 1 | test_config_file_invalid (Fixture *fixture, | |
212 | gconstpointer user_data G_GNUC_UNUSED) | ||
213 | { | ||
214 | 1 | g_autoptr(GError) error = NULL; | |
215 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
216 | 1 | const gchar * const paths[] = | |
217 | { | ||
218 | 1 | fixture->key_file_invalid_path, | |
219 | 1 | fixture->key_file1_path, | |
220 | NULL | ||
221 | }; | ||
222 | |||
223 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
224 | |||
225 | 1 | gss_config_file_get_uint (config, "Any", "Thing", 0, G_MAXUINT, &error); | |
226 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
|
1 | g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); |
227 | 1 | } | |
228 | |||
229 | /* Test that multiple non-existent paths are handled correctly. */ | ||
230 | static void | ||
231 | 1 | test_config_file_nonexistent (Fixture *fixture, | |
232 | gconstpointer user_data G_GNUC_UNUSED) | ||
233 | { | ||
234 | 1 | g_autoptr(GError) error = NULL; | |
235 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
236 | 1 | const gchar * const paths[] = | |
237 | { | ||
238 | 1 | fixture->key_file_nonexistent_path, | |
239 | 1 | fixture->key_file_nonexistent_path, | |
240 | 1 | fixture->key_file_nonexistent_path, | |
241 | 1 | fixture->key_file_nonexistent_path, | |
242 | 1 | fixture->key_file1_path, | |
243 | NULL | ||
244 | }; | ||
245 | guint loaded_file; | ||
246 | |||
247 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
248 | |||
249 | 1 | loaded_file = gss_config_file_get_uint (config, "Test", "File", 0, G_MAXUINT, &error); | |
250 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
251 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_cmpuint (loaded_file, ==, 1); |
252 | 1 | } | |
253 | |||
254 | /* Test that if none of the files exist, but the GResource does, we successfully | ||
255 | * use that. */ | ||
256 | static void | ||
257 | 1 | test_config_file_resource_only (Fixture *fixture, | |
258 | gconstpointer user_data G_GNUC_UNUSED) | ||
259 | { | ||
260 | 1 | g_autoptr(GError) error = NULL; | |
261 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
262 | 1 | const gchar * const paths[] = | |
263 | { | ||
264 | 1 | fixture->key_file_nonexistent_path, | |
265 | NULL | ||
266 | }; | ||
267 | guint loaded_file; | ||
268 | |||
269 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
270 | |||
271 | 1 | loaded_file = gss_config_file_get_uint (config, "Test", "File", 0, G_MAXUINT, &error); | |
272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
273 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_cmpuint (loaded_file, ==, 1000); |
274 | 1 | } | |
275 | |||
276 | /* Test that if no configuration files are found, we abort. */ | ||
277 | static void | ||
278 | 1 | test_config_file_fallback_per_file (Fixture *fixture, | |
279 | gconstpointer user_data G_GNUC_UNUSED) | ||
280 | { | ||
281 | 1 | const gchar * const paths[] = | |
282 | { | ||
283 | 1 | fixture->key_file_nonexistent_path, | |
284 | 1 | fixture->key_file_nonexistent_path, | |
285 | NULL | ||
286 | }; | ||
287 | |||
288 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (g_test_subprocess ()) |
289 | { | ||
290 | ✗ | g_autoptr(GssConfigFile) config = NULL; | |
291 | |||
292 | ✗ | config = gss_config_file_new (paths, fixture->default_resource, | |
293 | fixture->default_path_invalid); | ||
294 | ✗ | g_assert_not_reached (); | |
295 | } | ||
296 | else | ||
297 | { | ||
298 | 1 | g_test_trap_subprocess (NULL, 0, 0); | |
299 | 1 | g_test_trap_assert_failed (); | |
300 | 1 | g_test_trap_assert_stderr ("*ERROR*gss_config_file_constructed: " | |
301 | "assertion failed (error == NULL)*"); | ||
302 | } | ||
303 | 1 | } | |
304 | |||
305 | /* Test that loading a key from the second file works if it’s not set in the | ||
306 | * first. */ | ||
307 | static void | ||
308 | 1 | test_config_file_fallback_per_key (Fixture *fixture, | |
309 | gconstpointer user_data G_GNUC_UNUSED) | ||
310 | { | ||
311 | 1 | g_autoptr(GError) error = NULL; | |
312 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
313 | 1 | const gchar * const paths[] = | |
314 | { | ||
315 | 1 | fixture->key_file1_path, | |
316 | 1 | fixture->key_file2_path, | |
317 | NULL | ||
318 | }; | ||
319 | guint loaded_file; | ||
320 | gboolean file1_key, file2_key; | ||
321 | |||
322 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
323 | |||
324 | 1 | loaded_file = gss_config_file_get_uint (config, "Test", "File", 0, G_MAXUINT, &error); | |
325 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
326 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_cmpuint (loaded_file, ==, 1); |
327 | |||
328 | 1 | file1_key = gss_config_file_get_boolean (config, "Test", "File1", &error); | |
329 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
330 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_true (file1_key); |
331 | |||
332 | 1 | file2_key = gss_config_file_get_boolean (config, "Test", "File2", &error); | |
333 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
334 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_true (file2_key); |
335 | 1 | } | |
336 | |||
337 | /* Test that the groups from all loaded files are returned. */ | ||
338 | static void | ||
339 | 1 | test_config_file_groups (Fixture *fixture, | |
340 | gconstpointer user_data G_GNUC_UNUSED) | ||
341 | { | ||
342 | 1 | g_autoptr(GError) error = NULL; | |
343 | 1 | g_autoptr(GssConfigFile) config = NULL; | |
344 | 1 | const gchar * const paths[] = | |
345 | { | ||
346 | 1 | fixture->key_file1_path, | |
347 | 1 | fixture->key_file2_path, | |
348 | NULL | ||
349 | }; | ||
350 | 1 | g_auto(GStrv) groups = NULL; | |
351 | gsize n_groups; | ||
352 | |||
353 | 1 | config = gss_config_file_new (paths, fixture->default_resource, fixture->default_path); | |
354 | |||
355 | 1 | groups = gss_config_file_get_groups (config, &n_groups, &error); | |
356 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_no_error (error); |
357 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_cmpuint (n_groups, ==, 4); |
358 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_nonnull (groups); |
359 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_assert_cmpstr (groups[0], ==, "DefaultGroup"); |
360 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_assert_cmpstr (groups[1], ==, "Group1"); |
361 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_assert_cmpstr (groups[2], ==, "Group2"); |
362 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_assert_cmpstr (groups[3], ==, "Test"); |
363 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_assert_null (groups[4]); |
364 | 1 | } | |
365 | |||
366 | int | ||
367 | 1 | main (int argc, | |
368 | char *argv[]) | ||
369 | { | ||
370 | 1 | setlocale (LC_ALL, ""); | |
371 | |||
372 | 1 | g_test_init (&argc, &argv, NULL); | |
373 | |||
374 | 1 | g_test_add ("/config/load-one", Fixture, NULL, setup, | |
375 | test_config_file_load_one, teardown); | ||
376 | 1 | g_test_add ("/config/load-many", Fixture, NULL, setup, | |
377 | test_config_file_load_many, teardown); | ||
378 | 1 | g_test_add ("/config/unreadable", Fixture, NULL, setup, | |
379 | test_config_file_unreadable, teardown); | ||
380 | 1 | g_test_add ("/config/invalid", Fixture, NULL, setup, | |
381 | test_config_file_invalid, teardown); | ||
382 | 1 | g_test_add ("/config/nonexistent", Fixture, NULL, setup, | |
383 | test_config_file_nonexistent, teardown); | ||
384 | 1 | g_test_add ("/config/resource-only", Fixture, NULL, setup, | |
385 | test_config_file_resource_only, teardown); | ||
386 | 1 | g_test_add ("/config/fallback/per-file", Fixture, NULL, setup, | |
387 | test_config_file_fallback_per_file, teardown); | ||
388 | 1 | g_test_add ("/config/fallback/per-key", Fixture, NULL, setup, | |
389 | test_config_file_fallback_per_key, teardown); | ||
390 | 1 | g_test_add ("/config/groups", Fixture, NULL, setup, | |
391 | test_config_file_groups, teardown); | ||
392 | |||
393 | 1 | return g_test_run (); | |
394 | } | ||
395 |