[asterisk-commits] mmichelson: branch 10-digiumphones r361293 - in /branches/10-digiumphones: fu...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Apr 5 12:48:50 CDT 2012
Author: mmichelson
Date: Thu Apr 5 12:48:47 2012
New Revision: 361293
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=361293
Log:
Add unit tests for func_presencestate and config hooks.
These tests were originally written for the trunk merge of Digium
phone support to Asterisk, but this branch can benefit from these
tests too.
Modified:
branches/10-digiumphones/funcs/func_presencestate.c
branches/10-digiumphones/tests/test_config.c
Modified: branches/10-digiumphones/funcs/func_presencestate.c
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/funcs/func_presencestate.c?view=diff&rev=361293&r1=361292&r2=361293
==============================================================================
--- branches/10-digiumphones/funcs/func_presencestate.c (original)
+++ branches/10-digiumphones/funcs/func_presencestate.c Thu Apr 5 12:48:47 2012
@@ -35,6 +35,11 @@
#include "asterisk/cli.h"
#include "asterisk/astdb.h"
#include "asterisk/app.h"
+#ifdef TEST_FRAMEWORK
+#include "asterisk/test.h"
+#include "asterisk/event.h"
+#include <semaphore.h>
+#endif
/*** DOCUMENTATION
<function name="PRESENCE_STATE" language="en_US">
@@ -267,12 +272,304 @@
.write = presence_write,
};
+#ifdef TEST_FRAMEWORK
+
+struct test_string {
+ char *parse_string;
+ struct {
+ int value;
+ const char *subtype;
+ const char *message;
+ const char *options;
+ } outputs;
+};
+
+AST_TEST_DEFINE(test_valid_parse_data)
+{
+ int i;
+ int state;
+ char *subtype;
+ char *message;
+ char *options;
+ enum ast_test_result_state res = AST_TEST_PASS;
+
+ struct test_string tests [] = {
+ { "away",
+ { AST_PRESENCE_AWAY,
+ "",
+ "",
+ ""
+ }
+ },
+ { "not_set",
+ { AST_PRESENCE_NOT_SET,
+ "",
+ "",
+ ""
+ }
+ },
+ { "unavailable",
+ { AST_PRESENCE_UNAVAILABLE,
+ "",
+ "",
+ ""
+ }
+ },
+ { "available",
+ { AST_PRESENCE_AVAILABLE,
+ "",
+ "",
+ ""
+ }
+ },
+ { "xa",
+ { AST_PRESENCE_XA,
+ "",
+ "",
+ ""
+ }
+ },
+ { "chat",
+ { AST_PRESENCE_CHAT,
+ "",
+ "",
+ ""
+ }
+ },
+ { "dnd",
+ { AST_PRESENCE_DND,
+ "",
+ "",
+ ""
+ }
+ },
+ { "away,down the hall",
+ { AST_PRESENCE_AWAY,
+ "down the hall",
+ "",
+ ""
+ }
+ },
+ { "away,down the hall,Quarterly financial meeting",
+ { AST_PRESENCE_AWAY,
+ "down the hall",
+ "Quarterly financial meeting",
+ ""
+ }
+ },
+ { "away,,Quarterly financial meeting",
+ { AST_PRESENCE_AWAY,
+ "",
+ "Quarterly financial meeting",
+ ""
+ }
+ },
+ { "away,,,e",
+ { AST_PRESENCE_AWAY,
+ "",
+ "",
+ "e",
+ }
+ },
+ { "away,down the hall,,e",
+ { AST_PRESENCE_AWAY,
+ "down the hall",
+ "",
+ "e"
+ }
+ },
+ { "away,down the hall,Quarterly financial meeting,e",
+ { AST_PRESENCE_AWAY,
+ "down the hall",
+ "Quarterly financial meeting",
+ "e"
+ }
+ },
+ { "away,,Quarterly financial meeting,e",
+ { AST_PRESENCE_AWAY,
+ "",
+ "Quarterly financial meeting",
+ "e"
+ }
+ }
+ };
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "parse_valid_presence_data";
+ info->category = "/funcs/func_presence";
+ info->summary = "PRESENCESTATE parsing test";
+ info->description =
+ "Ensure that parsing function accepts proper values, and gives proper outputs";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ for (i = 0; i < ARRAY_LEN(tests); ++i) {
+ int parse_result;
+ char *parse_string = ast_strdup(tests[i].parse_string);
+ if (!parse_string) {
+ res = AST_TEST_FAIL;
+ break;
+ }
+ parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
+ if (parse_result == -1) {
+ res = AST_TEST_FAIL;
+ ast_free(parse_string);
+ break;
+ }
+ if (tests[i].outputs.value != state ||
+ strcmp(tests[i].outputs.subtype, subtype) ||
+ strcmp(tests[i].outputs.message, message) ||
+ strcmp(tests[i].outputs.options, options)) {
+ res = AST_TEST_FAIL;
+ ast_free(parse_string);
+ break;
+ }
+ ast_free(parse_string);
+ }
+
+ return res;
+}
+
+AST_TEST_DEFINE(test_invalid_parse_data)
+{
+ int i;
+ int state;
+ char *subtype;
+ char *message;
+ char *options;
+ enum ast_test_result_state res = AST_TEST_PASS;
+
+ char *tests[] = {
+ "",
+ "bored",
+ "away,,,i",
+ /* XXX The following actually is parsed correctly. Should that
+ * be changed?
+ * "away,,,,e",
+ */
+ };
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "parse_invalid_presence_data";
+ info->category = "/funcs/func_presence";
+ info->summary = "PRESENCESTATE parsing test";
+ info->description =
+ "Ensure that parsing function rejects improper values";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ for (i = 0; i < ARRAY_LEN(tests); ++i) {
+ int parse_result;
+ char *parse_string = ast_strdup(tests[i]);
+ if (!parse_string) {
+ res = AST_TEST_FAIL;
+ break;
+ }
+ printf("parse string is %s\n", parse_string);
+ parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
+ if (parse_result == 0) {
+ res = AST_TEST_FAIL;
+ ast_free(parse_string);
+ break;
+ }
+ ast_free(parse_string);
+ }
+
+ return res;
+}
+
+struct test_cb_data {
+ enum ast_presence_state presence;
+ const char *provider;
+ const char *subtype;
+ const char *message;
+ /* That's right. I'm using a semaphore */
+ sem_t sem;
+};
+
+static void test_cb(const struct ast_event *event, void *userdata)
+{
+ struct test_cb_data *cb_data = userdata;
+ cb_data->presence = ast_event_get_ie_uint(event, AST_EVENT_IE_PRESENCE_STATE);
+ cb_data->provider = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_PROVIDER));
+ cb_data->subtype = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_SUBTYPE));
+ cb_data->message = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_MESSAGE));
+ sem_post(&cb_data->sem);
+ ast_log(LOG_NOTICE, "Callback called\n");
+}
+
+/* XXX This test could probably stand to be moved since
+ * it does not test func_presencestate but rather code in
+ * presencestate.h and presencestate.c. However, the convenience
+ * of presence_write() makes this a nice location for this test.
+ */
+AST_TEST_DEFINE(test_presence_state_change)
+{
+ struct ast_event_sub *test_sub;
+ struct test_cb_data *cb_data;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "test_presence_state_change";
+ info->category = "/funcs/func_presence";
+ info->summary = "presence state change subscription";
+ info->description =
+ "Ensure that presence state changes are communicated to subscribers";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cb_data = ast_calloc(1, sizeof(*cb_data));
+ if (!cb_data) {
+ return AST_TEST_FAIL;
+ }
+
+ if (!(test_sub = ast_event_subscribe(AST_EVENT_PRESENCE_STATE,
+ test_cb, "Test presence state callbacks", cb_data, AST_EVENT_IE_END))) {
+ return AST_TEST_FAIL;
+ }
+
+ if (sem_init(&cb_data->sem, 0, 0)) {
+ return AST_TEST_FAIL;
+ }
+
+ presence_write(NULL, "PRESENCESTATE", "CustomPresence:Bob", "away,down the hall,Quarterly financial meeting");
+ sem_wait(&cb_data->sem);
+ if (cb_data->presence != AST_PRESENCE_AWAY ||
+ strcmp(cb_data->provider, "CustomPresence:Bob") ||
+ strcmp(cb_data->subtype, "down the hall") ||
+ strcmp(cb_data->message, "Quarterly financial meeting")) {
+ return AST_TEST_FAIL;
+ }
+
+ ast_free((char *)cb_data->provider);
+ ast_free((char *)cb_data->subtype);
+ ast_free((char *)cb_data->message);
+ ast_free((char *)cb_data);
+
+ return AST_TEST_PASS;
+}
+
+#endif
+
static int unload_module(void)
{
int res = 0;
res |= ast_custom_function_unregister(&presence_function);
res |= ast_presence_state_prov_del("CustomPresence");
+#ifdef TEST_FRAMEWORK
+ AST_TEST_UNREGISTER(test_valid_parse_data);
+ AST_TEST_UNREGISTER(test_invalid_parse_data);
+ AST_TEST_UNREGISTER(test_presence_state_change);
+#endif
return res;
}
@@ -282,6 +579,11 @@
res |= ast_custom_function_register(&presence_function);
res |= ast_presence_state_prov_add("CustomPresence", custom_presence_callback);
+#ifdef TEST_FRAMEWORK
+ AST_TEST_REGISTER(test_valid_parse_data);
+ AST_TEST_REGISTER(test_invalid_parse_data);
+ AST_TEST_REGISTER(test_presence_state_change);
+#endif
return res;
}
Modified: branches/10-digiumphones/tests/test_config.c
URL: http://svnview.digium.com/svn/asterisk/branches/10-digiumphones/tests/test_config.c?view=diff&rev=361293&r1=361292&r2=361293
==============================================================================
--- branches/10-digiumphones/tests/test_config.c (original)
+++ branches/10-digiumphones/tests/test_config.c Thu Apr 5 12:48:47 2012
@@ -36,7 +36,24 @@
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/test.h"
-
+#include "asterisk/paths.h"
+
+#define CONFIG_FILE "test_config.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";
@@ -78,6 +95,12 @@
},
};
+/*!
+ * \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;
@@ -120,13 +143,46 @@
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;
- const char *cat_iter = NULL;
- size_t i;
switch (cmd) {
case TEST_INIT:
@@ -150,26 +206,8 @@
goto out;
}
- /* 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(copy, 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);
- goto out;
- }
- for (j = 0; j < ARRAY_LEN(categories[i].vars); ++j) {
- var = var ? var->next : ast_variable_browse(copy, 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);
- goto out;
- }
- 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);
- goto out;
- }
- }
+ if (test_config_validity(copy) != 0) {
+ goto out;
}
res = AST_TEST_PASS;
@@ -180,15 +218,173 @@
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_hook)
+{
+ enum ast_test_result_state res = AST_TEST_FAIL;
+ enum config_hook_flags hook_flags = { 0, };
+ struct ast_flags config_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, config_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:
+ delete_config_file();
+ return res;
+}
+
static int unload_module(void)
{
AST_TEST_UNREGISTER(copy_config);
+ AST_TEST_UNREGISTER(config_hook);
return 0;
}
static int load_module(void)
{
AST_TEST_REGISTER(copy_config);
+ AST_TEST_REGISTER(config_hook);
return AST_MODULE_LOAD_SUCCESS;
}
More information about the asterisk-commits
mailing list