1 /* $Id: confparse-t.c 10308 2018-12-02 14:33:59Z iulius $ */
2 /* confparse test suite. */
3 
4 #include "config.h"
5 #include "clibrary.h"
6 
7 #include "inn/confparse.h"
8 #include "inn/messages.h"
9 #include "inn/vector.h"
10 #include "inn/libinn.h"
11 #include "tap/basic.h"
12 #include "tap/float.h"
13 #include "tap/messages.h"
14 
15 /* Given a FILE *, read from that file, putting the results into a newly
16    allocated buffer, until encountering a line consisting solely of "===".
17    Returns the buffer, NULL on end of file, dies on error. */
18 static char *
read_section(FILE * file)19 read_section(FILE *file)
20 {
21     char buf[1024];
22     char *data = NULL;
23     char *status;
24 
25     status = fgets(buf, sizeof(buf), file);
26     if (status == NULL)
27         return false;
28     while (1) {
29         if (status == NULL)
30             die("Unexpected end of file while reading tests");
31         if (strcmp(buf, "===\n") == 0)
32             break;
33         if (data == NULL) {
34             data = xstrdup(buf);
35         } else {
36             char *new_data;
37 
38             new_data = concat(data, buf, (char *) 0);
39             free(data);
40             data = new_data;
41         }
42         status = fgets(buf, sizeof(buf), file);
43     }
44     return data;
45 }
46 
47 /* Read from the given file a configuration file and write it out to
48    config/tmp.  Returns true on success, false on end of file, and dies on
49    any error. */
50 static bool
write_test_config(FILE * file)51 write_test_config(FILE *file)
52 {
53     FILE *tmp;
54     char *config;
55 
56     config = read_section(file);
57     if (config == NULL)
58         return false;
59     tmp = fopen("config/tmp", "w");
60     if (tmp == NULL)
61         sysdie("Cannot create config/tmp");
62     if (fputs(config, tmp) == EOF)
63         sysdie("Write error while writing to config/tmp");
64     fclose(tmp);
65     free(config);
66     return true;
67 }
68 
69 /* Parse a given config file with errors, setting the appropriate error
70    handler for the duration of the parse to save errors into the errors
71    global.  Returns the resulting config_group. */
72 static struct config_group *
parse_error_config(const char * filename)73 parse_error_config(const char *filename)
74 {
75     struct config_group *group;
76 
77     errors_capture();
78     group = config_parse_file(filename);
79     errors_uncapture();
80     return group;
81 }
82 
83 /* Read in a configuration file from the provided FILE *, write it to disk,
84    parse the temporary config file, and return the resulting config_group in
85    the pointer passed as the second parameter.  Returns true on success,
86    false on end of file. */
87 static bool
parse_test_config(FILE * file,struct config_group ** group)88 parse_test_config(FILE *file, struct config_group **group)
89 {
90     if (!write_test_config(file))
91         return false;
92     *group = parse_error_config("config/tmp");
93     unlink("config/tmp");
94     return true;
95 }
96 
97 /* Test the error test cases in config/errors, ensuring that they all fail
98    to parse and match the expected error messages.  Takes the current test
99    count and returns the new test count. */
100 static int
test_errors(int n)101 test_errors(int n)
102 {
103     FILE *errfile;
104     char *expected;
105     struct config_group *group;
106 
107     if (symlink(".", "config/link") < 0)
108         sysdie("Cannot create config/link symlink");
109     errfile = fopen("config/errors", "r");
110     if (errfile == NULL)
111         sysdie("Cannot open config/errors");
112     while (parse_test_config(errfile, &group)) {
113         expected = read_section(errfile);
114         if (expected == NULL)
115             die("Unexpected end of file while reading error tests");
116         ok(n++, group == NULL);
117         ok_string(n++, expected, errors);
118         free(expected);
119         if (group != NULL)
120             config_free(group);
121     }
122     fclose(errfile);
123     unlink("config/link");
124     return n;
125 }
126 
127 /* Test the warning test cases in config/warnings, ensuring that they all
128    parse successfully and match the expected error messages.  Takes the
129    current test count and returns the new test count. */
130 static int
test_warnings(int n)131 test_warnings(int n)
132 {
133     FILE *warnfile;
134     char *expected;
135     struct config_group *group;
136 
137     warnfile = fopen("config/warnings", "r");
138     if (warnfile == NULL)
139         sysdie("Cannot open config/warnings");
140     while (parse_test_config(warnfile, &group)) {
141         expected = read_section(warnfile);
142         if (expected == NULL)
143             die("Unexpected end of file while reading error tests");
144         ok(n++, group != NULL);
145         ok_string(n++, expected, errors);
146         free(expected);
147         if (group != NULL)
148             config_free(group);
149     }
150     fclose(warnfile);
151     return n;
152 }
153 
154 /* Test the warning test cases in config/warn-bool, ensuring that they all
155    parse successfully and produce the expected error messages when retrieved
156    as bools.  Takes the current test count and returns the new test count. */
157 static int
test_warnings_bool(int n)158 test_warnings_bool(int n)
159 {
160     FILE *warnfile;
161     char *expected;
162     struct config_group *group;
163     bool b_value = false;
164 
165     warnfile = fopen("config/warn-bool", "r");
166     if (warnfile == NULL)
167         sysdie("Cannot open config/warn-bool");
168     while (parse_test_config(warnfile, &group)) {
169         expected = read_section(warnfile);
170         if (expected == NULL)
171             die("Unexpected end of file while reading error tests");
172         ok(n++, group != NULL);
173         ok(n++, errors == NULL);
174         errors_capture();
175         ok(n++, !config_param_boolean(group, "parameter", &b_value));
176         ok_string(n++, expected, errors);
177         errors_uncapture();
178         free(expected);
179         if (group != NULL)
180             config_free(group);
181     }
182     fclose(warnfile);
183     return n;
184 }
185 
186 /* Test the warning test cases in config/warn-int, ensuring that they all
187    parse successfully and produce the expected error messages when retrieved
188    as signed numbers.  Takes the current test count and returns the new test
189    count. */
190 static int
test_warnings_int(int n)191 test_warnings_int(int n)
192 {
193     FILE *warnfile;
194     char *expected;
195     struct config_group *group;
196     long l_value = 1;
197 
198     warnfile = fopen("config/warn-int", "r");
199     if (warnfile == NULL)
200         sysdie("Cannot open config/warn-int");
201     while (parse_test_config(warnfile, &group)) {
202         expected = read_section(warnfile);
203         if (expected == NULL)
204             die("Unexpected end of file while reading error tests");
205         ok(n++, group != NULL);
206         ok(n++, errors == NULL);
207         errors_capture();
208         ok(n++, !config_param_signed_number(group, "parameter", &l_value));
209         ok_string(n++, expected, errors);
210         errors_uncapture();
211         free(expected);
212         if (group != NULL)
213             config_free(group);
214     }
215     fclose(warnfile);
216     return n;
217 }
218 
219 /* Test the warning test cases in config/warn-uint, ensuring that they all
220    parse successfully and produce the expected error messages when retrieved
221    as usigned numbers.  Takes the current test count and returns the new test
222    count. */
223 static int
test_warnings_uint(int n)224 test_warnings_uint(int n)
225 {
226     FILE *warnfile;
227     char *expected;
228     struct config_group *group;
229     unsigned long lu_value = 1;
230 
231     warnfile = fopen("config/warn-uint", "r");
232     if (warnfile == NULL)
233         sysdie("Cannot open config/warn-uint");
234     while (parse_test_config(warnfile, &group)) {
235         expected = read_section(warnfile);
236         if (expected == NULL)
237             die("Unexpected end of file while reading error tests");
238         ok(n++, group != NULL);
239         ok(n++, errors == NULL);
240         errors_capture();
241         ok(n++, !config_param_unsigned_number(group, "parameter", &lu_value));
242         ok_string(n++, expected, errors);
243         errors_uncapture();
244         free(expected);
245         if (group != NULL)
246             config_free(group);
247     }
248     fclose(warnfile);
249     return n;
250 }
251 
252 /* Test the warning test cases in config/warn-real, ensuring that they all
253    parse successfully and produce the expected error messages when retrieved
254    as reals.  Takes the current test count and returns the new test count. */
255 static int
test_warnings_real(int n)256 test_warnings_real(int n)
257 {
258     FILE *warnfile;
259     char *expected;
260     struct config_group *group;
261     double d_value;
262 
263     warnfile = fopen("config/warn-real", "r");
264     if (warnfile == NULL)
265         sysdie("Cannot open config/warn-real");
266     while (parse_test_config(warnfile, &group)) {
267         expected = read_section(warnfile);
268         if (expected == NULL)
269             die("Unexpected end of file while reading error tests");
270         ok(n++, group != NULL);
271         ok(n++, errors == NULL);
272         errors_capture();
273         ok(n++, !config_param_real(group, "parameter", &d_value));
274         ok_string(n++, expected, errors);
275         errors_uncapture();
276         free(expected);
277         if (group != NULL)
278             config_free(group);
279     }
280     fclose(warnfile);
281     return n;
282 }
283 
284 /* Test the warning test cases in config/warn-string, ensuring that they all
285    parse successfully and produce the expected error messages when retrieved
286    as strings.  Takes the current test count and returns the new test count. */
287 static int
test_warnings_string(int n)288 test_warnings_string(int n)
289 {
290     FILE *warnfile;
291     char *expected;
292     struct config_group *group;
293     const char *s_value = NULL;
294 
295     warnfile = fopen("config/warn-string", "r");
296     if (warnfile == NULL)
297         sysdie("Cannot open config/warn-string");
298     while (parse_test_config(warnfile, &group)) {
299         expected = read_section(warnfile);
300         if (expected == NULL)
301             die("Unexpected end of file while reading error tests");
302         ok(n++, group != NULL);
303         ok(n++, errors == NULL);
304         errors_capture();
305         ok(n++, !config_param_string(group, "parameter", &s_value));
306         ok_string(n++, expected, errors);
307         errors_uncapture();
308         free(expected);
309         if (group != NULL)
310             config_free(group);
311     }
312     fclose(warnfile);
313     return n;
314 }
315 
316 /* Test the warning test cases in config/warn-list, ensuring that they all
317    parse successfully and produce the expected error messages when retrieved
318    as lists.  Takes the current test count and returns the new test count. */
319 static int
test_warnings_list(int n)320 test_warnings_list(int n)
321 {
322     FILE *warnfile;
323     char *expected;
324     struct config_group *group;
325     const struct vector *v_value = NULL;
326 
327     warnfile = fopen("config/warn-list", "r");
328     if (warnfile == NULL)
329         sysdie("Cannot open config/warn-list");
330     while (parse_test_config(warnfile, &group)) {
331         expected = read_section(warnfile);
332         if (expected == NULL)
333             die("Unexpected end of file while reading error tests");
334         ok(n++, group != NULL);
335         ok(n++, errors == NULL);
336         errors_capture();
337         ok(n++, !config_param_list(group, "parameter", &v_value));
338         ok_string(n++, expected, errors);
339         errors_uncapture();
340         free(expected);
341         if (group != NULL)
342             config_free(group);
343     }
344     fclose(warnfile);
345     return n;
346 }
347 
348 int
main(void)349 main(void)
350 {
351     struct config_group *group, *subgroup;
352     bool b_value = false;
353     long l_value = 1;
354     double d_value = 1;
355     const char *s_value;
356     const struct vector *v_value;
357     struct vector *vector;
358     char *long_param, *long_value;
359     size_t length;
360     int n;
361     FILE *tmpconfig;
362 
363     test_init(373);
364 
365     if (access("../data/config/valid", F_OK) == 0) {
366         if (chdir("../data") < 0) {
367             sysbail("cannot chdir to ../data");
368         }
369     } else if (access("data/config/valid", F_OK) == 0) {
370         if (chdir("data") < 0) {
371             sysbail("cannot chdir to data");
372         }
373     } else if (access("tests/data/config/valid", F_OK) == 0) {
374         if (chdir("tests/data") < 0) {
375             sysbail("cannot chdir to tests/data");
376         }
377     }
378     group = config_parse_file("config/valid");
379     ok(1, group != NULL);
380     if (group == NULL)
381         exit(1);
382 
383     /* Booleans. */
384     ok(2, config_param_boolean(group, "param1", &b_value));
385     ok(3, b_value);
386     b_value = false;
387     ok(4, config_param_boolean(group, "param2", &b_value));
388     ok(5, b_value);
389     b_value = false;
390     ok(6, config_param_boolean(group, "param3", &b_value));
391     ok(7, b_value);
392     ok(8, config_param_boolean(group, "param4", &b_value));
393     ok(9, !b_value);
394     b_value = true;
395     ok(10, config_param_boolean(group, "param5", &b_value));
396     ok(11, !b_value);
397     b_value = true;
398     ok(12, config_param_boolean(group, "param6", &b_value));
399     ok(13, !b_value);
400 
401     /* Integers. */
402     ok(14, config_param_signed_number(group, "int1", &l_value));
403     ok(15, l_value == 0);
404     ok(16, config_param_signed_number(group, "int2", &l_value));
405     ok(17, l_value == -3);
406     ok(18, !config_param_signed_number(group, "int3", &l_value));
407     ok(19, l_value == -3);
408     ok(20, config_param_signed_number(group, "int4", &l_value));
409     ok(21, l_value == 5000);
410     ok(22, config_param_signed_number(group, "int5", &l_value));
411     ok(23, l_value == 2147483647L);
412     ok(24, config_param_signed_number(group, "int6", &l_value));
413     ok(25, l_value == (-2147483647L - 1));
414 
415     /* Strings. */
416     ok(26, config_param_string(group, "string1", &s_value));
417     ok_string(27, "foo", s_value);
418     ok(28, config_param_string(group, "string2", &s_value));
419     ok_string(29, "bar", s_value);
420     ok(30, config_param_string(group, "string3", &s_value));
421     ok_string(31, "this is a test", s_value);
422     ok(32, config_param_string(group, "string4", &s_value));
423     ok_string(33, "this is a test", s_value);
424     ok(34, config_param_string(group, "string5", &s_value));
425     ok_string(35, "this is \a\b\f\n\r\t\v a test \' of \" escapes \?\\",
426               s_value);
427     ok(36, config_param_string(group, "string6", &s_value));
428     ok_string(37, "# this is not a comment", s_value);
429     ok(38, config_param_string(group, "string7", &s_value));
430     ok_string(39, "lost \nyet?", s_value);
431 
432     config_free(group);
433 
434     /* Missing newline. */
435     group = config_parse_file("config/no-newline");
436     ok(40, group != NULL);
437     if (group == NULL)
438         ok_block(41, 2, false);
439     else {
440         ok(41, config_param_string(group, "parameter", &s_value));
441         ok_string(42, "value", s_value);
442         config_free(group);
443     }
444 
445     /* Extremely long parameter and value. */
446     tmpconfig = fopen("config/tmp", "w");
447     if (tmpconfig == NULL)
448         sysdie("cannot create config/tmp");
449     long_param = xcalloc(20001, 1);
450     memset(long_param, 'a', 20000);
451     long_value = xcalloc(64 * 1024 + 1, 1);
452     memset(long_value, 'b', 64 * 1024);
453     fprintf(tmpconfig, "%s: \"%s\"; two: %s", long_param, long_value,
454             long_value);
455     fclose(tmpconfig);
456     group = config_parse_file("config/tmp");
457     ok(43, group != NULL);
458     if (group == NULL)
459         ok_block(44, 4, false);
460     else {
461         ok(44, config_param_string(group, long_param, &s_value));
462         ok_string(45, long_value, s_value);
463         ok(46, config_param_string(group, "two", &s_value));
464         ok_string(47, long_value, s_value);
465         config_free(group);
466     }
467     unlink("config/tmp");
468     free(long_param);
469     free(long_value);
470 
471     /* Parsing problems exactly on the boundary of a buffer.  This test caught
472        a bug in the parser that caused it to miss the colon at the end of a
473        parameter because the colon was the first character read in a new read
474        of the file buffer. */
475     tmpconfig = fopen("config/tmp", "w");
476     if (tmpconfig == NULL)
477         sysdie("cannot create config/tmp");
478     length = 16 * 1024 - strlen(": baz\nfoo:");
479     long_param = xcalloc(length + 1, 1);
480     memset(long_param, 'c', length);
481     fprintf(tmpconfig, "%s: baz\nfoo: bar\n", long_param);
482     fclose(tmpconfig);
483     group = config_parse_file("config/tmp");
484     ok(48, group != NULL);
485     if (group == NULL)
486         ok_block(49, 4, false);
487     else {
488         ok(49, config_param_string(group, long_param, &s_value));
489         ok_string(50, "baz", s_value);
490         ok(51, config_param_string(group, "foo", &s_value));
491         ok_string(52, "bar", s_value);
492         config_free(group);
493     }
494     unlink("config/tmp");
495     free(long_param);
496 
497     /* Alternate line endings. */
498     group = config_parse_file("config/line-endings");
499     ok(53, group != NULL);
500     if (group == NULL)
501         exit(1);
502     ok(54, config_param_boolean(group, "param1", &b_value));
503     ok(55, b_value);
504     b_value = false;
505     ok(56, config_param_boolean(group, "param2", &b_value));
506     ok(57, b_value);
507     b_value = false;
508     ok(58, config_param_boolean(group, "param3", &b_value));
509     ok(59, b_value);
510     ok(60, config_param_boolean(group, "param4", &b_value));
511     ok(61, !b_value);
512     ok(62, config_param_signed_number(group, "int1", &l_value));
513     ok(63, l_value == 0);
514     ok(64, config_param_signed_number(group, "int2", &l_value));
515     ok(65, l_value == -3);
516     config_free(group);
517 
518     /* Listing parameters. */
519     group = config_parse_file("config/simple");
520     ok(66, group != NULL);
521     if (group == NULL)
522         exit(1);
523     vector = config_params(group);
524     ok_int(67, 2, vector->count);
525     skip(68, "test to be removed when using the new C TAP Harness syntax");
526     if (strcmp(vector->strings[0], "foo") == 0)
527         ok_string(69, "bar", vector->strings[1]);
528     else if (strcmp(vector->strings[0], "bar") == 0)
529         ok_string(69, "foo", vector->strings[1]);
530     else
531         ok(69, false);
532     vector_free(vector);
533     config_free(group);
534 
535     /* Lists. */
536     group = config_parse_file("config/lists");
537     ok(70, group != NULL);
538     if (group == NULL)
539         exit(1);
540     ok(71, config_param_list(group, "vector1", &v_value));
541     ok_int(72, 1, v_value->count);
542     ok_string(73, "simple", v_value->strings[0]);
543     ok(74, config_param_list(group, "vector2", &v_value));
544     ok_int(75, 3, v_value->count);
545     ok_string(76, "foo\tbar", v_value->strings[0]);
546     ok_string(77, "baz", v_value->strings[1]);
547     ok_string(78, "# this is not a comment", v_value->strings[2]);
548     ok(79, config_param_list(group, "vector3", &v_value));
549     ok_int(80, 0, v_value->count);
550     ok(81, config_param_list(group, "vector4", &v_value));
551     ok_int(82, 0, v_value->count);
552     ok(83, config_param_list(group, "vector5", &v_value));
553     ok_int(84, 1, v_value->count);
554     ok_string(85, "baz", v_value->strings[0]);
555     ok(86, config_param_list(group, "vector6", &v_value));
556     ok_int(87, 1, v_value->count);
557     ok_string(88, "bar baz", v_value->strings[0]);
558     config_free(group);
559 
560     /* Groups. */
561     group = config_parse_file("config/groups");
562     ok(89, group != NULL);
563     if (group == NULL)
564         exit(1);
565     subgroup = config_find_group(group, "test");
566     ok(90, subgroup != NULL);
567     ok_string(91, "test", config_group_type(subgroup));
568     ok(92, config_param_boolean(subgroup, "value", &b_value));
569     ok(93, b_value);
570     subgroup = config_next_group(subgroup);
571     ok(94, subgroup != NULL);
572     ok_string(95, "test", config_group_type(subgroup));
573     ok(96, config_group_tag(subgroup) == NULL);
574     ok(97, config_param_boolean(subgroup, "value", &b_value));
575     subgroup = config_next_group(subgroup);
576     ok(98, subgroup != NULL);
577     ok(99, config_param_signed_number(subgroup, "value", &l_value));
578     ok_int(100, 2, l_value);
579     subgroup = config_next_group(subgroup);
580     ok(101, subgroup != NULL);
581     ok(102, config_param_signed_number(subgroup, "value", &l_value));
582     ok_int(103, 3, l_value);
583     subgroup = config_find_group(subgroup, "test");
584     ok(104, subgroup != NULL);
585     ok(105, config_param_signed_number(subgroup, "value", &l_value));
586     ok_int(106, 2, l_value);
587     subgroup = config_next_group(subgroup);
588     ok(107, subgroup != NULL);
589     ok_string(108, "test", config_group_type(subgroup));
590     ok_string(109, "final", config_group_tag(subgroup));
591     ok(110, config_param_signed_number(subgroup, "value", &l_value));
592     ok_int(111, 4, l_value);
593     subgroup = config_next_group(subgroup);
594     ok(112, subgroup == NULL);
595     subgroup = config_find_group(group, "nest");
596     ok(113, subgroup != NULL);
597     ok_string(114, "nest", config_group_type(subgroup));
598     ok_string(115, "1", config_group_tag(subgroup));
599     ok(116, config_param_signed_number(subgroup, "param", &l_value));
600     ok_int(117, 10, l_value);
601     subgroup = config_next_group(subgroup);
602     ok(118, subgroup != NULL);
603     ok_string(119, "2", config_group_tag(subgroup));
604     ok(120, config_param_signed_number(subgroup, "param", &l_value));
605     ok_int(121, 10, l_value);
606     subgroup = config_next_group(subgroup);
607     ok(122, subgroup != NULL);
608     ok_string(123, "3", config_group_tag(subgroup));
609     ok(124, config_param_signed_number(subgroup, "param", &l_value));
610     ok_int(125, 10, l_value);
611     subgroup = config_next_group(subgroup);
612     ok(126, subgroup != NULL);
613     ok_string(127, "4", config_group_tag(subgroup));
614     ok(128, config_param_signed_number(subgroup, "param", &l_value));
615     ok_int(129, 10, l_value);
616     subgroup = config_next_group(subgroup);
617     ok(130, subgroup == NULL);
618     subgroup = config_find_group(group, "nonexistent");
619     ok(131, subgroup == NULL);
620     subgroup = config_find_group(group, "nest");
621     ok(132, subgroup != NULL);
622     subgroup = config_find_group(subgroup, "params");
623     ok(133, subgroup != NULL);
624     ok_string(134, "params", config_group_type(subgroup));
625     ok_string(135, "first", config_group_tag(subgroup));
626     ok(136, config_param_signed_number(subgroup, "first", &l_value));
627     ok_int(137, 1, l_value);
628     ok(138, !config_param_signed_number(subgroup, "second", &l_value));
629     subgroup = config_next_group(subgroup);
630     ok(139, subgroup != NULL);
631     ok_string(140, "second", config_group_tag(subgroup));
632     ok(141, config_param_signed_number(subgroup, "first", &l_value));
633     ok_int(142, 1, l_value);
634     ok(143, config_param_signed_number(subgroup, "second", &l_value));
635     ok_int(144, 2, l_value);
636     subgroup = config_next_group(subgroup);
637     ok(145, subgroup != NULL);
638     ok_string(146, "third", config_group_tag(subgroup));
639     ok(147, config_param_signed_number(subgroup, "first", &l_value));
640     ok_int(148, 1, l_value);
641     ok(149, config_param_signed_number(subgroup, "second", &l_value));
642     ok_int(150, 2, l_value);
643     ok(151, config_param_signed_number(subgroup, "third", &l_value));
644     ok_int(152, 3, l_value);
645     vector = config_params(subgroup);
646     ok(153, vector != NULL);
647     ok_int(154, 3, vector->count);
648     skip(155, "test to be removed when using the new C TAP Harness syntax");
649     ok_string(156, "third", vector->strings[0]);
650     ok_string(157, "second", vector->strings[1]);
651     ok_string(158, "first", vector->strings[2]);
652     vector_free(vector);
653     config_free(group);
654 
655     /* Includes. */
656     group = config_parse_file("config/include");
657     ok(159, group != NULL);
658     if (group == NULL)
659         exit(1);
660     subgroup = config_find_group(group, "group");
661     ok(160, subgroup != NULL);
662     ok(161, config_param_string(subgroup, "foo", &s_value));
663     ok_string(162, "baz", s_value);
664     ok(163, config_param_string(subgroup, "bar", &s_value));
665     ok_string(164, "baz", s_value);
666     ok(165, !config_param_signed_number(subgroup, "value", &l_value));
667     subgroup = config_next_group(subgroup);
668     ok(166, subgroup != NULL);
669     subgroup = config_next_group(subgroup);
670     ok(167, subgroup != NULL);
671     ok_string(168, "test", config_group_tag(subgroup));
672     ok(169, config_param_string(subgroup, "foo", &s_value));
673     ok_string(170, "baz", s_value);
674     ok(171, config_param_signed_number(subgroup, "value", &l_value));
675     ok_int(172, 10, l_value);
676     config_free(group);
677 
678     /* Real numbers. */
679     group = config_parse_file("config/reals");
680     ok(173, group != NULL);
681     ok(174, config_param_real(group, "real1", &d_value));
682     ok_double(175, 0.1, d_value);
683     ok(176, config_param_real(group, "real2", &d_value));
684     ok_double(177, -123.45e10, d_value);
685     ok(178, config_param_real(group, "real3", &d_value));
686     ok_double(179, 4.0e-3, d_value);
687     ok(180, config_param_real(group, "real4", &d_value));
688     ok_double(181, 1, d_value);
689     config_free(group);
690 
691     /* Errors. */
692     group = parse_error_config("config/null");
693     ok(182, group == NULL);
694     ok_string(183, "config/null: invalid NUL character found in file\n",
695               errors);
696     n = test_errors(184);
697     n = test_warnings(n);
698     n = test_warnings_bool(n);
699     n = test_warnings_int(n);
700     n = test_warnings_uint(n);
701     n = test_warnings_real(n);
702     n = test_warnings_string(n);
703     test_warnings_list(n);
704 
705     return 0;
706 }
707