config: Fix ast_config_text_file_save2 writability check for missing files
[asterisk/asterisk.git] / tests / test_config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Configuration unit tests
22  *
23  * \author Mark Michelson <mmichelson@digium.com>
24  *
25  */
26
27 /*** MODULEINFO
28         <depend>TEST_FRAMEWORK</depend>
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_REGISTER_FILE();
35
36 #include <math.h> /* HUGE_VAL */
37 #include <sys/stat.h>
38
39 #include "asterisk/config.h"
40 #include "asterisk/module.h"
41 #include "asterisk/test.h"
42 #include "asterisk/paths.h"
43 #include "asterisk/config_options.h"
44 #include "asterisk/netsock2.h"
45 #include "asterisk/acl.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/frame.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/format_cap.h"
51
52 #define CONFIG_FILE "test_config.conf"
53 #define CONFIG_INCLUDE_FILE "test_config_include.conf"
54
55 /*
56  * This builds the folowing config:
57  * [Capitals]
58  * Germany = Berlin
59  * China = Beijing
60  * Canada = Ottawa
61  *
62  * [Protagonists]
63  * 1984 = Winston Smith
64  * Green Eggs And Ham = Sam I Am
65  * The Kalevala = Vainamoinen
66  *
67  * This config is used for all tests below.
68  */
69 const char cat1[] = "Capitals";
70 const char cat1varname1[] = "Germany";
71 const char cat1varvalue1[] = "Berlin";
72 const char cat1varname2[] = "China";
73 const char cat1varvalue2[] = "Beijing";
74 const char cat1varname3[] = "Canada";
75 const char cat1varvalue3[] = "Ottawa";
76
77 const char cat2[] = "Protagonists";
78 const char cat2varname1[] = "1984";
79 const char cat2varvalue1[] = "Winston Smith";
80 const char cat2varname2[] = "Green Eggs And Ham";
81 const char cat2varvalue2[] = "Sam I Am";
82 const char cat2varname3[] = "The Kalevala";
83 const char cat2varvalue3[] = "Vainamoinen";
84
85 struct pair {
86         const char *name;
87         const char *val;
88 };
89
90 struct association {
91         const char *category;
92         struct pair vars[3];
93 } categories [] = {
94         { cat1,
95                 {
96                         { cat1varname1, cat1varvalue1 },
97                         { cat1varname2, cat1varvalue2 },
98                         { cat1varname3, cat1varvalue3 },
99                 }
100         },
101         { cat2,
102                 {
103                         { cat2varname1, cat2varvalue1 },
104                         { cat2varname2, cat2varvalue2 },
105                         { cat2varname3, cat2varvalue3 },
106                 }
107         },
108 };
109
110 /*!
111  * \brief Build ast_config struct from above definitions
112  *
113  * \retval NULL Failed to build the config
114  * \retval non-NULL An ast_config struct populated with data
115  */
116 static struct ast_config *build_cfg(void)
117 {
118         struct ast_config *cfg;
119         struct association *cat_iter;
120         struct pair *var_iter;
121         size_t i;
122         size_t j;
123
124         cfg = ast_config_new();
125         if (!cfg) {
126                 goto fail;
127         }
128
129         for (i = 0; i < ARRAY_LEN(categories); ++i) {
130                 struct ast_category *cat;
131                 cat_iter = &categories[i];
132
133                 cat = ast_category_new(cat_iter->category, "", 999999);
134                 if (!cat) {
135                         goto fail;
136                 }
137                 ast_category_append(cfg, cat);
138
139                 for (j = 0; j < ARRAY_LEN(cat_iter->vars); ++j) {
140                         struct ast_variable *var;
141                         var_iter = &cat_iter->vars[j];
142
143                         var = ast_variable_new(var_iter->name, var_iter->val, "");
144                         if (!var) {
145                                 goto fail;
146                         }
147                         ast_variable_append(cat, var);
148                 }
149         }
150
151         return cfg;
152
153 fail:
154         ast_config_destroy(cfg);
155         return NULL;
156 }
157
158 /*!
159  * \brief Tests that the contents of an ast_config is what is expected
160  *
161  * \param cfg Config to test
162  * \retval -1 Failed to pass a test
163  * \retval 0 Config passes checks
164  */
165 static int test_config_validity(struct ast_config *cfg)
166 {
167         int i;
168         const char *cat_iter = NULL;
169         /* Okay, let's see if the correct content is there */
170         for (i = 0; i < ARRAY_LEN(categories); ++i) {
171                 struct ast_variable *var = NULL;
172                 size_t j;
173                 cat_iter = ast_category_browse(cfg, cat_iter);
174                 if (strcmp(cat_iter, categories[i].category)) {
175                         ast_log(LOG_ERROR, "Category name mismatch, %s does not match %s\n", cat_iter, categories[i].category);
176                         return -1;
177                 }
178                 for (j = 0; j < ARRAY_LEN(categories[i].vars); ++j) {
179                         var = var ? var->next : ast_variable_browse(cfg, cat_iter);
180                         if (strcmp(var->name, categories[i].vars[j].name)) {
181                                 ast_log(LOG_ERROR, "Variable name mismatch, %s does not match %s\n", var->name, categories[i].vars[j].name);
182                                 return -1;
183                         }
184                         if (strcmp(var->value, categories[i].vars[j].val)) {
185                                 ast_log(LOG_ERROR, "Variable value mismatch, %s does not match %s\n", var->value, categories[i].vars[j].val);
186                                 return -1;
187                         }
188                 }
189         }
190         return 0;
191 }
192
193 AST_TEST_DEFINE(copy_config)
194 {
195         enum ast_test_result_state res = AST_TEST_FAIL;
196         struct ast_config *cfg = NULL;
197         struct ast_config *copy = NULL;
198
199         switch (cmd) {
200         case TEST_INIT:
201                 info->name = "copy_config";
202                 info->category = "/main/config/";
203                 info->summary = "Test copying configuration";
204                 info->description =
205                         "Ensure that variables and categories are copied correctly";
206                 return AST_TEST_NOT_RUN;
207         case TEST_EXECUTE:
208                 break;
209         }
210
211         cfg = build_cfg();
212         if (!cfg) {
213                 goto out;
214         }
215
216         copy = ast_config_copy(cfg);
217         if (!copy) {
218                 goto out;
219         }
220
221         if (test_config_validity(copy) != 0) {
222                 goto out;
223         }
224
225         res = AST_TEST_PASS;
226
227 out:
228         ast_config_destroy(cfg);
229         ast_config_destroy(copy);
230         return res;
231 }
232
233 AST_TEST_DEFINE(config_basic_ops)
234 {
235         enum ast_test_result_state res = AST_TEST_FAIL;
236         struct ast_config *cfg = NULL;
237         struct ast_category *cat = NULL;
238         struct ast_variable *var;
239         struct ast_variable *varlist;
240         char temp[32];
241         const char *cat_name;
242         const char *var_value;
243         int i;
244
245         switch (cmd) {
246         case TEST_INIT:
247                 info->name = "config_basic_ops";
248                 info->category = "/main/config/";
249                 info->summary = "Test basic config ops";
250                 info->description =     "Test basic config ops";
251                 return AST_TEST_NOT_RUN;
252         case TEST_EXECUTE:
253                 break;
254         }
255
256         cfg = ast_config_new();
257         if (!cfg) {
258                 return res;
259         }
260
261         /* load the config */
262         for(i = 0; i < 5; i++) {
263                 snprintf(temp, sizeof(temp), "test%d", i);
264                 ast_category_append(cfg, ast_category_new(temp, "dummy", -1));
265         }
266
267         /* test0 test1 test2 test3 test4 */
268         /* check the config has 5 elements */
269         i = 0;
270         cat = NULL;
271         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) {
272                 snprintf(temp, sizeof(temp), "test%d", i);
273                 if (strcmp(ast_category_get_name(cat), temp)) {
274                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
275                         goto out;
276                 }
277                 i++;
278         }
279         if (i != 5) {
280                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
281                 goto out;
282         }
283
284         /* search for test2 */
285         cat = ast_category_get(cfg, "test2", NULL);
286         if (!cat || strcmp(ast_category_get_name(cat), "test2")) {
287                 ast_test_status_update(test, "Get failed %s != %s\n", ast_category_get_name(cat), "test2");
288                 goto out;
289         }
290
291         /* delete test2 */
292         cat = ast_category_delete(cfg, cat);
293
294         /* Now: test0 test1 test3 test4 */
295         /* make sure the curr category is test1 */
296         if (!cat || strcmp(ast_category_get_name(cat), "test1")) {
297                 ast_test_status_update(test, "Delete failed %s != %s\n", ast_category_get_name(cat), "test1");
298                 goto out;
299         }
300
301         /* Now: test0 test1 test3 test4 */
302         /* make sure the test2 is not found */
303         cat = ast_category_get(cfg, "test2", NULL);
304         if (cat) {
305                 ast_test_status_update(test, "Should not have found test2\n");
306                 goto out;
307         }
308
309         /* Now: test0 test1 test3 test4 */
310         /* make sure the sequence is correctly missing test2 */
311         i = 0;
312         cat = NULL;
313         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) {
314                 snprintf(temp, sizeof(temp), "test%d", i);
315                 if (strcmp(ast_category_get_name(cat), temp)) {
316                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
317                         goto out;
318                 }
319                 i++;
320                 if (i == 2) {
321                         i++;
322                 }
323         }
324         if (i != 5) {
325                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
326                 goto out;
327         }
328
329         /* insert test2 back in before test3 */
330         ast_category_insert(cfg, ast_category_new("test2", "dummy", -1), "test3");
331
332         /* Now: test0 test1 test2 test3 test4 */
333         /* make sure the sequence is correct again */
334         i = 0;
335         cat = NULL;
336         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) {
337                 snprintf(temp, sizeof(temp), "test%d", i);
338                 if (strcmp(ast_category_get_name(cat), temp)) {
339                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
340                         goto out;
341                 }
342                 i++;
343         }
344         if (i != 5) {
345                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
346                 goto out;
347         }
348
349         /* Now: test0 test1 test2 test3 test4 */
350         /* make sure non filtered browse still works */
351         i = 0;
352         cat_name = NULL;
353         while ((cat_name = ast_category_browse(cfg, cat_name))) {
354                 snprintf(temp, sizeof(temp), "test%d", i);
355                 if (strcmp(cat_name, temp)) {
356                         ast_test_status_update(test, "%s != %s\n", cat_name, temp);
357                         goto out;
358                 }
359                 i++;
360         }
361         if (i != 5) {
362                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
363                 goto out;
364         }
365
366         /* append another test2 */
367         ast_category_append(cfg, ast_category_new("test2", "dummy", -1));
368         /* Now: test0 test1 test2 test3 test4 test2*/
369         /* make sure only test2's are returned */
370         i = 0;
371         cat = NULL;
372         while ((cat = ast_category_browse_filtered(cfg, "test2", cat, NULL))) {
373                 if (strcmp(ast_category_get_name(cat), "test2")) {
374                         ast_test_status_update(test, "Should have returned test2 instead of %s\n", ast_category_get_name(cat));
375                         goto out;
376                 }
377                 i++;
378         }
379         /* make sure 2 test2's were found */
380         if (i != 2) {
381                 ast_test_status_update(test, "Should have found 2 test2's %d\n", i);
382                 goto out;
383         }
384
385         /* Test in-flight deletion using ast_category_browse_filtered */
386         /* Now: test0 test1 test2 test3 test4 test2 */
387         /* Delete the middle test2 and continue */
388         cat = NULL;
389         for(i = 0; i < 5; i++) {
390                 snprintf(temp, sizeof(temp), "test%d", i);
391                 cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
392                 cat_name = ast_category_get_name(cat);
393                 if (strcmp(cat_name, temp)) {
394                         ast_test_status_update(test, "Should have returned %s instead of %s: %d\n", temp, cat_name, i);
395                         goto out;
396                 }
397                 if (i == 2) {
398                         cat = ast_category_delete(cfg, cat);
399                 }
400         }
401
402         /* Now: test0 test3 test4 test2 */
403         /* delete the head item */
404         cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL);
405         cat_name = ast_category_get_name(cat);
406         if (strcmp(cat_name, "test0")) {
407                 ast_test_status_update(test, "Should have returned test0 instead of %s\n", cat_name);
408                 goto out;
409         }
410         ast_category_delete(cfg, cat);
411         /* Now: test3 test4 test2 */
412
413         /* make sure head got updated to the new first element */
414         cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL);
415         cat_name = ast_category_get_name(cat);
416         if (strcmp(cat_name, "test1")) {
417                 ast_test_status_update(test, "Should have returned test3 instead of %s\n", cat_name);
418                 goto out;
419         }
420
421         /* delete the tail item */
422         cat = ast_category_get(cfg, "test2", NULL);
423         cat_name = ast_category_get_name(cat);
424         if (strcmp(cat_name, "test2")) {
425                 ast_test_status_update(test, "Should have returned test2 instead of %s\n", cat_name);
426                 goto out;
427         }
428         ast_category_delete(cfg, cat);
429         /* Now: test3 test4 */
430
431         /* There should now only be 2 elements in the list */
432         cat = NULL;
433         cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
434         cat_name = ast_category_get_name(cat);
435         if (strcmp(cat_name, "test1")) {
436                 ast_test_status_update(test, "Should have returned test1 instead of %s\n", cat_name);
437                 goto out;
438         }
439
440         cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
441         cat_name = ast_category_get_name(cat);
442         if (strcmp(cat_name, "test3")) {
443                 ast_test_status_update(test, "Should have returned test3 instead of %s\n", cat_name);
444                 goto out;
445         }
446
447         cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
448         cat_name = ast_category_get_name(cat);
449         if (strcmp(cat_name, "test4")) {
450                 ast_test_status_update(test, "Should have returned test4 instead of %s\n", cat_name);
451                 goto out;
452         }
453
454         /* There should be nothing more */
455         cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
456         if (cat) {
457                 ast_test_status_update(test, "Should not have returned anything\n");
458                 goto out;
459         }
460
461         /* Test ast_variable retrieve.
462          * Get the second category.
463          */
464         cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL);
465         cat = ast_category_browse_filtered(cfg, NULL, cat, NULL);
466         cat_name = ast_category_get_name(cat);
467         var = ast_variable_new("aaa", "bbb0", "dummy");
468         if (!var) {
469                 ast_test_status_update(test, "Couldn't allocate variable.\n");
470                 goto out;
471         }
472         ast_variable_append(cat, var);
473
474         /* Make sure we can retrieve with specific category name */
475         var_value = ast_variable_retrieve(cfg, cat_name, "aaa");
476         if (!var_value || strcmp(var_value, "bbb0")) {
477                 ast_test_status_update(test, "Variable not found or wrong value.\n");
478                 goto out;
479         }
480
481         /* Make sure we can retrieve with NULL category name */
482         var_value = ast_variable_retrieve(cfg, NULL, "aaa");
483         if (!var_value || strcmp(var_value, "bbb0")) {
484                 ast_test_status_update(test, "Variable not found or wrong value.\n");
485                 goto out;
486         }
487
488         /* Now test variable retrieve inside a browse loop
489          * with multiple categories of the same name
490          */
491         cat = ast_category_new("test3", "dummy", -1);
492         if (!cat) {
493                 ast_test_status_update(test, "Couldn't allocate category.\n");
494                 goto out;
495         }
496         var = ast_variable_new("aaa", "bbb1", "dummy");
497         if (!var) {
498                 ast_test_status_update(test, "Couldn't allocate variable.\n");
499                 goto out;
500         }
501         ast_variable_append(cat, var);
502         ast_category_append(cfg, cat);
503
504         cat = ast_category_new("test3", "dummy", -1);
505         if (!cat) {
506                 ast_test_status_update(test, "Couldn't allocate category.\n");
507                 goto out;
508         }
509         var = ast_variable_new("aaa", "bbb2", "dummy");
510         if (!var) {
511                 ast_test_status_update(test, "Couldn't allocate variable.\n");
512                 goto out;
513         }
514         ast_variable_append(cat, var);
515         ast_category_append(cfg, cat);
516
517         cat_name = NULL;
518         i = 0;
519         while ((cat_name = ast_category_browse(cfg, cat_name))) {
520                 if (!strcmp(cat_name, "test3")) {
521                         snprintf(temp, sizeof(temp), "bbb%d", i);
522
523                         var_value = ast_variable_retrieve(cfg, cat_name, "aaa");
524                         if (!var_value || strcmp(var_value, temp)) {
525                                 ast_test_status_update(test, "Variable not found or wrong value %s.\n", var_value);
526                                 goto out;
527                         }
528
529                         var = ast_variable_browse(cfg, cat_name);
530                         if (!var->value || strcmp(var->value, temp)) {
531                                 ast_test_status_update(test, "Variable not found or wrong value %s.\n", var->value);
532                                 goto out;
533                         }
534
535                         i++;
536                 }
537         }
538         if (i != 3) {
539                 ast_test_status_update(test, "There should have been 3 matches instead of %d.\n", i);
540                 goto out;
541         }
542
543         varlist = ast_variable_new("name1", "value1", "");
544         ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value2", ""));
545         ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value3", ""));
546
547         var_value = ast_variable_find_in_list(varlist, "name1");
548         if (strcmp(var_value, "value1") != 0) {
549                 ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
550                 goto out;
551         }
552
553         var_value = ast_variable_find_last_in_list(varlist, "name1");
554         if (strcmp(var_value, "value3") != 0) {
555                 ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
556                 goto out;
557         }
558
559         res = AST_TEST_PASS;
560
561 out:
562         ast_config_destroy(cfg);
563         return res;
564 }
565
566 AST_TEST_DEFINE(config_filtered_ops)
567 {
568         enum ast_test_result_state res = AST_TEST_FAIL;
569         struct ast_config *cfg = NULL;
570         struct ast_category *cat = NULL;
571         char temp[32];
572         const char *value;
573         int i;
574
575         switch (cmd) {
576         case TEST_INIT:
577                 info->name = "config_filtered_ops";
578                 info->category = "/main/config/";
579                 info->summary = "Test filtered config ops";
580                 info->description =     "Test filtered config ops";
581                 return AST_TEST_NOT_RUN;
582         case TEST_EXECUTE:
583                 break;
584         }
585
586         cfg = ast_config_new();
587         if (!cfg) {
588                 return res;
589         }
590
591         /* load the config */
592         for(i = 0; i < 5; i++) {
593                 snprintf(temp, sizeof(temp), "test%d", i);
594                 cat = ast_category_new(temp, "dummy", -1);
595                 ast_variable_insert(cat, ast_variable_new("type", "a", "dummy"), "0");
596                 ast_category_append(cfg, cat);
597         }
598
599         for(i = 0; i < 5; i++) {
600                 snprintf(temp, sizeof(temp), "test%d", i);
601                 cat = ast_category_new(temp, "dummy", -1);
602                 ast_variable_insert(cat, ast_variable_new("type", "b", "dummy"), "0");
603                 ast_category_append(cfg, cat);
604         }
605
606         /* check the config has 5 elements for each type*/
607         i = 0;
608         cat = NULL;
609         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "type=a"))) {
610                 snprintf(temp, sizeof(temp), "test%d", i);
611                 if (strcmp(ast_category_get_name(cat), temp)) {
612                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
613                         goto out;
614                 }
615                 value = ast_variable_find(cat, "type");
616                 if (!value || strcmp(value, "a")) {
617                         ast_test_status_update(test, "Type %s != %s\n", "a", value);
618                         goto out;
619                 }
620                 i++;
621         }
622         if (i != 5) {
623                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
624                 goto out;
625         }
626
627         i = 0;
628         cat = NULL;
629         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "type=b"))) {
630                 snprintf(temp, sizeof(temp), "test%d", i);
631                 if (!cat || strcmp(ast_category_get_name(cat), temp)) {
632                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
633                         goto out;
634                 }
635                 value = ast_variable_find(cat, "type");
636                 if (!value || strcmp(value, "b")) {
637                         ast_test_status_update(test, "Type %s != %s\n", "b", value);
638                         goto out;
639                 }
640                 i++;
641         }
642         if (i != 5) {
643                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
644                 goto out;
645         }
646
647         /* Delete b3 and make sure it's gone and a3 is still there.
648          * Really this is a test of get since delete takes a specific category structure.
649          */
650         cat = ast_category_get(cfg, "test3", "type=b");
651         value = ast_variable_find(cat, "type");
652         if (strcmp(value, "b")) {
653                 ast_test_status_update(test, "Type %s != %s\n", "b", value);
654                 goto out;
655         }
656         ast_category_delete(cfg, cat);
657
658         cat = ast_category_get(cfg, "test3", "type=b");
659         if (cat) {
660                 ast_test_status_update(test, "Category b was not deleted.\n");
661                 goto out;
662         }
663
664         cat = ast_category_get(cfg, "test3", "type=a");
665         if (!cat) {
666                 ast_test_status_update(test, "Category a was deleted.\n");
667                 goto out;
668         }
669
670         value = ast_variable_find(cat, "type");
671         if (strcmp(value, "a")) {
672                 ast_test_status_update(test, "Type %s != %s\n", value, "a");
673                 goto out;
674         }
675
676         /* Basic regex stuff is handled by regcomp/regexec so not testing here.
677          * Still need to test multiple name/value pairs though.
678          */
679         ast_category_empty(cat);
680         ast_variable_insert(cat, ast_variable_new("type", "bx", "dummy"), "0");
681         ast_variable_insert(cat, ast_variable_new("e", "z", "dummy"), "0");
682
683         cat = ast_category_get(cfg, "test3", "type=.,e=z");
684         if (!cat) {
685                 ast_test_status_update(test, "Category not found.\n");
686                 goto out;
687         }
688
689         cat = ast_category_get(cfg, "test3", "type=.,e=zX");
690         if (cat) {
691                 ast_test_status_update(test, "Category found.\n");
692                 goto out;
693         }
694
695         cat = ast_category_get(cfg, "test3", "TEMPLATE=restrict,type=.,e=z");
696         if (cat) {
697                 ast_test_status_update(test, "Category found.\n");
698                 goto out;
699         }
700
701         res = AST_TEST_PASS;
702
703 out:
704         ast_config_destroy(cfg);
705         return res;
706 }
707
708 AST_TEST_DEFINE(config_template_ops)
709 {
710         enum ast_test_result_state res = AST_TEST_FAIL;
711         struct ast_config *cfg = NULL;
712         struct ast_category *cat = NULL;
713         char temp[32];
714         const char *value;
715         int i;
716
717         switch (cmd) {
718         case TEST_INIT:
719                 info->name = "config_template_ops";
720                 info->category = "/main/config/";
721                 info->summary = "Test template config ops";
722                 info->description =     "Test template config ops";
723                 return AST_TEST_NOT_RUN;
724         case TEST_EXECUTE:
725                 break;
726         }
727
728         cfg = ast_config_new();
729         if (!cfg) {
730                 return res;
731         }
732
733         /* load the config with 5 templates and 5 regular */
734         for(i = 0; i < 5; i++) {
735                 snprintf(temp, sizeof(temp), "test%d", i);
736                 cat = ast_category_new_template(temp, "dummy", -1);
737                 ast_variable_insert(cat, ast_variable_new("type", "a", "dummy"), "0");
738                 ast_category_append(cfg, cat);
739         }
740
741         for(i = 0; i < 5; i++) {
742                 snprintf(temp, sizeof(temp), "test%d", i);
743                 cat = ast_category_new(temp, "dummy", -1);
744                 ast_variable_insert(cat, ast_variable_new("type", "b", "dummy"), "0");
745                 ast_category_append(cfg, cat);
746         }
747
748         /* check the config has 5 template elements of type a */
749         i = 0;
750         cat = NULL;
751         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=restrict,type=a"))) {
752                 snprintf(temp, sizeof(temp), "test%d", i);
753                 if (strcmp(ast_category_get_name(cat), temp)) {
754                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
755                         goto out;
756                 }
757                 value = ast_variable_find(cat, "type");
758                 if (!value || strcmp(value, "a")) {
759                         ast_test_status_update(test, "Type %s != %s\n", value, "a");
760                         goto out;
761                 }
762                 i++;
763         }
764         if (i != 5) {
765                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
766                 goto out;
767         }
768
769         /* Test again with 'include'.  There should still only be 5 (type a) */
770         i = 0;
771         cat = NULL;
772         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=include,type=a"))) {
773                 snprintf(temp, sizeof(temp), "test%d", i);
774                 if (strcmp(ast_category_get_name(cat), temp)) {
775                         ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp);
776                         goto out;
777                 }
778                 value = ast_variable_find(cat, "type");
779                 if (!value || strcmp(value, "a")) {
780                         ast_test_status_update(test, "Type %s != %s\n", value, "a");
781                         goto out;
782                 }
783                 i++;
784         }
785         if (i != 5) {
786                 ast_test_status_update(test, "There were %d matches instead of 5.\n", i);
787                 goto out;
788         }
789
790         /* Test again with 'include' but no type.  There should now be 10 (type a and type b) */
791         i = 0;
792         cat = NULL;
793         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=include"))) {
794                 i++;
795         }
796         if (i != 10) {
797                 ast_test_status_update(test, "There were %d matches instead of 10.\n", i);
798                 goto out;
799         }
800
801         /* Test again with 'restrict' and type b.  There should 0 */
802         i = 0;
803         cat = NULL;
804         while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=restrict,type=b"))) {
805                 i++;
806         }
807         if (i != 0) {
808                 ast_test_status_update(test, "There were %d matches instead of 0.\n", i);
809                 goto out;
810         }
811
812         res = AST_TEST_PASS;
813
814 out:
815         ast_config_destroy(cfg);
816         return res;
817 }
818
819 /*!
820  * \brief Write the config file to disk
821  *
822  * This is necessary for testing config hooks since
823  * they are only triggered when a config is read from
824  * its intended storage medium
825  */
826 static int write_config_file(void)
827 {
828         int i;
829         FILE *config_file;
830         char filename[PATH_MAX];
831
832         snprintf(filename, sizeof(filename), "%s/%s",
833                         ast_config_AST_CONFIG_DIR, CONFIG_FILE);
834         config_file = fopen(filename, "w");
835
836         if (!config_file) {
837                 return -1;
838         }
839
840         for (i = 0; i < ARRAY_LEN(categories); ++i) {
841                 int j;
842                 fprintf(config_file, "[%s]\n", categories[i].category);
843                 for (j = 0; j < ARRAY_LEN(categories[i].vars); ++j) {
844                         fprintf(config_file, "%s = %s\n",
845                                         categories[i].vars[j].name,
846                                         categories[i].vars[j].val);
847                 }
848         }
849
850         fclose(config_file);
851         return 0;
852 }
853
854 /*!
855  * \brief Delete config file created by write_config_file
856  */
857 static void delete_config_file(void)
858 {
859         char filename[PATH_MAX];
860         snprintf(filename, sizeof(filename), "%s/%s",
861                         ast_config_AST_CONFIG_DIR, CONFIG_FILE);
862         unlink(filename);
863 }
864
865 /*
866  * Boolean to indicate if the config hook has run
867  */
868 static int hook_run;
869
870 /*
871  * Boolean to indicate if, when the hook runs, the
872  * data passed to it is what is expected
873  */
874 static int hook_config_sane;
875
876 static int hook_cb(struct ast_config *cfg)
877 {
878         hook_run = 1;
879         if (test_config_validity(cfg) == 0) {
880                 hook_config_sane = 1;
881         }
882         ast_config_destroy(cfg);
883         return 0;
884 }
885
886 AST_TEST_DEFINE(config_save)
887 {
888         enum ast_test_result_state res = AST_TEST_FAIL;
889         struct ast_flags config_flags = { 0 };
890         struct ast_config *cfg;
891         char config_filename[PATH_MAX];
892         char include_filename[PATH_MAX];
893         struct stat config_stat;
894         off_t before_save;
895
896         switch (cmd) {
897         case TEST_INIT:
898                 info->name = "config_save";
899                 info->category = "/main/config/";
900                 info->summary = "Test config save";
901                 info->description =
902                         "Test configuration save.";
903                 return AST_TEST_NOT_RUN;
904         case TEST_EXECUTE:
905                 break;
906         }
907
908         if (write_config_file()) {
909                 ast_test_status_update(test, "Could not write initial config files\n");
910                 return res;
911         }
912
913         snprintf(config_filename, PATH_MAX, "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_FILE);
914         snprintf(include_filename, PATH_MAX, "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_INCLUDE_FILE);
915
916         cfg = ast_config_load(CONFIG_FILE, config_flags);
917         if (!cfg) {
918                 ast_test_status_update(test, "Could not load config\n");
919                 goto out;
920         }
921
922         /* We need to re-save to get the generator header */
923         if (ast_config_text_file_save(CONFIG_FILE, cfg, "TEST")) {
924                 ast_test_status_update(test, "Unable to write files\n");
925                 goto out;
926         }
927
928         stat(config_filename, &config_stat);
929         before_save = config_stat.st_size;
930
931         if (!ast_include_new(cfg, CONFIG_FILE, CONFIG_INCLUDE_FILE, 0, NULL, 4, include_filename, PATH_MAX)) {
932                 ast_test_status_update(test, "Could not create include\n");
933                 goto out;
934         }
935
936         if (ast_config_text_file_save(CONFIG_FILE, cfg, "TEST")) {
937                 ast_test_status_update(test, "Unable to write files\n");
938                 goto out;
939         }
940
941         stat(config_filename, &config_stat);
942         if (config_stat.st_size <= before_save) {
943                 ast_test_status_update(test, "Did not save config file with #include\n");
944                 goto out;
945         }
946
947         res = AST_TEST_PASS;
948
949 out:
950         ast_config_destroy(cfg);
951         unlink(config_filename);
952         unlink(include_filename);
953
954         return res;
955 }
956
957 AST_TEST_DEFINE(config_hook)
958 {
959         enum ast_test_result_state res = AST_TEST_FAIL;
960         enum config_hook_flags hook_flags = { 0, };
961         struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
962         struct ast_config *cfg;
963
964         switch (cmd) {
965         case TEST_INIT:
966                 info->name = "config_hook";
967                 info->category = "/main/config/";
968                 info->summary = "Test config hooks";
969                 info->description =
970                         "Ensure that config hooks are called at approriate times,"
971                         "not called at inappropriate times, and that all information"
972                         "that should be present is present.";
973                 return AST_TEST_NOT_RUN;
974         case TEST_EXECUTE:
975                 break;
976         }
977
978         write_config_file();
979
980         /*
981          * Register a config hook to run when CONFIG_FILE is loaded by this module
982          */
983         ast_config_hook_register("test_hook",
984                         CONFIG_FILE,
985                         AST_MODULE,
986                         hook_flags,
987                         hook_cb);
988
989         /*
990          * Try loading the config file. This should result in the hook
991          * being called
992          */
993         cfg = ast_config_load(CONFIG_FILE, config_flags);
994         ast_config_destroy(cfg);
995         if (!hook_run || !hook_config_sane) {
996                 ast_test_status_update(test, "Config hook either did not run or was given bad data!\n");
997                 goto out;
998         }
999
1000         /*
1001          * Now try loading the wrong config file but from the right module.
1002          * Hook should not run
1003          */
1004         hook_run = 0;
1005         cfg = ast_config_load("asterisk.conf", config_flags);
1006         ast_config_destroy(cfg);
1007         if (hook_run) {
1008                 ast_test_status_update(test, "Config hook ran even though an incorrect file was specified.\n");
1009                 goto out;
1010         }
1011
1012         /*
1013          * Now try loading the correct config file but from the wrong module.
1014          * Hook should not run
1015          */
1016         hook_run = 0;
1017         cfg = ast_config_load2(CONFIG_FILE, "fake_module.so", config_flags);
1018         ast_config_destroy(cfg);
1019         if (hook_run) {
1020                 ast_test_status_update(test, "Config hook ran even though an incorrect module was specified.\n");
1021                 goto out;
1022         }
1023
1024         /*
1025          * Now try loading the file correctly, but without any changes to the file.
1026          * Hook should not run
1027          */
1028         hook_run = 0;
1029         cfg = ast_config_load(CONFIG_FILE, config_flags);
1030         /* Only destroy this cfg conditionally. Otherwise a crash happens. */
1031         if (cfg != CONFIG_STATUS_FILEUNCHANGED) {
1032                 ast_config_destroy(cfg);
1033         }
1034         if (hook_run) {
1035                 ast_test_status_update(test, "Config hook ran even though file contents had not changed\n");
1036                 goto out;
1037         }
1038
1039         res = AST_TEST_PASS;
1040
1041 out:
1042         delete_config_file();
1043         return res;
1044 }
1045
1046 enum {
1047         EXPECT_FAIL = 0,
1048         EXPECT_SUCCEED,
1049 };
1050
1051 #define TOOBIG_I32 "2147483649"
1052 #define TOOSMALL_I32 "-2147483649"
1053 #define TOOBIG_U32 "4294967297"
1054 #define TOOSMALL_U32 "-4294967297"
1055 #define DEFAULTVAL 42
1056 #define EPSILON 0.001
1057
1058 #define TEST_PARSE(input, should_succeed, expected_result, flags, result, ...) do {\
1059         int __res = ast_parse_arg(input, (flags), result, ##__VA_ARGS__); \
1060         if (!__res == !should_succeed) { \
1061                 ast_test_status_update(test, "ast_parse_arg failed on '%s'. %d/%d\n", input, __res, should_succeed); \
1062                 ret = AST_TEST_FAIL; \
1063         } else { \
1064                 if (((flags) & PARSE_TYPE) == PARSE_INT32) { \
1065                         int32_t *r = (int32_t *) (void *) result; \
1066                         int32_t e = (int32_t) expected_result; \
1067                         if (*r != e) { \
1068                                 ast_test_status_update(test, "ast_parse_arg int32_t failed with %d != %d\n", *r, e); \
1069                                 ret = AST_TEST_FAIL; \
1070                         } \
1071                 } else if (((flags) & PARSE_TYPE) == PARSE_UINT32) { \
1072                         uint32_t *r = (uint32_t *) (void *) result; \
1073                         uint32_t e = (uint32_t) expected_result; \
1074                         if (*r != e) { \
1075                                 ast_test_status_update(test, "ast_parse_arg uint32_t failed with %u != %u\n", *r, e); \
1076                                 ret = AST_TEST_FAIL; \
1077                         } \
1078                 } else if (((flags) & PARSE_TYPE) == PARSE_DOUBLE) { \
1079                         double *r = (double *) (void *) result; \
1080                         double e = (double) expected_result; \
1081                         if (fabs(*r - e) > EPSILON) { \
1082                                 ast_test_status_update(test, "ast_parse_arg double failed with %f != %f\n", *r, e); \
1083                                 ret = AST_TEST_FAIL; \
1084                         } \
1085                 } \
1086         } \
1087         *(result) = DEFAULTVAL; \
1088 } while (0)
1089
1090 AST_TEST_DEFINE(ast_parse_arg_test)
1091 {
1092         int ret = AST_TEST_PASS;
1093         int32_t int32_t_val = DEFAULTVAL;
1094         uint32_t uint32_t_val = DEFAULTVAL;
1095         double double_val = DEFAULTVAL;
1096
1097         switch (cmd) {
1098         case TEST_INIT:
1099                 info->name = "ast_parse_arg";
1100                 info->category = "/config/";
1101                 info->summary = "Test the output of ast_parse_arg";
1102                 info->description =
1103                         "Ensures that ast_parse_arg behaves as expected";
1104                 return AST_TEST_NOT_RUN;
1105         case TEST_EXECUTE:
1106                 break;
1107         }
1108
1109         /* int32 testing */
1110         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32, &int32_t_val);
1111         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32, &int32_t_val);
1112         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32, &int32_t_val);
1113         TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val);
1114         TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val);
1115         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val);
1116         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val);
1117         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1118         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1119         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1120         TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1121         TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1122         TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1123         TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7);
1124
1125         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, 0, 200);
1126         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, -200, 100);
1127         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, -1, 0);
1128         TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, 0, 122);
1129         TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, -122, 100);
1130         TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, 1, 100);
1131         TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1132         TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1133         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1134         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1135         TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, 0, 200);
1136         TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, -200, 100);
1137         TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, -1, 0);
1138         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, 0, 122);
1139         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, -122, 100);
1140         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, 1, 100);
1141         TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1142         TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1143         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1144         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX);
1145
1146         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, 0, 200);
1147         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, -200, 100);
1148         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, -1, 0);
1149         TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, 0, 122);
1150         TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, -122, 100);
1151         TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, 1, 100);
1152         TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1153         TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1154         TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1155         TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1156         TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, 0, 200);
1157         TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, -200, 100);
1158         TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, -1, 0);
1159         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, 0, 122);
1160         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, -122, 100);
1161         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, 1, 100);
1162         TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1163         TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1164         TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1165         TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX);
1166
1167         /* uuint32 testing */
1168         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32, &uint32_t_val);
1169         TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
1170         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32, &uint32_t_val);
1171         TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
1172         TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
1173         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
1174         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
1175
1176         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1177         TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1178         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1179         TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1180         TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1181         TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1182         TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7);
1183
1184         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 200);
1185         TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 200);
1186         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 1);
1187
1188         TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 122);
1189         TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 1, 100);
1190         TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1191         TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1192         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1193         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1194
1195         TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 200);
1196         TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 200);
1197         TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 1);
1198
1199         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 122);
1200         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 1, 100);
1201         TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1202         TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1203         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1204         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX);
1205
1206         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 200);
1207         TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 200);
1208         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 1);
1209         TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 122);
1210         TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 1, 100);
1211         TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1212         TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1213         TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1214         TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1215         TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 200);
1216         TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 100);
1217         TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 1);
1218         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 122);
1219         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 1, 100);
1220         TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1221         TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1222         TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1223         TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX);
1224
1225         TEST_PARSE("   -123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val);
1226
1227         /* double testing */
1228         TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_DOUBLE, &double_val);
1229         TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE, &double_val);
1230         TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_DOUBLE, &double_val);
1231         TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE, &double_val);
1232         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE, &double_val);
1233         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE, &double_val);
1234         TEST_PARSE("7.0not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE, &double_val);
1235         TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0);
1236         TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0);
1237         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0);
1238         TEST_PARSE("not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0);
1239         TEST_PARSE("7.0not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0);
1240
1241         TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, 0.0, 200.0);
1242         TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -200.0, 100.0);
1243         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -1.0, 0.0);
1244         TEST_PARSE("123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, 0.0, 122.0);
1245         TEST_PARSE("-123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -122.0, 100.0);
1246         TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, 1.0, 100.0);
1247         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -HUGE_VAL, HUGE_VAL);
1248         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -HUGE_VAL, HUGE_VAL);
1249         TEST_PARSE("123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, 0.0, 200.0);
1250         TEST_PARSE("-123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -200.0, 100.0);
1251         TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -1.0, 0.0);
1252         TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, 0.0, 122.0);
1253         TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -122.0, 100.0);
1254         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, 1.0, 100.0);
1255         TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -HUGE_VAL, HUGE_VAL);
1256         TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -HUGE_VAL, HUGE_VAL);
1257
1258         TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, 0.0, 200.0);
1259         TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -200.0, 100.0);
1260         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -1.0, 0.0);
1261         TEST_PARSE("123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, 0.0, 122.0);
1262         TEST_PARSE("-123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -122.0, 100.0);
1263         TEST_PARSE("0", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, 1.0, 100.0);
1264         TEST_PARSE("not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL);
1265         TEST_PARSE("7not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL);
1266         TEST_PARSE("123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, 0.0, 200.0);
1267         TEST_PARSE("-123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -200.0, 100.0);
1268         TEST_PARSE("0", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -1.0, 0.0);
1269         TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, 0.0, 122.0);
1270         TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -122.0, 100.0);
1271         TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, 1.0, 100.0);
1272         TEST_PARSE("not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL);
1273         TEST_PARSE("7not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL);
1274
1275         /* ast_sockaddr_parse is tested extensively in test_netsock2.c and PARSE_ADDR is a very simple wrapper */
1276
1277         return ret;
1278 }
1279 struct test_item {
1280         AST_DECLARE_STRING_FIELDS(
1281                 AST_STRING_FIELD(name);
1282                 AST_STRING_FIELD(stropt);
1283         );
1284         int32_t intopt;
1285         uint32_t uintopt;
1286         unsigned int flags;
1287         double doubleopt;
1288         struct ast_sockaddr sockaddropt;
1289         int boolopt;
1290         struct ast_ha *aclopt;
1291         struct ast_format_cap *codeccapopt;
1292         unsigned int customopt:1;
1293 };
1294 struct test_config {
1295         struct test_item *global;
1296         struct test_item *global_defaults;
1297         struct ao2_container *items;
1298 };
1299
1300 static int test_item_hash(const void *obj, const int flags)
1301 {
1302         const struct test_item *item = obj;
1303         const char *name = (flags & OBJ_KEY) ? obj : item->name;
1304         return ast_str_case_hash(name);
1305 }
1306 static int test_item_cmp(void *obj, void *arg, int flags)
1307 {
1308         struct test_item *one = obj, *two = arg;
1309         const char *match = (flags & OBJ_KEY) ? arg : two->name;
1310         return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
1311 }
1312 static void test_item_destructor(void *obj)
1313 {
1314         struct test_item *item = obj;
1315         ast_string_field_free_memory(item);
1316         ao2_cleanup(item->codeccapopt);
1317         if (item->aclopt) {
1318                 ast_free_ha(item->aclopt);
1319         }
1320         return;
1321 }
1322 static void *test_item_alloc(const char *cat)
1323 {
1324         struct test_item *item;
1325         if (!(item = ao2_alloc(sizeof(*item), test_item_destructor))) {
1326                 return NULL;
1327         }
1328         if (ast_string_field_init(item, 128)) {
1329                 ao2_ref(item, -1);
1330                 return NULL;
1331         }
1332         if (!(item->codeccapopt = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
1333                 ao2_ref(item, -1);
1334                 return NULL;
1335         }
1336         ast_string_field_set(item, name, cat);
1337         return item;
1338 }
1339 static void test_config_destructor(void *obj)
1340 {
1341         struct test_config *cfg = obj;
1342         ao2_cleanup(cfg->global);
1343         ao2_cleanup(cfg->global_defaults);
1344         ao2_cleanup(cfg->items);
1345 }
1346 static void *test_config_alloc(void)
1347 {
1348         struct test_config *cfg;
1349         if (!(cfg = ao2_alloc(sizeof(*cfg), test_config_destructor))) {
1350                 goto error;
1351         }
1352         if (!(cfg->global = test_item_alloc("global"))) {
1353                 goto error;
1354         }
1355         if (!(cfg->global_defaults = test_item_alloc("global_defaults"))) {
1356                 goto error;
1357         }
1358         if (!(cfg->items = ao2_container_alloc(1, test_item_hash, test_item_cmp))) {
1359                 goto error;
1360         }
1361         return cfg;
1362 error:
1363         ao2_cleanup(cfg);
1364         return NULL;
1365 }
1366 static void *test_item_find(struct ao2_container *container, const char *cat)
1367 {
1368         return ao2_find(container, cat, OBJ_KEY);
1369 }
1370
1371 static int customopt_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1372 {
1373         struct test_item *item = obj;
1374         if (!strcasecmp(var->name, "customopt")) {
1375                 item->customopt = ast_true(var->value);
1376         } else {
1377                 return -1;
1378         }
1379
1380         return 0;
1381 }
1382
1383 static struct aco_type global = {
1384         .type = ACO_GLOBAL,
1385         .item_offset = offsetof(struct test_config, global),
1386         .category_match = ACO_WHITELIST,
1387         .category = "^global$",
1388 };
1389 static struct aco_type global_defaults = {
1390         .type = ACO_GLOBAL,
1391         .item_offset = offsetof(struct test_config, global_defaults),
1392         .category_match = ACO_WHITELIST,
1393         .category = "^global_defaults$",
1394 };
1395 static struct aco_type item = {
1396         .type = ACO_ITEM,
1397         .category_match = ACO_BLACKLIST,
1398         .category = "^(global|global_defaults)$",
1399         .item_alloc = test_item_alloc,
1400         .item_find = test_item_find,
1401         .item_offset = offsetof(struct test_config, items),
1402 };
1403
1404 struct aco_file config_test_conf = {
1405         .filename = "config_test.conf",
1406         .types = ACO_TYPES(&global, &global_defaults, &item),
1407 };
1408
1409 static AO2_GLOBAL_OBJ_STATIC(global_obj);
1410 CONFIG_INFO_TEST(cfg_info, global_obj, test_config_alloc,
1411         .files = ACO_FILES(&config_test_conf),
1412 );
1413
1414 AST_TEST_DEFINE(config_options_test)
1415 {
1416         int res = AST_TEST_PASS, x, error;
1417         struct test_item defaults = { 0, }, configs = { 0, };
1418         struct test_item *arr[4];
1419         struct ast_sockaddr acl_allow = {{ 0, }}, acl_fail = {{ 0, }};
1420         RAII_VAR(struct test_config *, cfg, NULL, ao2_cleanup);
1421         RAII_VAR(struct test_item *, item, NULL, ao2_cleanup);
1422         RAII_VAR(struct test_item *, item_defaults, NULL, ao2_cleanup);
1423
1424         switch (cmd) {
1425         case TEST_INIT:
1426                 info->name = "config_options_test";
1427                 info->category = "/config/";
1428                 info->summary = "Config opptions unit test";
1429                 info->description =
1430                         "Tests the Config Options API";
1431                 return AST_TEST_NOT_RUN;
1432         case TEST_EXECUTE:
1433                 break;
1434         }
1435
1436 #define INT_DEFAULT "-2"
1437 #define INT_CONFIG "-1"
1438 #define UINT_DEFAULT "2"
1439 #define UINT_CONFIG "1"
1440 #define DOUBLE_DEFAULT "1.1"
1441 #define DOUBLE_CONFIG "0.1"
1442 #define SOCKADDR_DEFAULT "4.3.2.1:4321"
1443 #define SOCKADDR_CONFIG "1.2.3.4:1234"
1444 #define BOOL_DEFAULT "false"
1445 #define BOOL_CONFIG "true"
1446 #define BOOLFLAG1_DEFAULT "false"
1447 #define BOOLFLAG1_CONFIG "true"
1448 #define BOOLFLAG2_DEFAULT "false"
1449 #define BOOLFLAG2_CONFIG "false"
1450 #define BOOLFLAG3_DEFAULT "false"
1451 #define BOOLFLAG3_CONFIG "true"
1452 #define ACL_DEFAULT NULL
1453 #define ACL_CONFIG_PERMIT "1.2.3.4/32"
1454 #define ACL_CONFIG_DENY "0.0.0.0/0"
1455 #define CODEC_DEFAULT "!all,alaw"
1456 #define CODEC_CONFIG "!all,ulaw,g729"
1457 #define STR_DEFAULT "default"
1458 #define STR_CONFIG "test"
1459 #define CUSTOM_DEFAULT "no"
1460 #define CUSTOM_CONFIG "yes"
1461
1462 #define BOOLFLAG1 1 << 0
1463 #define BOOLFLAG2 1 << 1
1464 #define BOOLFLAG3 1 << 2
1465
1466         if (aco_info_init(&cfg_info)) {
1467                 ast_test_status_update(test, "Could not init cfg info\n");
1468                 return AST_TEST_FAIL;
1469         }
1470
1471         /* Register all options */
1472         aco_option_register(&cfg_info, "intopt", ACO_EXACT, config_test_conf.types, INT_DEFAULT, OPT_INT_T, 0, FLDSET(struct test_item, intopt));
1473         aco_option_register(&cfg_info, "uintopt", ACO_EXACT, config_test_conf.types, UINT_DEFAULT, OPT_UINT_T, 0, FLDSET(struct test_item, uintopt));
1474         aco_option_register(&cfg_info, "doubleopt", ACO_EXACT, config_test_conf.types, DOUBLE_DEFAULT, OPT_DOUBLE_T, 0, FLDSET(struct test_item, doubleopt));
1475         aco_option_register(&cfg_info, "sockaddropt", ACO_EXACT, config_test_conf.types, SOCKADDR_DEFAULT, OPT_SOCKADDR_T, 0, FLDSET(struct test_item, sockaddropt));
1476         aco_option_register(&cfg_info, "boolopt", ACO_EXACT, config_test_conf.types, BOOL_DEFAULT, OPT_BOOL_T, 1, FLDSET(struct test_item, boolopt));
1477         aco_option_register(&cfg_info, "boolflag1", ACO_EXACT, config_test_conf.types, BOOLFLAG1_DEFAULT, OPT_BOOLFLAG_T, 1, FLDSET(struct test_item, flags), BOOLFLAG1);
1478         aco_option_register(&cfg_info, "boolflag2", ACO_EXACT, config_test_conf.types, BOOLFLAG2_DEFAULT, OPT_BOOLFLAG_T, 1, FLDSET(struct test_item, flags), BOOLFLAG2);
1479         aco_option_register(&cfg_info, "boolflag3", ACO_EXACT, config_test_conf.types, BOOLFLAG3_DEFAULT, OPT_BOOLFLAG_T, 1, FLDSET(struct test_item, flags), BOOLFLAG3);
1480         aco_option_register(&cfg_info, "aclpermitopt", ACO_EXACT, config_test_conf.types, ACL_DEFAULT, OPT_ACL_T, 1, FLDSET(struct test_item, aclopt));
1481         aco_option_register(&cfg_info, "acldenyopt", ACO_EXACT, config_test_conf.types, ACL_DEFAULT, OPT_ACL_T, 0, FLDSET(struct test_item, aclopt));
1482         aco_option_register(&cfg_info, "codecopt", ACO_EXACT, config_test_conf.types, CODEC_DEFAULT, OPT_CODEC_T, 1, FLDSET(struct test_item, codeccapopt));
1483         aco_option_register(&cfg_info, "stropt", ACO_EXACT, config_test_conf.types, STR_DEFAULT, OPT_STRINGFIELD_T, 0, STRFLDSET(struct test_item, stropt));
1484         aco_option_register_custom(&cfg_info, "customopt", ACO_EXACT, config_test_conf.types, CUSTOM_DEFAULT, customopt_handler, 0);
1485         aco_option_register_deprecated(&cfg_info, "permit", config_test_conf.types, "aclpermitopt");
1486         aco_option_register_deprecated(&cfg_info, "deny", config_test_conf.types, "acldenyopt");
1487
1488         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1489                 ast_test_status_update(test, "Could not parse config\n");
1490                 return AST_TEST_FAIL;
1491         }
1492
1493         ast_parse_arg(INT_DEFAULT, PARSE_INT32, &defaults.intopt);
1494         ast_parse_arg(INT_CONFIG, PARSE_INT32, &configs.intopt);
1495         ast_parse_arg(UINT_DEFAULT, PARSE_UINT32, &defaults.uintopt);
1496         ast_parse_arg(UINT_CONFIG, PARSE_UINT32, &configs.uintopt);
1497         ast_parse_arg(DOUBLE_DEFAULT, PARSE_DOUBLE, &defaults.doubleopt);
1498         ast_parse_arg(DOUBLE_CONFIG, PARSE_DOUBLE, &configs.doubleopt);
1499         ast_parse_arg(SOCKADDR_DEFAULT, PARSE_ADDR, &defaults.sockaddropt);
1500         ast_parse_arg(SOCKADDR_CONFIG, PARSE_ADDR, &configs.sockaddropt);
1501         defaults.boolopt = ast_true(BOOL_DEFAULT);
1502         configs.boolopt = ast_true(BOOL_CONFIG);
1503         ast_set2_flag(&defaults, ast_true(BOOLFLAG1_DEFAULT), BOOLFLAG1);
1504         ast_set2_flag(&defaults, ast_true(BOOLFLAG2_DEFAULT), BOOLFLAG2);
1505         ast_set2_flag(&defaults, ast_true(BOOLFLAG3_DEFAULT), BOOLFLAG3);
1506         ast_set2_flag(&configs, ast_true(BOOLFLAG1_CONFIG), BOOLFLAG1);
1507         ast_set2_flag(&configs, ast_true(BOOLFLAG2_CONFIG), BOOLFLAG2);
1508         ast_set2_flag(&configs, ast_true(BOOLFLAG3_CONFIG), BOOLFLAG3);
1509
1510         defaults.aclopt = NULL;
1511         configs.aclopt = ast_append_ha("deny", ACL_CONFIG_DENY, configs.aclopt, &error);
1512         configs.aclopt = ast_append_ha("permit", ACL_CONFIG_PERMIT, configs.aclopt, &error);
1513         ast_sockaddr_parse(&acl_allow, "1.2.3.4", PARSE_PORT_FORBID);
1514         ast_sockaddr_parse(&acl_fail, "1.1.1.1", PARSE_PORT_FORBID);
1515
1516         defaults.codeccapopt = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
1517         ast_format_cap_update_by_allow_disallow(defaults.codeccapopt, CODEC_DEFAULT, 1);
1518
1519         configs.codeccapopt = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
1520         ast_format_cap_update_by_allow_disallow(configs.codeccapopt, CODEC_CONFIG, 1);
1521
1522         ast_string_field_init(&defaults, 128);
1523         ast_string_field_init(&configs, 128);
1524         ast_string_field_set(&defaults, stropt, STR_DEFAULT);
1525         ast_string_field_set(&configs, stropt, STR_CONFIG);
1526
1527         defaults.customopt = ast_true(CUSTOM_DEFAULT);
1528         configs.customopt = ast_true(CUSTOM_CONFIG);
1529
1530
1531         cfg = ao2_global_obj_ref(global_obj);
1532         if (!(item = ao2_find(cfg->items, "item", OBJ_KEY))) {
1533                 ast_test_status_update(test, "could not look up 'item'\n");
1534                 return AST_TEST_FAIL;
1535         }
1536         if (!(item_defaults = ao2_find(cfg->items, "item_defaults", OBJ_KEY))) {
1537                 ast_test_status_update(test, "could not look up 'item_defaults'\n");
1538                 return AST_TEST_FAIL;
1539         }
1540         arr[0] = cfg->global;
1541         arr[1] = item;
1542         arr[2] = cfg->global_defaults;
1543         arr[3] = item_defaults;
1544         /* Test global and item against configs, global_defaults and item_defaults against defaults */
1545
1546 #define NOT_EQUAL_FAIL(field, format)  \
1547         if (arr[x]->field != control->field) { \
1548                 ast_test_status_update(test, "%s did not match: " format " != " format " with x = %d\n", #field, arr[x]->field, control->field, x); \
1549                 res = AST_TEST_FAIL; \
1550         }
1551         for (x = 0; x < 4; x++) {
1552                 struct test_item *control = x < 2 ? &configs : &defaults;
1553
1554                 NOT_EQUAL_FAIL(intopt, "%d");
1555                 NOT_EQUAL_FAIL(uintopt, "%u");
1556                 NOT_EQUAL_FAIL(boolopt, "%d");
1557                 NOT_EQUAL_FAIL(flags, "%u");
1558                 NOT_EQUAL_FAIL(customopt, "%d");
1559                 if (fabs(arr[x]->doubleopt - control->doubleopt) > 0.001) {
1560                         ast_test_status_update(test, "doubleopt did not match: %f vs %f on loop %d\n", arr[x]->doubleopt, control->doubleopt, x);
1561                         res = AST_TEST_FAIL;
1562                 }
1563                 if (ast_sockaddr_cmp(&arr[x]->sockaddropt, &control->sockaddropt)) {
1564                         ast_test_status_update(test, "sockaddr did not match on loop %d\n", x);
1565                         res = AST_TEST_FAIL;
1566                 }
1567                 if (!ast_format_cap_identical(arr[x]->codeccapopt, control->codeccapopt)) {
1568                         struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
1569                         struct ast_str *codec_buf2 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
1570
1571                         ast_test_status_update(test, "format did not match: '%s' vs '%s' on loop %d\n",
1572                                 ast_format_cap_get_names(arr[x]->codeccapopt, &codec_buf1),
1573                                 ast_format_cap_get_names(control->codeccapopt, &codec_buf2),
1574                                 x);
1575                         res = AST_TEST_FAIL;
1576                 }
1577                 if (strcasecmp(arr[x]->stropt, control->stropt)) {
1578                         ast_test_status_update(test, "stropt did not match: '%s' vs '%s' on loop %d\n", arr[x]->stropt, control->stropt, x);
1579                         res = AST_TEST_FAIL;
1580                 }
1581                 if (arr[x]->aclopt != control->aclopt && (ast_apply_ha(arr[x]->aclopt, &acl_allow) != ast_apply_ha(control->aclopt, &acl_allow) ||
1582                                 ast_apply_ha(arr[x]->aclopt, &acl_fail) != ast_apply_ha(control->aclopt, &acl_fail))) {
1583                         ast_test_status_update(test, "acl not match: on loop %d\n", x);
1584                         res = AST_TEST_FAIL;
1585                 }
1586         }
1587
1588         ast_free_ha(configs.aclopt);
1589         ao2_cleanup(defaults.codeccapopt);
1590         defaults.codeccapopt = NULL;
1591         ao2_cleanup(configs.codeccapopt);
1592         configs.codeccapopt = NULL;
1593         ast_string_field_free_memory(&defaults);
1594         ast_string_field_free_memory(&configs);
1595         return res;
1596 }
1597
1598 AST_TEST_DEFINE(config_dialplan_function)
1599 {
1600         enum ast_test_result_state res = AST_TEST_PASS;
1601         FILE *config_file;
1602         char filename[PATH_MAX];
1603         struct ast_str *buf;
1604
1605         switch (cmd) {
1606         case TEST_INIT:
1607                 info->name = "config_dialplan_function";
1608                 info->category = "/main/config/";
1609                 info->summary = "Test AST_CONFIG dialplan function";
1610                 info->description = "Test AST_CONFIG dialplan function";
1611                 return AST_TEST_NOT_RUN;
1612         case TEST_EXECUTE:
1613                 break;
1614         }
1615
1616         snprintf(filename, sizeof(filename), "%s/%s",
1617                         ast_config_AST_CONFIG_DIR, CONFIG_FILE);
1618         config_file = fopen(filename, "w");
1619
1620         if (!config_file) {
1621                 return AST_TEST_FAIL;
1622         }
1623
1624         fputs(
1625                 "[c1t](!)\n"
1626                 "var1=val1\n"
1627                 "var1=val2\n"
1628                 "var2=val21\n"
1629                 "\n"
1630                 "[c1](c1t)\n"
1631                 "var1=val3\n"
1632                 "var1=val4\n"
1633                 , config_file);
1634
1635         fclose(config_file);
1636
1637         if (!(buf = ast_str_create(32))) {
1638                 ast_test_status_update(test, "Failed to allocate return buffer\n");
1639                 res = AST_TEST_FAIL;
1640                 goto out;
1641         }
1642
1643         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1)", &buf, 32)) {
1644                 ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
1645                 res = AST_TEST_FAIL;
1646                 goto out;
1647         }
1648         if (strcmp(ast_str_buffer(buf), "val1")) {
1649                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1650                         ast_str_buffer(buf), "val1");
1651                 res = AST_TEST_FAIL;
1652                 goto out;
1653         }
1654
1655         ast_str_reset(buf);
1656         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,0)", &buf, 32)) {
1657                 ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
1658                 res = AST_TEST_FAIL;
1659                 goto out;
1660         }
1661         if (strcmp(ast_str_buffer(buf), "val1")) {
1662                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1663                         ast_str_buffer(buf), "val1");
1664                 res = AST_TEST_FAIL;
1665                 goto out;
1666         }
1667
1668         ast_str_reset(buf);
1669         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,1)", &buf, 32)) {
1670                 ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
1671                 res = AST_TEST_FAIL;
1672                 goto out;
1673         }
1674         if (strcmp(ast_str_buffer(buf), "val2")) {
1675                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1676                         ast_str_buffer(buf), "val2");
1677                 res = AST_TEST_FAIL;
1678                 goto out;
1679         }
1680
1681         ast_str_reset(buf);
1682         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,2)", &buf, 32)) {
1683                 ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
1684                 res = AST_TEST_FAIL;
1685                 goto out;
1686         }
1687         if (strcmp(ast_str_buffer(buf), "val3")) {
1688                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1689                         ast_str_buffer(buf), "val3");
1690                 res = AST_TEST_FAIL;
1691                 goto out;
1692         }
1693
1694         ast_str_reset(buf);
1695         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,3)", &buf, 32)) {
1696                 ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
1697                 res = AST_TEST_FAIL;
1698                 goto out;
1699         }
1700         if (strcmp(ast_str_buffer(buf), "val4")) {
1701                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1702                         ast_str_buffer(buf), "val4");
1703                 res = AST_TEST_FAIL;
1704                 goto out;
1705         }
1706
1707         ast_str_reset(buf);
1708         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,-1)", &buf, 32)) {
1709                 ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
1710                 res = AST_TEST_FAIL;
1711                 goto out;
1712         }
1713         if (strcmp(ast_str_buffer(buf), "val4")) {
1714                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1715                         ast_str_buffer(buf), "val4");
1716                 res = AST_TEST_FAIL;
1717                 goto out;
1718         }
1719
1720         ast_str_reset(buf);
1721         if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var2,-1)", &buf, 32)) {
1722                 ast_test_status_update(test, "Failed to retrieve field 'var2'\n");
1723                 res = AST_TEST_FAIL;
1724                 goto out;
1725         }
1726         if (strcmp(ast_str_buffer(buf), "val21")) {
1727                 ast_test_status_update(test, "Got '%s', should be '%s'\n",
1728                         ast_str_buffer(buf), "val21");
1729                 res = AST_TEST_FAIL;
1730                 goto out;
1731         }
1732
1733         ast_str_reset(buf);
1734         if (!ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,5)", &buf, 32)) {
1735                 ast_test_status_update(test, "Should not have retrieved a value\n");
1736                 res = AST_TEST_FAIL;
1737                 goto out;
1738         }
1739
1740 out:
1741         if (buf) {
1742                 ast_free(buf);
1743         }
1744         delete_config_file();
1745         return res;
1746 }
1747
1748 AST_TEST_DEFINE(variable_lists_match)
1749 {
1750         RAII_VAR(struct ast_variable *, left, NULL, ast_variables_destroy);
1751         RAII_VAR(struct ast_variable *, right, NULL, ast_variables_destroy);
1752         struct ast_variable *var;
1753
1754         switch (cmd) {
1755         case TEST_INIT:
1756                 info->name = "variable_lists_match";
1757                 info->category = "/main/config/";
1758                 info->summary = "Test ast_variable_lists_match";
1759                 info->description =     "Test ast_variable_lists_match";
1760                 return AST_TEST_NOT_RUN;
1761         case TEST_EXECUTE:
1762                 break;
1763         }
1764
1765         var = ast_variable_new("aaa", "111", "");
1766         ast_test_validate(test, var);
1767         left = var;
1768         var = ast_variable_new("bbb", "222", "");
1769         ast_test_validate(test, var);
1770         ast_variable_list_append(&left, var);
1771
1772         var = ast_variable_new("aaa", "111", "");
1773         ast_test_validate(test, var);
1774         right = var;
1775
1776         ast_test_validate(test, ast_variable_lists_match(left, right, 0));
1777         ast_test_validate(test, !ast_variable_lists_match(left, right, 1));
1778
1779         var = ast_variable_new("bbb", "222", "");
1780         ast_test_validate(test, var);
1781         ast_variable_list_append(&right, var);
1782
1783         ast_test_validate(test, ast_variable_lists_match(left, right, 0));
1784         ast_test_validate(test, ast_variable_lists_match(left, right, 1));
1785
1786         var = ast_variable_new("ccc >", "333", "");
1787         ast_test_validate(test, var);
1788         ast_variable_list_append(&right, var);
1789
1790         ast_test_validate(test, !ast_variable_lists_match(left, right, 0));
1791         ast_test_validate(test, !ast_variable_lists_match(left, right, 1));
1792
1793         var = ast_variable_new("ccc", "444", "");
1794         ast_test_validate(test, var);
1795         ast_variable_list_append(&left, var);
1796
1797         ast_test_validate(test, ast_variable_lists_match(left, right, 0));
1798         ast_test_validate(test, !ast_variable_lists_match(left, right, 1));
1799
1800         ast_test_validate(test, !ast_variable_lists_match(left, NULL, 0));
1801         ast_test_validate(test, ast_variable_lists_match(NULL, NULL, 0));
1802         ast_test_validate(test, !ast_variable_lists_match(NULL, right, 0));
1803         ast_test_validate(test, ast_variable_lists_match(left, left, 0));
1804
1805         return AST_TEST_PASS;
1806 }
1807
1808 static int unload_module(void)
1809 {
1810         AST_TEST_UNREGISTER(config_save);
1811         AST_TEST_UNREGISTER(config_basic_ops);
1812         AST_TEST_UNREGISTER(config_filtered_ops);
1813         AST_TEST_UNREGISTER(config_template_ops);
1814         AST_TEST_UNREGISTER(copy_config);
1815         AST_TEST_UNREGISTER(config_hook);
1816         AST_TEST_UNREGISTER(ast_parse_arg_test);
1817         AST_TEST_UNREGISTER(config_options_test);
1818         AST_TEST_UNREGISTER(config_dialplan_function);
1819         AST_TEST_UNREGISTER(variable_lists_match);
1820         return 0;
1821 }
1822
1823 static int load_module(void)
1824 {
1825         AST_TEST_REGISTER(config_save);
1826         AST_TEST_REGISTER(config_basic_ops);
1827         AST_TEST_REGISTER(config_filtered_ops);
1828         AST_TEST_REGISTER(config_template_ops);
1829         AST_TEST_REGISTER(copy_config);
1830         AST_TEST_REGISTER(config_hook);
1831         AST_TEST_REGISTER(ast_parse_arg_test);
1832         AST_TEST_REGISTER(config_options_test);
1833         AST_TEST_REGISTER(config_dialplan_function);
1834         AST_TEST_REGISTER(variable_lists_match);
1835         return AST_MODULE_LOAD_SUCCESS;
1836 }
1837
1838 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Config test module");
1839