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