Imported Upstream version 1.20.1
[platform/upstream/krb5.git] / src / util / profile / t_profile.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* util/profile/t_profile.c - profile library regression tests */
3 /*
4  * Copyright (C) 2021 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
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
17  *   distribution.
18  *
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.
31  */
32
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <utime.h>
41 #include "profile.h"
42
43 static void
44 check(long code)
45 {
46     assert(code == 0);
47 }
48
49 static void
50 check_fail(long code, long expected)
51 {
52     assert(code == expected);
53 }
54
55 static void
56 write_file(const char *name, int nlines, ...)
57 {
58     FILE *f;
59     va_list ap;
60     int i;
61
62     (void)unlink(name);
63     f = fopen(name, "w");
64     assert(f != NULL);
65     va_start(ap, nlines);
66     for (i = 0; i < nlines; i++)
67         fprintf(f, "%s\n", va_arg(ap, char *));
68     va_end(ap);
69     fclose(f);
70 }
71
72 /* Regression test for #2685 (profile iterator breaks when modifications
73  * made) */
74 static void
75 test_iterate()
76 {
77     profile_t p;
78     void *iter;
79     const char *names[] = { "test section 1", "child_section", "child", NULL };
80     const char *values[] = { "slick", "harry", "john", NULL };
81     char *name, *value;
82     int i;
83
84     check(profile_init_path("test2.ini", &p));
85
86     /* Iterate and check for the expected values. */
87     check(profile_iterator_create(p, names, 0, &iter));
88     for (i = 0;; i++) {
89         check(profile_iterator(&iter, &name, &value));
90         if (name == NULL && value == NULL)
91             break;
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);
97     }
98     assert(values[i] == NULL);
99     profile_iterator_free(&iter);
100
101     /* Iterate again, deleting each value as we go.  Flush the result to a
102      * separate file. */
103     check(profile_iterator_create(p, names, 0, &iter));
104     for (;;) {
105         check(profile_iterator(&iter, NULL, &value));
106         if (value == NULL)
107             break;
108         check(profile_update_relation(p, names, value, NULL));
109         profile_release_string(value);
110     }
111     profile_iterator_free(&iter);
112     (void)unlink("test3.ini");
113     profile_flush_to_file(p, "test3.ini");
114
115     profile_abandon(p);
116
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);
123     profile_abandon(p);
124 }
125
126 /*
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.
130  */
131 static void
132 test_shared()
133 {
134     profile_t a, b;
135     struct utimbuf times;
136
137     system("cp test2.ini test3.ini");
138
139     /* Create an entry in the shared table. */
140     check(profile_init_path("test3.ini", &a));
141
142     /*
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.
147      */
148     times.actime = time(NULL) + 2;
149     times.modtime = times.actime;
150     utime("test3.ini", &times);
151     check(profile_init_path("test3.ini", &b));
152     profile_release(b);
153
154     /* Release the profile.  With the bug present, a dangling reference is left
155      * behind in the shared table. */
156     profile_release(a);
157
158     /* Open the profile again to dereference the dangling pointer if one was
159      * created. */
160     check(profile_init_path("test3.ini", &a));
161     profile_release(a);
162 }
163
164 /* Regression test for #2950 (profile_clear_relation not reflected within
165  * handle where deletion is performed) */
166 static void
167 test_clear()
168 {
169     profile_t p;
170     const char *names[] = { "test section 1", "quux", NULL };
171     char **values, **dummy;
172
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);
182     profile_abandon(p);
183 }
184
185 static void
186 test_include()
187 {
188     profile_t p;
189     const char *names[] = { "test section 1", "bar", NULL };
190     char **values;
191
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);
195
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);
199
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);
206     profile_release(p);
207
208     /*
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
211      * of the variables.
212      */
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);
226     profile_release(p);
227
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);
235     profile_release(p);
236 }
237
238 /* Test syntactic independence of included profile files. */
239 static void
240 test_independence()
241 {
242     profile_t p;
243     const char *names1[] = { "sec1", "var", "a", NULL };
244     const char *names2[] = { "sec2", "b", NULL };
245     const char *names3[] = { "sec1", "var", "c", NULL };
246     char **values;
247
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");
251
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);
262     profile_release(p);
263 }
264
265 /* Regression test for #7971 (deleted sections should not be iterable) */
266 static void
267 test_delete_section()
268 {
269     profile_t p;
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 };
273     char **values;
274
275     check(profile_init_path("test2.ini", &p));
276
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"));
281
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);
287     profile_abandon(p);
288 }
289
290 /* Regression test for #7971 (profile_clear_relation() error with deleted node
291  * at end of value set) */
292 static void
293 test_delete_clear_relation()
294 {
295     profile_t p;
296     const char *names[] = { "test section 1", "testkey", NULL };
297
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));
303     profile_abandon(p);
304 }
305
306 /* Test that order of relations is preserved if some relations are deleted. */
307 static void
308 test_delete_ordering()
309 {
310     profile_t p;
311     const char *names[] = { "test section 1", "testkey", NULL };
312     char **values;
313
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);
326     profile_abandon(p);
327 }
328
329 /* Regression test for #8431 (profile_flush_to_file erroneously changes flag
330  * state on source object) */
331 static void
332 test_flush_to_file()
333 {
334     profile_t p;
335
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));
339     unlink("test3.ini");
340     check(profile_flush_to_file(p, "test3.ini"));
341     profile_release(p);
342
343     /* Check for a dangling reference in g_shared_trees by creating another
344      * profile object. */
345     profile_init_path("test2.ini", &p);
346     profile_release(p);
347 }
348
349 /* Regression test for #7863 (multiply-specified subsections should
350  * be merged) */
351 static void
352 test_merge_subsections()
353 {
354     profile_t p;
355     const char *n1[] = { "test section 2", "child_section2", "child", NULL };
356     const char *n2[] = { "test section 2", "child_section2", "chores", NULL };
357     char **values;
358
359     check(profile_init_path("test2.ini", &p));
360
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);
368
369     check(profile_get_values(p, n2, &values));
370     assert(strcmp(values[0], "cleaning") == 0 && values[1] == NULL);
371     profile_free_list(values);
372
373     profile_release(p);
374 }
375
376 int
377 main()
378 {
379     test_iterate();
380     test_shared();
381     test_clear();
382     test_include();
383     test_independence();
384     test_delete_section();
385     test_delete_clear_relation();
386     test_delete_ordering();
387     test_flush_to_file();
388     test_merge_subsections();
389 }