/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2010, Digium, Inc. * * Mark Michelson * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! * \file * \brief Configuration unit tests * * \author Mark Michelson * */ /*** MODULEINFO TEST_FRAMEWORK core ***/ #include "asterisk.h" #include /* HUGE_VAL */ #include #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/test.h" #include "asterisk/paths.h" #include "asterisk/config_options.h" #include "asterisk/netsock2.h" #include "asterisk/acl.h" #include "asterisk/app.h" #include "asterisk/pbx.h" #include "asterisk/frame.h" #include "asterisk/utils.h" #include "asterisk/logger.h" #include "asterisk/format_cap.h" #define CONFIG_FILE "test_config.conf" #define CONFIG_INCLUDE_FILE "test_config_include.conf" /* * This builds the folowing config: * [Capitals] * Germany = Berlin * China = Beijing * Canada = Ottawa * * [Protagonists] * 1984 = Winston Smith * Green Eggs And Ham = Sam I Am * The Kalevala = Vainamoinen * * This config is used for all tests below. */ const char cat1[] = "Capitals"; const char cat1varname1[] = "Germany"; const char cat1varvalue1[] = "Berlin"; const char cat1varname2[] = "China"; const char cat1varvalue2[] = "Beijing"; const char cat1varname3[] = "Canada"; const char cat1varvalue3[] = "Ottawa"; const char cat2[] = "Protagonists"; const char cat2varname1[] = "1984"; const char cat2varvalue1[] = "Winston Smith"; const char cat2varname2[] = "Green Eggs And Ham"; const char cat2varvalue2[] = "Sam I Am"; const char cat2varname3[] = "The Kalevala"; const char cat2varvalue3[] = "Vainamoinen"; struct pair { const char *name; const char *val; }; struct association { const char *category; struct pair vars[3]; } categories [] = { { cat1, { { cat1varname1, cat1varvalue1 }, { cat1varname2, cat1varvalue2 }, { cat1varname3, cat1varvalue3 }, } }, { cat2, { { cat2varname1, cat2varvalue1 }, { cat2varname2, cat2varvalue2 }, { cat2varname3, cat2varvalue3 }, } }, }; /*! * \brief Build ast_config struct from above definitions * * \retval NULL Failed to build the config * \retval non-NULL An ast_config struct populated with data */ static struct ast_config *build_cfg(void) { struct ast_config *cfg; struct association *cat_iter; struct pair *var_iter; size_t i; size_t j; cfg = ast_config_new(); if (!cfg) { goto fail; } for (i = 0; i < ARRAY_LEN(categories); ++i) { struct ast_category *cat; cat_iter = &categories[i]; cat = ast_category_new(cat_iter->category, "", 999999); if (!cat) { goto fail; } ast_category_append(cfg, cat); for (j = 0; j < ARRAY_LEN(cat_iter->vars); ++j) { struct ast_variable *var; var_iter = &cat_iter->vars[j]; var = ast_variable_new(var_iter->name, var_iter->val, ""); if (!var) { goto fail; } ast_variable_append(cat, var); } } return cfg; fail: ast_config_destroy(cfg); return NULL; } /*! * \brief Tests that the contents of an ast_config is what is expected * * \param cfg Config to test * \retval -1 Failed to pass a test * \retval 0 Config passes checks */ static int test_config_validity(struct ast_config *cfg) { int i; const char *cat_iter = NULL; /* Okay, let's see if the correct content is there */ for (i = 0; i < ARRAY_LEN(categories); ++i) { struct ast_variable *var = NULL; size_t j; cat_iter = ast_category_browse(cfg, cat_iter); if (strcmp(cat_iter, categories[i].category)) { ast_log(LOG_ERROR, "Category name mismatch, %s does not match %s\n", cat_iter, categories[i].category); return -1; } for (j = 0; j < ARRAY_LEN(categories[i].vars); ++j) { var = var ? var->next : ast_variable_browse(cfg, cat_iter); if (strcmp(var->name, categories[i].vars[j].name)) { ast_log(LOG_ERROR, "Variable name mismatch, %s does not match %s\n", var->name, categories[i].vars[j].name); return -1; } if (strcmp(var->value, categories[i].vars[j].val)) { ast_log(LOG_ERROR, "Variable value mismatch, %s does not match %s\n", var->value, categories[i].vars[j].val); return -1; } } } return 0; } AST_TEST_DEFINE(copy_config) { enum ast_test_result_state res = AST_TEST_FAIL; struct ast_config *cfg = NULL; struct ast_config *copy = NULL; switch (cmd) { case TEST_INIT: info->name = "copy_config"; info->category = "/main/config/"; info->summary = "Test copying configuration"; info->description = "Ensure that variables and categories are copied correctly"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } cfg = build_cfg(); if (!cfg) { goto out; } copy = ast_config_copy(cfg); if (!copy) { goto out; } if (test_config_validity(copy) != 0) { goto out; } res = AST_TEST_PASS; out: ast_config_destroy(cfg); ast_config_destroy(copy); return res; } AST_TEST_DEFINE(config_basic_ops) { enum ast_test_result_state res = AST_TEST_FAIL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; struct ast_variable *var; struct ast_variable *varlist; char temp[32]; const char *cat_name; const char *var_value; int i; switch (cmd) { case TEST_INIT: info->name = "config_basic_ops"; info->category = "/main/config/"; info->summary = "Test basic config ops"; info->description = "Test basic config ops"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } cfg = ast_config_new(); if (!cfg) { return res; } /* load the config */ for(i = 0; i < 5; i++) { snprintf(temp, sizeof(temp), "test%d", i); ast_category_append(cfg, ast_category_new(temp, "dummy", -1)); } /* test0 test1 test2 test3 test4 */ /* check the config has 5 elements */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* search for test2 */ cat = ast_category_get(cfg, "test2", NULL); if (!cat || strcmp(ast_category_get_name(cat), "test2")) { ast_test_status_update(test, "Get failed %s != %s\n", ast_category_get_name(cat), "test2"); goto out; } /* delete test2 */ cat = ast_category_delete(cfg, cat); /* Now: test0 test1 test3 test4 */ /* make sure the curr category is test1 */ if (!cat || strcmp(ast_category_get_name(cat), "test1")) { ast_test_status_update(test, "Delete failed %s != %s\n", ast_category_get_name(cat), "test1"); goto out; } /* Now: test0 test1 test3 test4 */ /* make sure the test2 is not found */ cat = ast_category_get(cfg, "test2", NULL); if (cat) { ast_test_status_update(test, "Should not have found test2\n"); goto out; } /* Now: test0 test1 test3 test4 */ /* make sure the sequence is correctly missing test2 */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } i++; if (i == 2) { i++; } } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* insert test2 back in before test3 */ ast_category_insert(cfg, ast_category_new("test2", "dummy", -1), "test3"); /* Now: test0 test1 test2 test3 test4 */ /* make sure the sequence is correct again */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, NULL))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* Now: test0 test1 test2 test3 test4 */ /* make sure non filtered browse still works */ i = 0; cat_name = NULL; while ((cat_name = ast_category_browse(cfg, cat_name))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(cat_name, temp)) { ast_test_status_update(test, "%s != %s\n", cat_name, temp); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* append another test2 */ ast_category_append(cfg, ast_category_new("test2", "dummy", -1)); /* Now: test0 test1 test2 test3 test4 test2*/ /* make sure only test2's are returned */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, "test2", cat, NULL))) { if (strcmp(ast_category_get_name(cat), "test2")) { ast_test_status_update(test, "Should have returned test2 instead of %s\n", ast_category_get_name(cat)); goto out; } i++; } /* make sure 2 test2's were found */ if (i != 2) { ast_test_status_update(test, "Should have found 2 test2's %d\n", i); goto out; } /* Test in-flight deletion using ast_category_browse_filtered */ /* Now: test0 test1 test2 test3 test4 test2 */ /* Delete the middle test2 and continue */ cat = NULL; for(i = 0; i < 5; i++) { snprintf(temp, sizeof(temp), "test%d", i); cat = ast_category_browse_filtered(cfg, NULL, cat, NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, temp)) { ast_test_status_update(test, "Should have returned %s instead of %s: %d\n", temp, cat_name, i); goto out; } if (i == 2) { cat = ast_category_delete(cfg, cat); } } /* Now: test0 test3 test4 test2 */ /* delete the head item */ cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, "test0")) { ast_test_status_update(test, "Should have returned test0 instead of %s\n", cat_name); goto out; } ast_category_delete(cfg, cat); /* Now: test3 test4 test2 */ /* make sure head got updated to the new first element */ cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, "test1")) { ast_test_status_update(test, "Should have returned test3 instead of %s\n", cat_name); goto out; } /* delete the tail item */ cat = ast_category_get(cfg, "test2", NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, "test2")) { ast_test_status_update(test, "Should have returned test2 instead of %s\n", cat_name); goto out; } ast_category_delete(cfg, cat); /* Now: test3 test4 */ /* There should now only be 2 elements in the list */ cat = NULL; cat = ast_category_browse_filtered(cfg, NULL, cat, NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, "test1")) { ast_test_status_update(test, "Should have returned test1 instead of %s\n", cat_name); goto out; } cat = ast_category_browse_filtered(cfg, NULL, cat, NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, "test3")) { ast_test_status_update(test, "Should have returned test3 instead of %s\n", cat_name); goto out; } cat = ast_category_browse_filtered(cfg, NULL, cat, NULL); cat_name = ast_category_get_name(cat); if (strcmp(cat_name, "test4")) { ast_test_status_update(test, "Should have returned test4 instead of %s\n", cat_name); goto out; } /* There should be nothing more */ cat = ast_category_browse_filtered(cfg, NULL, cat, NULL); if (cat) { ast_test_status_update(test, "Should not have returned anything\n"); goto out; } /* Test ast_variable retrieve. * Get the second category. */ cat = ast_category_browse_filtered(cfg, NULL, NULL, NULL); cat = ast_category_browse_filtered(cfg, NULL, cat, NULL); cat_name = ast_category_get_name(cat); var = ast_variable_new("aaa", "bbb0", "dummy"); if (!var) { ast_test_status_update(test, "Couldn't allocate variable.\n"); goto out; } ast_variable_append(cat, var); /* Make sure we can retrieve with specific category name */ var_value = ast_variable_retrieve(cfg, cat_name, "aaa"); if (!var_value || strcmp(var_value, "bbb0")) { ast_test_status_update(test, "Variable not found or wrong value.\n"); goto out; } /* Make sure we can retrieve with NULL category name */ var_value = ast_variable_retrieve(cfg, NULL, "aaa"); if (!var_value || strcmp(var_value, "bbb0")) { ast_test_status_update(test, "Variable not found or wrong value.\n"); goto out; } /* Now test variable retrieve inside a browse loop * with multiple categories of the same name */ cat = ast_category_new("test3", "dummy", -1); if (!cat) { ast_test_status_update(test, "Couldn't allocate category.\n"); goto out; } var = ast_variable_new("aaa", "bbb1", "dummy"); if (!var) { ast_test_status_update(test, "Couldn't allocate variable.\n"); goto out; } ast_variable_append(cat, var); ast_category_append(cfg, cat); cat = ast_category_new("test3", "dummy", -1); if (!cat) { ast_test_status_update(test, "Couldn't allocate category.\n"); goto out; } var = ast_variable_new("aaa", "bbb2", "dummy"); if (!var) { ast_test_status_update(test, "Couldn't allocate variable.\n"); goto out; } ast_variable_append(cat, var); ast_category_append(cfg, cat); cat_name = NULL; i = 0; while ((cat_name = ast_category_browse(cfg, cat_name))) { if (!strcmp(cat_name, "test3")) { snprintf(temp, sizeof(temp), "bbb%d", i); var_value = ast_variable_retrieve(cfg, cat_name, "aaa"); if (!var_value || strcmp(var_value, temp)) { ast_test_status_update(test, "Variable not found or wrong value %s.\n", var_value); goto out; } var = ast_variable_browse(cfg, cat_name); if (!var->value || strcmp(var->value, temp)) { ast_test_status_update(test, "Variable not found or wrong value %s.\n", var->value); goto out; } i++; } } if (i != 3) { ast_test_status_update(test, "There should have been 3 matches instead of %d.\n", i); goto out; } varlist = ast_variable_new("name1", "value1", ""); ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value2", "")); ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value3", "")); var_value = ast_variable_find_in_list(varlist, "name1"); if (strcmp(var_value, "value1") != 0) { ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value); goto out; } var_value = ast_variable_find_last_in_list(varlist, "name1"); if (strcmp(var_value, "value3") != 0) { ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value); goto out; } res = AST_TEST_PASS; out: ast_config_destroy(cfg); return res; } AST_TEST_DEFINE(config_filtered_ops) { enum ast_test_result_state res = AST_TEST_FAIL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; char temp[32]; const char *value; int i; switch (cmd) { case TEST_INIT: info->name = "config_filtered_ops"; info->category = "/main/config/"; info->summary = "Test filtered config ops"; info->description = "Test filtered config ops"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } cfg = ast_config_new(); if (!cfg) { return res; } /* load the config */ for(i = 0; i < 5; i++) { snprintf(temp, sizeof(temp), "test%d", i); cat = ast_category_new(temp, "dummy", -1); ast_variable_insert(cat, ast_variable_new("type", "a", "dummy"), "0"); ast_category_append(cfg, cat); } for(i = 0; i < 5; i++) { snprintf(temp, sizeof(temp), "test%d", i); cat = ast_category_new(temp, "dummy", -1); ast_variable_insert(cat, ast_variable_new("type", "b", "dummy"), "0"); ast_category_append(cfg, cat); } /* check the config has 5 elements for each type*/ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "type=a"))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } value = ast_variable_find(cat, "type"); if (!value || strcmp(value, "a")) { ast_test_status_update(test, "Type %s != %s\n", "a", value); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "type=b"))) { snprintf(temp, sizeof(temp), "test%d", i); if (!cat || strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } value = ast_variable_find(cat, "type"); if (!value || strcmp(value, "b")) { ast_test_status_update(test, "Type %s != %s\n", "b", value); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* Delete b3 and make sure it's gone and a3 is still there. * Really this is a test of get since delete takes a specific category structure. */ cat = ast_category_get(cfg, "test3", "type=b"); value = ast_variable_find(cat, "type"); if (strcmp(value, "b")) { ast_test_status_update(test, "Type %s != %s\n", "b", value); goto out; } ast_category_delete(cfg, cat); cat = ast_category_get(cfg, "test3", "type=b"); if (cat) { ast_test_status_update(test, "Category b was not deleted.\n"); goto out; } cat = ast_category_get(cfg, "test3", "type=a"); if (!cat) { ast_test_status_update(test, "Category a was deleted.\n"); goto out; } value = ast_variable_find(cat, "type"); if (strcmp(value, "a")) { ast_test_status_update(test, "Type %s != %s\n", value, "a"); goto out; } /* Basic regex stuff is handled by regcomp/regexec so not testing here. * Still need to test multiple name/value pairs though. */ ast_category_empty(cat); ast_variable_insert(cat, ast_variable_new("type", "bx", "dummy"), "0"); ast_variable_insert(cat, ast_variable_new("e", "z", "dummy"), "0"); cat = ast_category_get(cfg, "test3", "type=.,e=z"); if (!cat) { ast_test_status_update(test, "Category not found.\n"); goto out; } cat = ast_category_get(cfg, "test3", "type=.,e=zX"); if (cat) { ast_test_status_update(test, "Category found.\n"); goto out; } cat = ast_category_get(cfg, "test3", "TEMPLATE=restrict,type=.,e=z"); if (cat) { ast_test_status_update(test, "Category found.\n"); goto out; } res = AST_TEST_PASS; out: ast_config_destroy(cfg); return res; } AST_TEST_DEFINE(config_template_ops) { enum ast_test_result_state res = AST_TEST_FAIL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; char temp[32]; const char *value; int i; switch (cmd) { case TEST_INIT: info->name = "config_template_ops"; info->category = "/main/config/"; info->summary = "Test template config ops"; info->description = "Test template config ops"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } cfg = ast_config_new(); if (!cfg) { return res; } /* load the config with 5 templates and 5 regular */ for(i = 0; i < 5; i++) { snprintf(temp, sizeof(temp), "test%d", i); cat = ast_category_new_template(temp, "dummy", -1); ast_variable_insert(cat, ast_variable_new("type", "a", "dummy"), "0"); ast_category_append(cfg, cat); } for(i = 0; i < 5; i++) { snprintf(temp, sizeof(temp), "test%d", i); cat = ast_category_new(temp, "dummy", -1); ast_variable_insert(cat, ast_variable_new("type", "b", "dummy"), "0"); ast_category_append(cfg, cat); } /* check the config has 5 template elements of type a */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=restrict,type=a"))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } value = ast_variable_find(cat, "type"); if (!value || strcmp(value, "a")) { ast_test_status_update(test, "Type %s != %s\n", value, "a"); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* Test again with 'include'. There should still only be 5 (type a) */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=include,type=a"))) { snprintf(temp, sizeof(temp), "test%d", i); if (strcmp(ast_category_get_name(cat), temp)) { ast_test_status_update(test, "%s != %s\n", ast_category_get_name(cat), temp); goto out; } value = ast_variable_find(cat, "type"); if (!value || strcmp(value, "a")) { ast_test_status_update(test, "Type %s != %s\n", value, "a"); goto out; } i++; } if (i != 5) { ast_test_status_update(test, "There were %d matches instead of 5.\n", i); goto out; } /* Test again with 'include' but no type. There should now be 10 (type a and type b) */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=include"))) { i++; } if (i != 10) { ast_test_status_update(test, "There were %d matches instead of 10.\n", i); goto out; } /* Test again with 'restrict' and type b. There should 0 */ i = 0; cat = NULL; while ((cat = ast_category_browse_filtered(cfg, NULL, cat, "TEMPLATES=restrict,type=b"))) { i++; } if (i != 0) { ast_test_status_update(test, "There were %d matches instead of 0.\n", i); goto out; } res = AST_TEST_PASS; out: ast_config_destroy(cfg); return res; } /*! * \brief Write the config file to disk * * This is necessary for testing config hooks since * they are only triggered when a config is read from * its intended storage medium */ static int write_config_file(void) { int i; FILE *config_file; char filename[PATH_MAX]; snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_FILE); config_file = fopen(filename, "w"); if (!config_file) { return -1; } for (i = 0; i < ARRAY_LEN(categories); ++i) { int j; fprintf(config_file, "[%s]\n", categories[i].category); for (j = 0; j < ARRAY_LEN(categories[i].vars); ++j) { fprintf(config_file, "%s = %s\n", categories[i].vars[j].name, categories[i].vars[j].val); } } fclose(config_file); return 0; } /*! * \brief Delete config file created by write_config_file */ static void delete_config_file(void) { char filename[PATH_MAX]; snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_FILE); unlink(filename); } /* * Boolean to indicate if the config hook has run */ static int hook_run; /* * Boolean to indicate if, when the hook runs, the * data passed to it is what is expected */ static int hook_config_sane; static int hook_cb(struct ast_config *cfg) { hook_run = 1; if (test_config_validity(cfg) == 0) { hook_config_sane = 1; } ast_config_destroy(cfg); return 0; } AST_TEST_DEFINE(config_save) { enum ast_test_result_state res = AST_TEST_FAIL; struct ast_flags config_flags = { 0 }; struct ast_config *cfg; char config_filename[PATH_MAX]; char include_filename[PATH_MAX]; struct stat config_stat; off_t before_save; switch (cmd) { case TEST_INIT: info->name = "config_save"; info->category = "/main/config/"; info->summary = "Test config save"; info->description = "Test configuration save."; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } if (write_config_file()) { ast_test_status_update(test, "Could not write initial config files\n"); return res; } snprintf(config_filename, PATH_MAX, "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_FILE); snprintf(include_filename, PATH_MAX, "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_INCLUDE_FILE); cfg = ast_config_load(CONFIG_FILE, config_flags); if (!cfg) { ast_test_status_update(test, "Could not load config\n"); goto out; } /* We need to re-save to get the generator header */ if (ast_config_text_file_save(CONFIG_FILE, cfg, "TEST")) { ast_test_status_update(test, "Unable to write files\n"); goto out; } stat(config_filename, &config_stat); before_save = config_stat.st_size; if (!ast_include_new(cfg, CONFIG_FILE, CONFIG_INCLUDE_FILE, 0, NULL, 4, include_filename, PATH_MAX)) { ast_test_status_update(test, "Could not create include\n"); goto out; } if (ast_config_text_file_save(CONFIG_FILE, cfg, "TEST")) { ast_test_status_update(test, "Unable to write files\n"); goto out; } stat(config_filename, &config_stat); if (config_stat.st_size <= before_save) { ast_test_status_update(test, "Did not save config file with #include\n"); goto out; } res = AST_TEST_PASS; out: ast_config_destroy(cfg); unlink(config_filename); unlink(include_filename); return res; } AST_TEST_DEFINE(config_hook) { enum ast_test_result_state res = AST_TEST_FAIL; enum config_hook_flags hook_flags = { 0, }; struct ast_flags config_flags = { 0 }; struct ast_flags reload_flags = { CONFIG_FLAG_FILEUNCHANGED }; struct ast_config *cfg; switch (cmd) { case TEST_INIT: info->name = "config_hook"; info->category = "/main/config/"; info->summary = "Test config hooks"; info->description = "Ensure that config hooks are called at approriate times," "not called at inappropriate times, and that all information" "that should be present is present."; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } write_config_file(); /* * Register a config hook to run when CONFIG_FILE is loaded by this module */ ast_config_hook_register("test_hook", CONFIG_FILE, AST_MODULE, hook_flags, hook_cb); /* * Try loading the config file. This should result in the hook * being called */ cfg = ast_config_load(CONFIG_FILE, config_flags); ast_config_destroy(cfg); if (!hook_run || !hook_config_sane) { ast_test_status_update(test, "Config hook either did not run or was given bad data!\n"); goto out; } /* * Now try loading the wrong config file but from the right module. * Hook should not run */ hook_run = 0; cfg = ast_config_load("asterisk.conf", config_flags); ast_config_destroy(cfg); if (hook_run) { ast_test_status_update(test, "Config hook ran even though an incorrect file was specified.\n"); goto out; } /* * Now try loading the correct config file but from the wrong module. * Hook should not run */ hook_run = 0; cfg = ast_config_load2(CONFIG_FILE, "fake_module.so", config_flags); ast_config_destroy(cfg); if (hook_run) { ast_test_status_update(test, "Config hook ran even though an incorrect module was specified.\n"); goto out; } /* * Now try loading the file correctly, but without any changes to the file. * Hook should not run */ hook_run = 0; cfg = ast_config_load(CONFIG_FILE, reload_flags); /* Only destroy this cfg conditionally. Otherwise a crash happens. */ if (cfg != CONFIG_STATUS_FILEUNCHANGED) { ast_config_destroy(cfg); } if (hook_run) { ast_test_status_update(test, "Config hook ran even though file contents had not changed\n"); goto out; } res = AST_TEST_PASS; out: ast_config_hook_unregister("test_hook"); delete_config_file(); return res; } enum { EXPECT_FAIL = 0, EXPECT_SUCCEED, }; #define TOOBIG_I32 "2147483649" #define TOOSMALL_I32 "-2147483649" #define TOOBIG_U32 "4294967297" #define TOOSMALL_U32 "-4294967297" #define DEFAULTVAL 42 #define EPSILON 0.001 #define TEST_PARSE(input, should_succeed, expected_result, flags, result, ...) do {\ int __res = ast_parse_arg(input, (flags), result, ##__VA_ARGS__); \ if (!__res == !should_succeed) { \ ast_test_status_update(test, "ast_parse_arg failed on '%s'. %d/%d\n", input, __res, should_succeed); \ ret = AST_TEST_FAIL; \ } else { \ if (((flags) & PARSE_TYPE) == PARSE_INT32) { \ int32_t *r = (int32_t *) (void *) result; \ int32_t e = (int32_t) expected_result; \ if (*r != e) { \ ast_test_status_update(test, "ast_parse_arg int32_t failed with %d != %d\n", *r, e); \ ret = AST_TEST_FAIL; \ } \ } else if (((flags) & PARSE_TYPE) == PARSE_UINT32) { \ uint32_t *r = (uint32_t *) (void *) result; \ uint32_t e = (uint32_t) expected_result; \ if (*r != e) { \ ast_test_status_update(test, "ast_parse_arg uint32_t failed with %u != %u\n", *r, e); \ ret = AST_TEST_FAIL; \ } \ } else if (((flags) & PARSE_TYPE) == PARSE_DOUBLE) { \ double *r = (double *) (void *) result; \ double e = (double) expected_result; \ if (fabs(*r - e) > EPSILON) { \ ast_test_status_update(test, "ast_parse_arg double failed with %f != %f\n", *r, e); \ ret = AST_TEST_FAIL; \ } \ } else if (((flags) & PARSE_TYPE) == PARSE_TIMELEN) { \ int *r = (int *) (void *) result; \ int e = (int) expected_result; \ if (*r != e) { \ ast_test_status_update(test, "ast_parse_arg timelen failed with %d != %d\n", *r, e); \ ret = AST_TEST_FAIL; \ } \ } \ } \ *(result) = DEFAULTVAL; \ } while (0) AST_TEST_DEFINE(ast_parse_arg_test) { int ret = AST_TEST_PASS; int32_t int32_t_val = DEFAULTVAL; uint32_t uint32_t_val = DEFAULTVAL; int timelen_val = DEFAULTVAL; double double_val = DEFAULTVAL; switch (cmd) { case TEST_INIT: info->name = "ast_parse_arg"; info->category = "/config/"; info->summary = "Test the output of ast_parse_arg"; info->description = "Ensures that ast_parse_arg behaves as expected"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } /* int32 testing */ TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32, &int32_t_val); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32, &int32_t_val); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32, &int32_t_val); TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val); TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32, &int32_t_val); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT, &int32_t_val, 7); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, 0, 200); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, -200, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, -1, 0); TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, 0, 122); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, -122, 100); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, 1, 100); TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_IN_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, -200, 100); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, -1, 0); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, 0, 122); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, -122, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, 1, 100); TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_INT32 | PARSE_OUT_RANGE, &int32_t_val, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, 0, 200); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, -200, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, -1, 0); TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, 0, 122); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, -122, 100); TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, 1, 100); TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, -200, 100); TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, -1, 0); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, 0, 122); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, -122, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, 1, 100); TEST_PARSE(TOOBIG_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_I32, EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_INT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &int32_t_val, 7, INT_MIN, INT_MAX); /* uuint32 testing */ TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32, &uint32_t_val); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32, &uint32_t_val); TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val); TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT, &uint32_t_val, 7); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 200); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 1); TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 0, 122); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, 1, 100); TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_IN_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 200); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 1); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 0, 122); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, 1, 100); TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32 | PARSE_OUT_RANGE, &uint32_t_val, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 200); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 1); TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 0, 122); TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, 1, 100); TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_IN_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 100); TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 1); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 0, 122); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, 1, 100); TEST_PARSE(TOOBIG_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE(TOOSMALL_U32, EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_UINT32 | PARSE_DEFAULT | PARSE_OUT_RANGE, &uint32_t_val, 7, INT_MIN, INT_MAX); TEST_PARSE(" -123", EXPECT_FAIL, DEFAULTVAL, PARSE_UINT32, &uint32_t_val); /* timelen testing */ TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("123s", EXPECT_SUCCEED, 123000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("-123s", EXPECT_SUCCEED, -123000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("1m", EXPECT_SUCCEED, 60000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("1", EXPECT_SUCCEED, 60000, PARSE_TIMELEN, &timelen_val, TIMELEN_MINUTES); TEST_PARSE("1h", EXPECT_SUCCEED, 3600000, PARSE_TIMELEN, &timelen_val, TIMELEN_MILLISECONDS); TEST_PARSE("1", EXPECT_SUCCEED, 3600000, PARSE_TIMELEN, &timelen_val, TIMELEN_HOURS); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT, &timelen_val, TIMELEN_MILLISECONDS, 7); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 200); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -200, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -1, 0); TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 122); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -122, 100); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 1, 100); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -200, 100); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -1, 0); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 0, 122); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, -122, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 1, 100); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_TIMELEN | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 200); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -200, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -1, 0); TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 122); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -122, 100); TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 1, 100); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_IN_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX); TEST_PARSE("123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 200); TEST_PARSE("-123", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -200, 100); TEST_PARSE("0", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -1, 0); TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 0, 122); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, -122, 100); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, 1, 100); TEST_PARSE("not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX); TEST_PARSE("7not a number", EXPECT_FAIL, 7, PARSE_TIMELEN | PARSE_DEFAULT | PARSE_OUT_RANGE, &timelen_val, TIMELEN_MILLISECONDS, 7, INT_MIN, INT_MAX); /* double testing */ TEST_PARSE("123", EXPECT_SUCCEED, 123, PARSE_DOUBLE, &double_val); TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE, &double_val); TEST_PARSE("-123", EXPECT_SUCCEED, -123, PARSE_DOUBLE, &double_val); TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE, &double_val); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE, &double_val); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE, &double_val); TEST_PARSE("7.0not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE, &double_val); TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0); TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0); TEST_PARSE("not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0); TEST_PARSE("7.0not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT, &double_val, 7.0); TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, 0.0, 200.0); TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -200.0, 100.0); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -1.0, 0.0); TEST_PARSE("123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, 0.0, 122.0); TEST_PARSE("-123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -122.0, 100.0); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, 1.0, 100.0); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -HUGE_VAL, HUGE_VAL); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_IN_RANGE, &double_val, -HUGE_VAL, HUGE_VAL); TEST_PARSE("123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, 0.0, 200.0); TEST_PARSE("-123.123", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -200.0, 100.0); TEST_PARSE("0", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -1.0, 0.0); TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, 0.0, 122.0); TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -122.0, 100.0); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, 1.0, 100.0); TEST_PARSE("not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -HUGE_VAL, HUGE_VAL); TEST_PARSE("7not a number", EXPECT_FAIL, DEFAULTVAL, PARSE_DOUBLE | PARSE_OUT_RANGE, &double_val, -HUGE_VAL, HUGE_VAL); TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, 0.0, 200.0); TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -200.0, 100.0); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -1.0, 0.0); TEST_PARSE("123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, 0.0, 122.0); TEST_PARSE("-123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -122.0, 100.0); TEST_PARSE("0", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, 1.0, 100.0); TEST_PARSE("not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL); TEST_PARSE("7not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_IN_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL); TEST_PARSE("123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, 0.0, 200.0); TEST_PARSE("-123.123", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -200.0, 100.0); TEST_PARSE("0", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -1.0, 0.0); TEST_PARSE("123.123", EXPECT_SUCCEED, 123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, 0.0, 122.0); TEST_PARSE("-123.123", EXPECT_SUCCEED, -123.123, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -122.0, 100.0); TEST_PARSE("0", EXPECT_SUCCEED, 0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, 1.0, 100.0); TEST_PARSE("not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL); TEST_PARSE("7not a number", EXPECT_FAIL, 7.0, PARSE_DOUBLE | PARSE_DEFAULT | PARSE_OUT_RANGE, &double_val, 7.0, -HUGE_VAL, HUGE_VAL); /* ast_sockaddr_parse is tested extensively in test_netsock2.c and PARSE_ADDR is a very simple wrapper */ return ret; } struct test_item { AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(name); AST_STRING_FIELD(stropt); ); int32_t intopt; uint32_t uintopt; int timelenopt1; int timelenopt2; int timelenopt3; int timelenopt4; unsigned int flags; double doubleopt; struct ast_sockaddr sockaddropt; int boolopt; struct ast_ha *aclopt; struct ast_format_cap *codeccapopt; unsigned int customopt:1; }; struct test_config { struct test_item *global; struct test_item *global_defaults; struct ao2_container *items; }; static int test_item_cmp(void *obj, void *arg, int flags) { struct test_item *one = obj, *two = arg; const char *match = (flags & OBJ_KEY) ? arg : two->name; return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP); } static void test_item_destructor(void *obj) { struct test_item *item = obj; ast_string_field_free_memory(item); ao2_cleanup(item->codeccapopt); if (item->aclopt) { ast_free_ha(item->aclopt); } return; } static void *test_item_alloc(const char *cat) { struct test_item *item; if (!(item = ao2_alloc(sizeof(*item), test_item_destructor))) { return NULL; } if (ast_string_field_init(item, 128)) { ao2_ref(item, -1); return NULL; } if (!(item->codeccapopt = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { ao2_ref(item, -1); return NULL; } ast_string_field_set(item, name, cat); return item; } static void test_config_destructor(void *obj) { struct test_config *cfg = obj; ao2_cleanup(cfg->global); ao2_cleanup(cfg->global_defaults); ao2_cleanup(cfg->items); } static void *test_config_alloc(void) { struct test_config *cfg; if (!(cfg = ao2_alloc(sizeof(*cfg), test_config_destructor))) { goto error; } if (!(cfg->global = test_item_alloc("global"))) { goto error; } if (!(cfg->global_defaults = test_item_alloc("global_defaults"))) { goto error; } cfg->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, test_item_cmp); if (!cfg->items) { goto error; } return cfg; error: ao2_cleanup(cfg); return NULL; } static void *test_item_find(struct ao2_container *container, const char *cat) { return ao2_find(container, cat, OBJ_KEY); } static int customopt_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct test_item *item = obj; if (!strcasecmp(var->name, "customopt")) { item->customopt = ast_true(var->value); } else { return -1; } return 0; } static struct aco_type global = { .type = ACO_GLOBAL, .item_offset = offsetof(struct test_config, global), .category_match = ACO_WHITELIST, .category = "^global$", }; static struct aco_type global_defaults = { .type = ACO_GLOBAL, .item_offset = offsetof(struct test_config, global_defaults), .category_match = ACO_WHITELIST_EXACT, .category = "global_defaults", }; static const char *item_blacklist[] = { "global", "global_defaults", NULL, }; static struct aco_type item = { .type = ACO_ITEM, .category_match = ACO_BLACKLIST_ARRAY, .category = (const char *)item_blacklist, .item_alloc = test_item_alloc, .item_find = test_item_find, .item_offset = offsetof(struct test_config, items), }; struct aco_file config_test_conf = { .filename = "config_test.conf", .types = ACO_TYPES(&global, &global_defaults, &item), }; static AO2_GLOBAL_OBJ_STATIC(global_obj); CONFIG_INFO_TEST(cfg_info, global_obj, test_config_alloc, .files = ACO_FILES(&config_test_conf), ); AST_TEST_DEFINE(config_options_test) { int res = AST_TEST_PASS, x, error; struct test_item defaults = { 0, }, configs = { 0, }; struct test_item *arr[4]; struct ast_sockaddr acl_allow = {{ 0, }}, acl_fail = {{ 0, }}; RAII_VAR(struct test_config *, cfg, NULL, ao2_cleanup); RAII_VAR(struct test_item *, item, NULL, ao2_cleanup); RAII_VAR(struct test_item *, item_defaults, NULL, ao2_cleanup); switch (cmd) { case TEST_INIT: info->name = "config_options_test"; info->category = "/config/"; info->summary = "Config options unit test"; info->description = "Tests the Config Options API"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } #define INT_DEFAULT "-2" #define INT_CONFIG "-1" #define UINT_DEFAULT "2" #define UINT_CONFIG "1" #define TIMELEN_DEFAULT "2" #define TIMELEN_CONFIG "1" #define DOUBLE_DEFAULT "1.1" #define DOUBLE_CONFIG "0.1" #define SOCKADDR_DEFAULT "4.3.2.1:4321" #define SOCKADDR_CONFIG "1.2.3.4:1234" #define BOOL_DEFAULT "false" #define BOOL_CONFIG "true" #define BOOLFLAG1_DEFAULT "false" #define BOOLFLAG1_CONFIG "true" #define BOOLFLAG2_DEFAULT "false" #define BOOLFLAG2_CONFIG "false" #define BOOLFLAG3_DEFAULT "false" #define BOOLFLAG3_CONFIG "true" #define ACL_DEFAULT NULL #define ACL_CONFIG_PERMIT "1.2.3.4/32" #define ACL_CONFIG_DENY "0.0.0.0/0" #define CODEC_DEFAULT "!all,alaw" #define CODEC_CONFIG "!all,ulaw,g729" #define STR_DEFAULT "default" #define STR_CONFIG "test" #define CUSTOM_DEFAULT "no" #define CUSTOM_CONFIG "yes" #define BOOLFLAG1 1 << 0 #define BOOLFLAG2 1 << 1 #define BOOLFLAG3 1 << 2 if (aco_info_init(&cfg_info)) { ast_test_status_update(test, "Could not init cfg info\n"); return AST_TEST_FAIL; } /* Register all options */ aco_option_register(&cfg_info, "intopt", ACO_EXACT, config_test_conf.types, INT_DEFAULT, OPT_INT_T, 0, FLDSET(struct test_item, intopt)); aco_option_register(&cfg_info, "uintopt", ACO_EXACT, config_test_conf.types, UINT_DEFAULT, OPT_UINT_T, 0, FLDSET(struct test_item, uintopt)); aco_option_register(&cfg_info, "timelenopt1", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt1), TIMELEN_MILLISECONDS); aco_option_register(&cfg_info, "timelenopt2", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt2), TIMELEN_SECONDS); aco_option_register(&cfg_info, "timelenopt3", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt3), TIMELEN_MINUTES); aco_option_register(&cfg_info, "timelenopt4", ACO_EXACT, config_test_conf.types, TIMELEN_DEFAULT, OPT_TIMELEN_T, 0, FLDSET(struct test_item, timelenopt4), TIMELEN_HOURS); aco_option_register(&cfg_info, "doubleopt", ACO_EXACT, config_test_conf.types, DOUBLE_DEFAULT, OPT_DOUBLE_T, 0, FLDSET(struct test_item, doubleopt)); aco_option_register(&cfg_info, "sockaddropt", ACO_EXACT, config_test_conf.types, SOCKADDR_DEFAULT, OPT_SOCKADDR_T, 0, FLDSET(struct test_item, sockaddropt)); aco_option_register(&cfg_info, "boolopt", ACO_EXACT, config_test_conf.types, BOOL_DEFAULT, OPT_BOOL_T, 1, FLDSET(struct test_item, boolopt)); aco_option_register(&cfg_info, "boolflag1", ACO_EXACT, config_test_conf.types, BOOLFLAG1_DEFAULT, OPT_BOOLFLAG_T, 1, FLDSET(struct test_item, flags), BOOLFLAG1); aco_option_register(&cfg_info, "boolflag2", ACO_EXACT, config_test_conf.types, BOOLFLAG2_DEFAULT, OPT_BOOLFLAG_T, 1, FLDSET(struct test_item, flags), BOOLFLAG2); aco_option_register(&cfg_info, "boolflag3", ACO_EXACT, config_test_conf.types, BOOLFLAG3_DEFAULT, OPT_BOOLFLAG_T, 1, FLDSET(struct test_item, flags), BOOLFLAG3); aco_option_register(&cfg_info, "aclpermitopt", ACO_EXACT, config_test_conf.types, ACL_DEFAULT, OPT_ACL_T, 1, FLDSET(struct test_item, aclopt)); aco_option_register(&cfg_info, "acldenyopt", ACO_EXACT, config_test_conf.types, ACL_DEFAULT, OPT_ACL_T, 0, FLDSET(struct test_item, aclopt)); aco_option_register(&cfg_info, "codecopt", ACO_EXACT, config_test_conf.types, CODEC_DEFAULT, OPT_CODEC_T, 1, FLDSET(struct test_item, codeccapopt)); aco_option_register(&cfg_info, "stropt", ACO_EXACT, config_test_conf.types, STR_DEFAULT, OPT_STRINGFIELD_T, 0, STRFLDSET(struct test_item, stropt)); aco_option_register_custom(&cfg_info, "customopt", ACO_EXACT, config_test_conf.types, CUSTOM_DEFAULT, customopt_handler, 0); aco_option_register_deprecated(&cfg_info, "permit", config_test_conf.types, "aclpermitopt"); aco_option_register_deprecated(&cfg_info, "deny", config_test_conf.types, "acldenyopt"); if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) { ast_test_status_update(test, "Could not parse config\n"); return AST_TEST_FAIL; } ast_parse_arg(INT_DEFAULT, PARSE_INT32, &defaults.intopt); ast_parse_arg(INT_CONFIG, PARSE_INT32, &configs.intopt); ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt1, TIMELEN_MILLISECONDS); ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt1, TIMELEN_MILLISECONDS); ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt2, TIMELEN_SECONDS); ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt2, TIMELEN_SECONDS); ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt3, TIMELEN_MINUTES); ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt3, TIMELEN_MINUTES); ast_parse_arg(TIMELEN_DEFAULT, PARSE_TIMELEN, &defaults.timelenopt4, TIMELEN_HOURS); ast_parse_arg(TIMELEN_CONFIG, PARSE_TIMELEN, &configs.timelenopt4, TIMELEN_HOURS); ast_parse_arg(UINT_DEFAULT, PARSE_UINT32, &defaults.uintopt); ast_parse_arg(UINT_CONFIG, PARSE_UINT32, &configs.uintopt); ast_parse_arg(DOUBLE_DEFAULT, PARSE_DOUBLE, &defaults.doubleopt); ast_parse_arg(DOUBLE_CONFIG, PARSE_DOUBLE, &configs.doubleopt); ast_parse_arg(SOCKADDR_DEFAULT, PARSE_ADDR, &defaults.sockaddropt); ast_parse_arg(SOCKADDR_CONFIG, PARSE_ADDR, &configs.sockaddropt); defaults.boolopt = ast_true(BOOL_DEFAULT); configs.boolopt = ast_true(BOOL_CONFIG); ast_set2_flag(&defaults, ast_true(BOOLFLAG1_DEFAULT), BOOLFLAG1); ast_set2_flag(&defaults, ast_true(BOOLFLAG2_DEFAULT), BOOLFLAG2); ast_set2_flag(&defaults, ast_true(BOOLFLAG3_DEFAULT), BOOLFLAG3); ast_set2_flag(&configs, ast_true(BOOLFLAG1_CONFIG), BOOLFLAG1); ast_set2_flag(&configs, ast_true(BOOLFLAG2_CONFIG), BOOLFLAG2); ast_set2_flag(&configs, ast_true(BOOLFLAG3_CONFIG), BOOLFLAG3); defaults.aclopt = NULL; configs.aclopt = ast_append_ha("deny", ACL_CONFIG_DENY, configs.aclopt, &error); configs.aclopt = ast_append_ha("permit", ACL_CONFIG_PERMIT, configs.aclopt, &error); ast_sockaddr_parse(&acl_allow, "1.2.3.4", PARSE_PORT_FORBID); ast_sockaddr_parse(&acl_fail, "1.1.1.1", PARSE_PORT_FORBID); defaults.codeccapopt = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); ast_format_cap_update_by_allow_disallow(defaults.codeccapopt, CODEC_DEFAULT, 1); configs.codeccapopt = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); ast_format_cap_update_by_allow_disallow(configs.codeccapopt, CODEC_CONFIG, 1); ast_string_field_init(&defaults, 128); ast_string_field_init(&configs, 128); ast_string_field_set(&defaults, stropt, STR_DEFAULT); ast_string_field_set(&configs, stropt, STR_CONFIG); defaults.customopt = ast_true(CUSTOM_DEFAULT); configs.customopt = ast_true(CUSTOM_CONFIG); cfg = ao2_global_obj_ref(global_obj); if (!(item = ao2_find(cfg->items, "item", OBJ_KEY))) { ast_test_status_update(test, "could not look up 'item'\n"); return AST_TEST_FAIL; } if (!(item_defaults = ao2_find(cfg->items, "item_defaults", OBJ_KEY))) { ast_test_status_update(test, "could not look up 'item_defaults'\n"); return AST_TEST_FAIL; } arr[0] = cfg->global; arr[1] = item; arr[2] = cfg->global_defaults; arr[3] = item_defaults; /* Test global and item against configs, global_defaults and item_defaults against defaults */ #define NOT_EQUAL_FAIL(field, format) \ if (arr[x]->field != control->field) { \ ast_test_status_update(test, "%s did not match: " format " != " format " with x = %d\n", #field, arr[x]->field, control->field, x); \ res = AST_TEST_FAIL; \ } for (x = 0; x < 4; x++) { struct test_item *control = x < 2 ? &configs : &defaults; NOT_EQUAL_FAIL(intopt, "%d"); NOT_EQUAL_FAIL(uintopt, "%u"); NOT_EQUAL_FAIL(timelenopt1, "%d"); NOT_EQUAL_FAIL(timelenopt2, "%d"); NOT_EQUAL_FAIL(timelenopt3, "%d"); NOT_EQUAL_FAIL(timelenopt4, "%d"); NOT_EQUAL_FAIL(boolopt, "%d"); NOT_EQUAL_FAIL(flags, "%u"); NOT_EQUAL_FAIL(customopt, "%d"); if (fabs(arr[x]->doubleopt - control->doubleopt) > 0.001) { ast_test_status_update(test, "doubleopt did not match: %f vs %f on loop %d\n", arr[x]->doubleopt, control->doubleopt, x); res = AST_TEST_FAIL; } if (ast_sockaddr_cmp(&arr[x]->sockaddropt, &control->sockaddropt)) { ast_test_status_update(test, "sockaddr did not match on loop %d\n", x); res = AST_TEST_FAIL; } if (!ast_format_cap_identical(arr[x]->codeccapopt, control->codeccapopt)) { struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN); struct ast_str *codec_buf2 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN); ast_test_status_update(test, "format did not match: '%s' vs '%s' on loop %d\n", ast_format_cap_get_names(arr[x]->codeccapopt, &codec_buf1), ast_format_cap_get_names(control->codeccapopt, &codec_buf2), x); res = AST_TEST_FAIL; } if (strcasecmp(arr[x]->stropt, control->stropt)) { ast_test_status_update(test, "stropt did not match: '%s' vs '%s' on loop %d\n", arr[x]->stropt, control->stropt, x); res = AST_TEST_FAIL; } if (arr[x]->aclopt != control->aclopt && (ast_apply_ha(arr[x]->aclopt, &acl_allow) != ast_apply_ha(control->aclopt, &acl_allow) || ast_apply_ha(arr[x]->aclopt, &acl_fail) != ast_apply_ha(control->aclopt, &acl_fail))) { ast_test_status_update(test, "acl not match: on loop %d\n", x); res = AST_TEST_FAIL; } } ast_free_ha(configs.aclopt); ao2_cleanup(defaults.codeccapopt); defaults.codeccapopt = NULL; ao2_cleanup(configs.codeccapopt); configs.codeccapopt = NULL; ast_string_field_free_memory(&defaults); ast_string_field_free_memory(&configs); aco_info_destroy(&cfg_info); ao2_global_obj_release(global_obj); return res; } AST_TEST_DEFINE(config_dialplan_function) { enum ast_test_result_state res = AST_TEST_PASS; FILE *config_file; char filename[PATH_MAX]; struct ast_str *buf; switch (cmd) { case TEST_INIT: info->name = "config_dialplan_function"; info->category = "/main/config/"; info->summary = "Test AST_CONFIG dialplan function"; info->description = "Test AST_CONFIG dialplan function"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, CONFIG_FILE); config_file = fopen(filename, "w"); if (!config_file) { return AST_TEST_FAIL; } fputs( "[c1t](!)\n" "var1=val1\n" "var1=val2\n" "var2=val21\n" "\n" "[c1](c1t)\n" "var1=val3\n" "var1=val4\n" , config_file); fclose(config_file); if (!(buf = ast_str_create(32))) { ast_test_status_update(test, "Failed to allocate return buffer\n"); res = AST_TEST_FAIL; goto out; } if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val1")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val1"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,0)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val1")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val1"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,1)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val2")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val2"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,2)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val3")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val3"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,3)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val4")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val4"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,-1)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var1'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val4")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val4"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var2,-1)", &buf, 32)) { ast_test_status_update(test, "Failed to retrieve field 'var2'\n"); res = AST_TEST_FAIL; goto out; } if (strcmp(ast_str_buffer(buf), "val21")) { ast_test_status_update(test, "Got '%s', should be '%s'\n", ast_str_buffer(buf), "val21"); res = AST_TEST_FAIL; goto out; } ast_str_reset(buf); if (!ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,5)", &buf, 32)) { ast_test_status_update(test, "Should not have retrieved a value\n"); res = AST_TEST_FAIL; goto out; } out: if (buf) { ast_free(buf); } delete_config_file(); return res; } AST_TEST_DEFINE(variable_lists_match) { RAII_VAR(struct ast_variable *, left, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, right, NULL, ast_variables_destroy); struct ast_variable *var; switch (cmd) { case TEST_INIT: info->name = "variable_lists_match"; info->category = "/main/config/"; info->summary = "Test ast_variable_lists_match"; info->description = "Test ast_variable_lists_match"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } var = ast_variable_new("aaa", "111", ""); ast_test_validate(test, var); left = var; var = ast_variable_new("bbb", "222", ""); ast_test_validate(test, var); ast_variable_list_append(&left, var); var = ast_variable_new("aaa", "111", ""); ast_test_validate(test, var); right = var; ast_test_validate(test, ast_variable_lists_match(left, right, 0)); ast_test_validate(test, !ast_variable_lists_match(left, right, 1)); var = ast_variable_new("bbb", "222", ""); ast_test_validate(test, var); ast_variable_list_append(&right, var); ast_test_validate(test, ast_variable_lists_match(left, right, 0)); ast_test_validate(test, ast_variable_lists_match(left, right, 1)); var = ast_variable_new("ccc >", "333", ""); ast_test_validate(test, var); ast_variable_list_append(&right, var); ast_test_validate(test, !ast_variable_lists_match(left, right, 0)); ast_test_validate(test, !ast_variable_lists_match(left, right, 1)); var = ast_variable_new("ccc", "444", ""); ast_test_validate(test, var); ast_variable_list_append(&left, var); ast_test_validate(test, ast_variable_lists_match(left, right, 0)); ast_test_validate(test, !ast_variable_lists_match(left, right, 1)); ast_test_validate(test, !ast_variable_lists_match(left, NULL, 0)); ast_test_validate(test, ast_variable_lists_match(NULL, NULL, 0)); ast_test_validate(test, !ast_variable_lists_match(NULL, right, 0)); ast_test_validate(test, ast_variable_lists_match(left, left, 0)); return AST_TEST_PASS; } AST_TEST_DEFINE(variable_list_join_replace) { RAII_VAR(struct ast_variable *, list, NULL, ast_variables_destroy); RAII_VAR(struct ast_str *, str, NULL, ast_free); struct ast_variable *bbb; int rc; switch (cmd) { case TEST_INIT: info->name = "variable_list_join_replace"; info->category = "/main/config/"; info->summary = "Test joining a variable list"; info->description = info->summary; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } list = ast_variable_new("aaa", "111", ""); bbb = ast_variable_new("bbb", "222", ""); ast_variable_list_append(&list, bbb); ast_variable_list_append(&list, ast_variable_new("ccc", "33 33", "")); str = ast_variable_list_join(list, ", ", " = ", "\"", &str); ast_test_validate(test, strcmp(ast_str_buffer(str), "aaa = \"111\", bbb = \"222\", ccc = \"33 33\"") == 0); ast_free(str); str = ast_str_create(AST_MAX_USER_FIELD); str = ast_variable_list_join(list, ", ", " = ", "\"", &str); ast_test_validate(test, strcmp(ast_str_buffer(str), "aaa = \"111\", bbb = \"222\", ccc = \"33 33\"") == 0); ast_free(str); str = ast_variable_list_join(list, ", ", " = ", "\"", NULL); ast_test_validate(test, strcmp(ast_str_buffer(str), "aaa = \"111\", bbb = \"222\", ccc = \"33 33\"") == 0); ast_free(str); /* Replace the head item in the list */ rc = ast_variable_list_replace_variable(&list, list, ast_variable_new("ddd", "444", "")); ast_test_validate(test, rc == 0); str = ast_variable_list_join(list, ", ", " = ", "\"", NULL); ast_test_validate(test, strcmp(ast_str_buffer(str), "ddd = \"444\", bbb = \"222\", ccc = \"33 33\"") == 0); ast_free(str); rc = ast_variable_list_replace_variable(&list, bbb, ast_variable_new("eee", "555", "")); ast_test_validate(test, rc == 0); str = ast_variable_list_join(list, ", ", " = ", "\"", NULL); ast_test_validate(test, strcmp(ast_str_buffer(str), "ddd = \"444\", eee = \"555\", ccc = \"33 33\"") == 0); return AST_TEST_PASS; } AST_TEST_DEFINE(variable_list_from_string) { RAII_VAR(struct ast_variable *, list, NULL, ast_variables_destroy); RAII_VAR(struct ast_str *, str, NULL, ast_free); char *parse_string; switch (cmd) { case TEST_INIT: info->name = "variable_list_from_quoted_string"; info->category = "/main/config/"; info->summary = "Test parsing a string into a variable list"; info->description = info->summary; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; } parse_string = "000= '', 111=, 222 = , 333 = ' ', abc = 'def', ghi = 'j,kl', mno='pq=r', stu = 'vwx=\"yz\", ABC = \"DEF\"'"; list = ast_variable_list_from_quoted_string(parse_string, ",", "=", "'"); ast_test_validate(test, list != NULL); str = ast_variable_list_join(list, "|", "^", "@", NULL); ast_test_validate(test, strcmp(ast_str_buffer(str), "000^@@|111^@@|222^@@|333^@ @|abc^@def@|ghi^@j,kl@|mno^@pq=r@|stu^@vwx=\"yz\", ABC = \"DEF\"@") == 0); return AST_TEST_PASS; } static int unload_module(void) { AST_TEST_UNREGISTER(config_save); AST_TEST_UNREGISTER(config_basic_ops); AST_TEST_UNREGISTER(config_filtered_ops); AST_TEST_UNREGISTER(config_template_ops); AST_TEST_UNREGISTER(copy_config); AST_TEST_UNREGISTER(config_hook); AST_TEST_UNREGISTER(ast_parse_arg_test); AST_TEST_UNREGISTER(config_options_test); AST_TEST_UNREGISTER(config_dialplan_function); AST_TEST_UNREGISTER(variable_lists_match); AST_TEST_UNREGISTER(variable_list_join_replace); AST_TEST_UNREGISTER(variable_list_from_string); return 0; } static int load_module(void) { AST_TEST_REGISTER(config_save); AST_TEST_REGISTER(config_basic_ops); AST_TEST_REGISTER(config_filtered_ops); AST_TEST_REGISTER(config_template_ops); AST_TEST_REGISTER(copy_config); AST_TEST_REGISTER(config_hook); AST_TEST_REGISTER(ast_parse_arg_test); AST_TEST_REGISTER(config_options_test); AST_TEST_REGISTER(config_dialplan_function); AST_TEST_REGISTER(variable_lists_match); AST_TEST_REGISTER(variable_list_join_replace); AST_TEST_REGISTER(variable_list_from_string); return AST_MODULE_LOAD_SUCCESS; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Config test module");