1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* util/profile/t_profile.c - profile library regression tests */
4 * Copyright (C) 2021 by the Massachusetts Institute of Technology.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
50 check_fail(long code, long expected)
52 assert(code == expected);
56 write_file(const char *name, int nlines, ...)
66 for (i = 0; i < nlines; i++)
67 fprintf(f, "%s\n", va_arg(ap, char *));
72 /* Regression test for #2685 (profile iterator breaks when modifications
79 const char *names[] = { "test section 1", "child_section", "child", NULL };
80 const char *values[] = { "slick", "harry", "john", NULL };
84 check(profile_init_path("test2.ini", &p));
86 /* Iterate and check for the expected values. */
87 check(profile_iterator_create(p, names, 0, &iter));
89 check(profile_iterator(&iter, &name, &value));
90 if (name == NULL && value == NULL)
92 assert(strcmp(name, names[2]) == 0);
93 assert(values[i] != NULL);
94 assert(strcmp(value, values[i]) == 0);
95 profile_release_string(name);
96 profile_release_string(value);
98 assert(values[i] == NULL);
99 profile_iterator_free(&iter);
101 /* Iterate again, deleting each value as we go. Flush the result to a
103 check(profile_iterator_create(p, names, 0, &iter));
105 check(profile_iterator(&iter, NULL, &value));
108 check(profile_update_relation(p, names, value, NULL));
109 profile_release_string(value);
111 profile_iterator_free(&iter);
112 (void)unlink("test3.ini");
113 profile_flush_to_file(p, "test3.ini");
117 /* Check that no values for the section are found in the resulting file. */
118 check(profile_init_path("test3.ini", &p));
119 check(profile_iterator_create(p, names, 0, &iter));
120 check(profile_iterator(&iter, &name, &value));
121 assert(name == NULL && value == NULL);
122 profile_iterator_free(&iter);
127 * Regression test for a 1.4-era bug where updating the underlying file data of
128 * a profile object lost track of the flag indicating that it was part of the
129 * global shared profiles list.
135 struct utimbuf times;
137 system("cp test2.ini test3.ini");
139 /* Create an entry in the shared table. */
140 check(profile_init_path("test3.ini", &a));
143 * Force an update of the underlying data. With the bug present, the
144 * shared flag is erroneously cleared. The easiest way to force an update
145 * is to reopen the file (since we don't enforce the one-stat-per-second
146 * limit during open) after changing the timestamp.
148 times.actime = time(NULL) + 2;
149 times.modtime = times.actime;
150 utime("test3.ini", ×);
151 check(profile_init_path("test3.ini", &b));
154 /* Release the profile. With the bug present, a dangling reference is left
155 * behind in the shared table. */
158 /* Open the profile again to dereference the dangling pointer if one was
160 check(profile_init_path("test3.ini", &a));
164 /* Regression test for #2950 (profile_clear_relation not reflected within
165 * handle where deletion is performed) */
170 const char *names[] = { "test section 1", "quux", NULL };
171 char **values, **dummy;
173 check(profile_init_path("test2.ini", &p));
174 check(profile_get_values(p, names, &values));
175 check(profile_clear_relation(p, names));
176 check_fail(profile_get_values(p, names, &dummy), PROF_NO_RELATION);
177 check(profile_add_relation(p, names, values[0]));
178 profile_free_list(values);
179 check(profile_get_values(p, names, &values));
180 assert(values[0] != NULL && values[1] == NULL);
181 profile_free_list(values);
189 const char *names[] = { "test section 1", "bar", NULL };
192 /* Test expected error code when including nonexistent file. */
193 write_file("testinc.ini", 1, "include does-not-exist");
194 check_fail(profile_init_path("testinc.ini", &p), PROF_FAIL_INCLUDE_FILE);
196 /* Test expected error code when including nonexistent directory. */
197 write_file("testinc.ini", 1, "includedir does-not-exist");
198 check_fail(profile_init_path("testinc.ini", &p), PROF_FAIL_INCLUDE_DIR);
200 /* Test including a file. */
201 write_file("testinc.ini", 1, "include test2.ini");
202 check(profile_init_path("testinc.ini", &p));
203 check(profile_get_values(p, names, &values));
204 assert(strcmp(values[0], "foo") == 0 && values[1] == NULL);
205 profile_free_list(values);
209 * Test including a directory. Put four copies of test2.ini inside the
210 * directory, two with invalid names. Check that we get two values for one
213 system("rm -rf test_include_dir");
214 system("mkdir test_include_dir");
215 system("cp test2.ini test_include_dir/a");
216 system("cp test2.ini test_include_dir/a~");
217 system("cp test2.ini test_include_dir/b.conf");
218 system("cp test2.ini test_include_dir/b.conf.rpmsave");
219 write_file("testinc.ini", 1, "includedir test_include_dir");
220 check(profile_init_path("testinc.ini", &p));
221 check(profile_get_values(p, names, &values));
222 assert(strcmp(values[0], "foo") == 0);
223 assert(strcmp(values[1], "foo") == 0);
224 assert(values[2] == NULL);
225 profile_free_list(values);
228 /* Directly list the directory in the profile path and try again. */
229 check(profile_init_path("test_include_dir", &p));
230 check(profile_get_values(p, names, &values));
231 assert(strcmp(values[0], "foo") == 0);
232 assert(strcmp(values[1], "foo") == 0);
233 assert(values[2] == NULL);
234 profile_free_list(values);
238 /* Test syntactic independence of included profile files. */
243 const char *names1[] = { "sec1", "var", "a", NULL };
244 const char *names2[] = { "sec2", "b", NULL };
245 const char *names3[] = { "sec1", "var", "c", NULL };
248 write_file("testinc.ini", 6, "[sec1]", "var = {", "a = 1",
249 "include testinc2.ini", "c = 3", "}");
250 write_file("testinc2.ini", 2, "[sec2]", "b = 2");
252 check(profile_init_path("testinc.ini", &p));
253 check(profile_get_values(p, names1, &values));
254 assert(strcmp(values[0], "1") == 0 && values[1] == NULL);
255 profile_free_list(values);
256 check(profile_get_values(p, names2, &values));
257 assert(strcmp(values[0], "2") == 0 && values[1] == NULL);
258 profile_free_list(values);
259 check(profile_get_values(p, names3, &values));
260 assert(strcmp(values[0], "3") == 0 && values[1] == NULL);
261 profile_free_list(values);
265 /* Regression test for #7971 (deleted sections should not be iterable) */
267 test_delete_section()
270 const char *sect[] = { "test section 1", NULL };
271 const char *newrel[] = { "test section 1", "testkey", NULL };
272 const char *oldrel[] = { "test section 1", "child", NULL };
275 check(profile_init_path("test2.ini", &p));
277 /* Remove and replace a section. */
278 check(profile_rename_section(p, sect, NULL));
279 check(profile_add_relation(p, sect, NULL));
280 check(profile_add_relation(p, newrel, "6"));
282 /* Check that we can read the new relation but not the old one. */
283 check(profile_get_values(p, newrel, &values));
284 assert(strcmp(values[0], "6") == 0 && values[1] == NULL);
285 profile_free_list(values);
286 check_fail(profile_get_values(p, oldrel, &values), PROF_NO_RELATION);
290 /* Regression test for #7971 (profile_clear_relation() error with deleted node
291 * at end of value set) */
293 test_delete_clear_relation()
296 const char *names[] = { "test section 1", "testkey", NULL };
298 check(profile_init_path("test2.ini", &p));
299 check(profile_add_relation(p, names, "1"));
300 check(profile_add_relation(p, names, "2"));
301 check(profile_update_relation(p, names, "2", NULL));
302 check(profile_clear_relation(p, names));
306 /* Test that order of relations is preserved if some relations are deleted. */
308 test_delete_ordering()
311 const char *names[] = { "test section 1", "testkey", NULL };
314 check(profile_init_path("test2.ini", &p));
315 check(profile_add_relation(p, names, "1"));
316 check(profile_add_relation(p, names, "2"));
317 check(profile_add_relation(p, names, "3"));
318 check(profile_update_relation(p, names, "2", NULL));
319 check(profile_add_relation(p, names, "4"));
320 check(profile_get_values(p, names, &values));
321 assert(strcmp(values[0], "1") == 0);
322 assert(strcmp(values[1], "3") == 0);
323 assert(strcmp(values[2], "4") == 0);
324 assert(values[3] == NULL);
325 profile_free_list(values);
329 /* Regression test for #8431 (profile_flush_to_file erroneously changes flag
330 * state on source object) */
336 /* Flush a profile object to a file without making any changes, so that the
337 * source object is still within g_shared_trees. */
338 check(profile_init_path("test2.ini", &p));
340 check(profile_flush_to_file(p, "test3.ini"));
343 /* Check for a dangling reference in g_shared_trees by creating another
345 profile_init_path("test2.ini", &p);
349 /* Regression test for #7863 (multiply-specified subsections should
352 test_merge_subsections()
355 const char *n1[] = { "test section 2", "child_section2", "child", NULL };
356 const char *n2[] = { "test section 2", "child_section2", "chores", NULL };
359 check(profile_init_path("test2.ini", &p));
361 check(profile_get_values(p, n1, &values));
362 assert(strcmp(values[0], "slick") == 0);
363 assert(strcmp(values[1], "harry") == 0);
364 assert(strcmp(values[2], "john\tb ") == 0);
365 assert(strcmp(values[3], "ron") == 0);
366 assert(values[4] == NULL);
367 profile_free_list(values);
369 check(profile_get_values(p, n2, &values));
370 assert(strcmp(values[0], "cleaning") == 0 && values[1] == NULL);
371 profile_free_list(values);
384 test_delete_section();
385 test_delete_clear_relation();
386 test_delete_ordering();
387 test_flush_to_file();
388 test_merge_subsections();