diff options
author | Terry Wilson <twilson@digium.com> | 2012-06-01 16:33:25 +0000 |
---|---|---|
committer | Terry Wilson <twilson@digium.com> | 2012-06-01 16:33:25 +0000 |
commit | d54717c39e62f4cc8b290ac4836c4d4469d87c24 (patch) | |
tree | 5a765e82be880f3b8c2407133fbcc15c4b4349d0 /tests/test_config.c | |
parent | 463f9d729aed1a5ed538aa3deed1a3fed9462111 (diff) |
Add new config-parsing framework
This framework adds a way to register the various options in a config
file with Asterisk and to handle loading and reloading of that config
in a consistent and atomic manner.
Review: https://reviewboard.asterisk.org/r/1873/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@368181 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'tests/test_config.c')
-rw-r--r-- | tests/test_config.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/tests/test_config.c b/tests/test_config.c index 1a58e46ff..c8f7a077b 100644 --- a/tests/test_config.c +++ b/tests/test_config.c @@ -38,6 +38,11 @@ ASTERISK_FILE_VERSION(__FILE__, "") #include "asterisk/test.h" #include "asterisk/module.h" #include "asterisk/config.h" +#include "asterisk/config_options.h" +#include "asterisk/netsock2.h" +#include "asterisk/acl.h" +#include "asterisk/frame.h" +#include "asterisk/utils.h" #include "asterisk/logger.h" enum { @@ -274,15 +279,311 @@ AST_TEST_DEFINE(ast_parse_arg_test) return ret; } +struct test_item { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(name); + AST_STRING_FIELD(stropt); + ); + int32_t intopt; + uint32_t uintopt; + double doubleopt; + struct ast_sockaddr sockaddropt; + int boolopt; + struct ast_ha *aclopt; + struct ast_codec_pref codecprefopt; + 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_hash(const void *obj, const int flags) +{ + const struct test_item *item = obj; + const char *name = (flags & OBJ_KEY) ? obj : item->name; + return ast_str_case_hash(name); +} +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); + if (item->codeccapopt) { + ast_format_cap_destroy(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())) { + 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; + } + if (!(cfg->items = ao2_container_alloc(1, test_item_hash, test_item_cmp))) { + 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, + .category = "^global_defaults$", +}; +static struct aco_type item = { + .type = ACO_ITEM, + .category_match = ACO_BLACKLIST, + .category = "^(global|global_defaults)$", + .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_STANDARD(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 opptions 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 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 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" + + 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, "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, "aclpermitopt", ACO_EXACT, config_test_conf.types, ACL_DEFAULT, OPT_ACL_T, 1, FLDSET(struct test_item, aclopt), "permit"); + aco_option_register(&cfg_info, "acldenyopt", ACO_EXACT, config_test_conf.types, ACL_DEFAULT, OPT_ACL_T, 0, FLDSET(struct test_item, aclopt), "deny"); + aco_option_register(&cfg_info, "codecopt", ACO_EXACT, config_test_conf.types, CODEC_DEFAULT, OPT_CODEC_T, 1, FLDSET(struct test_item, codecprefopt, 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); + + if (aco_process_config(&cfg_info, 0)) { + 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(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); + + 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_parse_allow_disallow(&defaults.codecprefopt, defaults.codeccapopt, CODEC_DEFAULT, 1); + + configs.codeccapopt = ast_format_cap_alloc(); + ast_parse_allow_disallow(&configs.codecprefopt, 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) \ + if (arr[x]->field != control->field) { \ + ast_test_status_update(test, "%s di not match: %d != %d 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); + NOT_EQUAL_FAIL(uintopt); + NOT_EQUAL_FAIL(boolopt); + NOT_EQUAL_FAIL(customopt); + 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)) { + char buf1[128], buf2[128]; + ast_getformatname_multiple(buf1, sizeof(buf1), arr[x]->codeccapopt); + ast_getformatname_multiple(buf2, sizeof(buf2), control->codeccapopt); + ast_test_status_update(test, "format did not match: '%s' vs '%s' on loop %d\n", buf1, 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); + ast_format_cap_destroy(defaults.codeccapopt); + ast_format_cap_destroy(configs.codeccapopt); + ast_string_field_free_memory(&defaults); + ast_string_field_free_memory(&configs); + return res; +} + static int unload_module(void) { AST_TEST_UNREGISTER(ast_parse_arg_test); + AST_TEST_UNREGISTER(config_options_test); return 0; } static int load_module(void) { AST_TEST_REGISTER(ast_parse_arg_test); + AST_TEST_REGISTER(config_options_test); return AST_MODULE_LOAD_SUCCESS; } |