[svn-commits] mmichelson: branch 1.8-digiumphones r361283 - in /branches/1.8-digiumphones: ...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Apr 5 12:24:17 CDT 2012


Author: mmichelson
Date: Thu Apr  5 12:24:13 2012
New Revision: 361283

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=361283
Log:
Add additional configuration and presence unit tests.

These were originally written while merging features into trunk, but
these tests apply just as much for the 1.8 version of Digium phones, so
might as well have them here, too.


Modified:
    branches/1.8-digiumphones/funcs/func_presence_state.c
    branches/1.8-digiumphones/tests/test_config.c

Modified: branches/1.8-digiumphones/funcs/func_presence_state.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8-digiumphones/funcs/func_presence_state.c?view=diff&rev=361283&r1=361282&r2=361283
==============================================================================
--- branches/1.8-digiumphones/funcs/func_presence_state.c (original)
+++ branches/1.8-digiumphones/funcs/func_presence_state.c Thu Apr  5 12:24:13 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/1.8-digiumphones/tests/test_config.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8-digiumphones/tests/test_config.c?view=diff&rev=361283&r1=361282&r2=361283
==============================================================================
--- branches/1.8-digiumphones/tests/test_config.c (original)
+++ branches/1.8-digiumphones/tests/test_config.c Thu Apr  5 12:24:13 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 svn-commits mailing list