[svn-commits] mmichelson: branch group/dns r432531 - /team/group/dns/tests/test_dns.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Mar 6 14:16:31 CST 2015


Author: mmichelson
Date: Fri Mar  6 14:16:28 2015
New Revision: 432531

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=432531
Log:
Clean up DNS unit test file.

* Put tests in same order they are registered in module load function
* Add doxygen to structures and helper functions.


Modified:
    team/group/dns/tests/test_dns.c

Modified: team/group/dns/tests/test_dns.c
URL: http://svnview.digium.com/svn/asterisk/team/group/dns/tests/test_dns.c?view=diff&rev=432531&r1=432530&r2=432531
==============================================================================
--- team/group/dns/tests/test_dns.c (original)
+++ team/group/dns/tests/test_dns.c Fri Mar  6 14:16:28 2015
@@ -210,8 +210,7 @@
 			"\t* Ensure that setting resolver data does not result in an error.\n"
 			"\t* Ensure that retrieving the set resolver data returns the data we expect\n"
 			"\t* Ensure that setting new resolver data on the query does not result in an error\n"
-			"\t* Ensure that retrieving the resolver data returns the new data that we set\n"
-			"\t* Ensure that ast_dns_resolver_completed() removes resolver data from the query\n";
+			"\t* Ensure that retrieving the resolver data returns the new data that we set\n";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -500,11 +499,11 @@
 	}
 
 	for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
-		/* The order of returned records is not specified. We use the record type as the discriminator
-		 * to determine which record data to expect.
+		int res;
+
+		/* The order of returned records is not specified by the API. We use the record type
+		 * as the discriminator to determine which record data to expect.
 		 */
-		int res;
-
 		if (ast_dns_record_get_rr_type(record) == records[0].type) {
 			res = test_record(test, record, records[0].type, records[0].class, records[0].ttl, records[0].data, records[0].size);
 			records[0].visited = 1;
@@ -619,14 +618,38 @@
 	return AST_TEST_PASS;
 }
 
+/*!
+ * \brief File-scoped data used during resolver tests
+ *
+ * This data has to live at file-scope since it needs to be
+ * accessible by multiple threads.
+ */
 static struct resolver_data {
+	/*! True if the resolver's resolve() method has been called */
 	int resolve_called;
+	/*! True if the resolver's cancel() method has been called */
 	int canceled;
+	/*! True if resolution successfully completed. This is mutually exclusive with \ref canceled */
 	int resolution_complete;
+	/*! Lock used for protecting \ref cancel_cond */
 	ast_mutex_t lock;
+	/*! Condition variable used to coordinate canceling a query */
 	ast_cond_t cancel_cond;
 } test_resolver_data;
 
+/*!
+ * \brief Thread spawned by the mock resolver
+ *
+ * All DNS resolvers are required to be asynchronous. The mock resolver
+ * spawns this thread for every DNS query that is executed.
+ *
+ * This thread waits for 5 seconds and then returns the same A record
+ * every time. The 5 second wait is to allow for the query to be
+ * canceled if desired
+ *
+ * \param dns_query The ast_dns_query that is being resolved
+ * \return NULL
+ */
 static void *resolution_thread(void *dns_query)
 {
 	struct ast_dns_query *query = dns_query;
@@ -665,6 +688,13 @@
 	return NULL;
 }
 
+/*!
+ * \brief Mock resolver's resolve method
+ *
+ * \param query The query to resolve
+ * \retval 0 Successfully spawned resolution thread
+ * \retval non-zero Failed to spawn the resolution thread
+ */
 static int test_resolve(struct ast_dns_query *query)
 {
 	pthread_t resolver_thread;
@@ -673,6 +703,14 @@
 	return ast_pthread_create_detached(&resolver_thread, NULL, resolution_thread, ao2_bump(query));
 }
 
+/*!
+ * \brief Mock resolver's cancel method
+ *
+ * This signals the resolution thread not to return any DNS results.
+ *
+ * \param query DNS query to cancel
+ * \return 0
+ */
 static int test_cancel(struct ast_dns_query *query)
 {
 	ast_mutex_lock(&test_resolver_data.lock);
@@ -683,6 +721,11 @@
 	return 0;
 }
 
+/*!
+ * \brief Initialize global mock resolver data.
+ *
+ * This must be called at the beginning of tests that use the mock resolver
+ */
 static void resolver_data_init(void)
 {
 	test_resolver_data.resolve_called = 0;
@@ -693,12 +736,24 @@
 	ast_cond_init(&test_resolver_data.cancel_cond, NULL);
 }
 
+/*!
+ * \brief Cleanup global mock resolver data
+ *
+ * This must be called at the end of tests that use the mock resolver
+ */
 static void resolver_data_cleanup(void)
 {
 	ast_mutex_destroy(&test_resolver_data.lock);
 	ast_cond_destroy(&test_resolver_data.cancel_cond);
 }
 
+/*!
+ * \brief The mock resolver
+ *
+ * The mock resolver does not care about the DNS query that is
+ * actually being made on it. It simply regurgitates the same
+ * DNS record no matter what.
+ */
 static struct ast_dns_resolver test_resolver = {
 	.name = "test",
 	.priority = 0,
@@ -776,143 +831,12 @@
 	return res;
 }
 
-struct async_resolution_data {
-	int complete;
-	ast_mutex_t lock;
-	ast_cond_t cond;
-};
-
-static void async_data_destructor(void *obj)
-{
-	struct async_resolution_data *async_data = obj;
-
-	ast_mutex_destroy(&async_data->lock);
-	ast_cond_destroy(&async_data->cond);
-}
-
-static struct async_resolution_data *async_data_alloc(void)
-{
-	struct async_resolution_data *async_data;
-
-	async_data = ao2_alloc(sizeof(*async_data), async_data_destructor);
-	if (!async_data) {
-		return NULL;
-	}
-
-	async_data->complete = 0;
-	ast_mutex_init(&async_data->lock);
-	ast_cond_init(&async_data->cond, NULL);
-
-	return async_data;
-}
-
-static void async_callback(const struct ast_dns_query *query)
-{
-	struct async_resolution_data *async_data = ast_dns_query_get_data(query);
-
-	ast_mutex_lock(&async_data->lock);
-	async_data->complete = 1;
-	ast_cond_signal(&async_data->cond);
-	ast_mutex_unlock(&async_data->lock);
-}
-
-AST_TEST_DEFINE(resolver_resolve_async)
-{
-	RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup); 
-	RAII_VAR(struct ast_dns_query *, query, NULL, ao2_cleanup);
-	struct ast_dns_result *result;
-	enum ast_test_result_state res = AST_TEST_PASS;
-	struct timespec timeout;
-
-	switch (cmd) {
-	case TEST_INIT:
-		info->name = "resolver_resolve_async";
-		info->category = "/main/dns/";
-		info->summary = "Test a nominal asynchronous DNS resolution";
-		info->description =
-			"This test performs an asynchronous DNS resolution of a domain. The goal of this\n"
-			"test is not to check the records for accuracy. Rather, the goal is to ensure that\n"
-			"the resolver is called into as expected, that we regain control before the query\n"
-			"is completed, and to ensure that nothing tried to cancel the resolution.";
-		return AST_TEST_NOT_RUN;
-	case TEST_EXECUTE:
-		break;
-	}
-
-	if (ast_dns_resolver_register(&test_resolver)) {
-		ast_test_status_update(test, "Unable to register test resolver\n");
-		return AST_TEST_FAIL;
-	}
-
-	resolver_data_init();
-
-	async_data = async_data_alloc();
-	if (!async_data) {
-		ast_test_status_update(test, "Failed to allocate asynchronous data\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-	query = ast_dns_resolve_async("asterisk.org", ns_t_a, ns_c_in, async_callback, async_data);
-	if (!query) {
-		ast_test_status_update(test, "Asynchronous resolution of address failed\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-	if (!test_resolver_data.resolve_called) {
-		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-	if (test_resolver_data.canceled) {
-		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-	clock_gettime(CLOCK_REALTIME, &timeout);
-	timeout.tv_sec += 10;
-	ast_mutex_lock(&async_data->lock);
-	while (!async_data->complete) {
-		if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {
-			break;
-		}
-	}
-	ast_mutex_unlock(&async_data->lock);
-
-	if (!async_data->complete) {
-		ast_test_status_update(test, "Asynchronous resolution timed out\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-	if (!test_resolver_data.resolution_complete) {
-		ast_test_status_update(test, "Asynchronous resolution completed early?\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-	
-	result = ast_dns_query_get_result(query);
-	if (!result) {
-		ast_test_status_update(test, "Asynchronous resolution yielded no result\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-	if (!ast_dns_result_get_records(result)) {
-		ast_test_status_update(test, "Asynchronous result had no records\n");
-		res = AST_TEST_FAIL;
-		goto cleanup;
-	}
-
-cleanup:
-	ast_dns_resolver_unregister(&test_resolver);
-	resolver_data_cleanup();
-	return res;
-}
-
+/*!
+ * \brief A resolve() method that simply fails
+ *
+ * \param query The DNS query to resolve. This is ignored.
+ * \return -1
+ */
 static int fail_resolve(struct ast_dns_query *query)
 {
 	return -1;
@@ -1005,6 +929,173 @@
 	return res;
 }
 
+/*!
+ * \brief Data used by async result callback
+ *
+ * This is the typical combination of boolean, lock, and condition
+ * used to synchronize the activities of two threads. In this case,
+ * the testing thread waits on the condition, and the async callback
+ * signals the condition when the asynchronous callback is complete.
+ */
+struct async_resolution_data {
+	int complete;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+};
+
+/*!
+ * \brief Destructor for async_resolution_data
+ */
+static void async_data_destructor(void *obj)
+{
+	struct async_resolution_data *async_data = obj;
+
+	ast_mutex_destroy(&async_data->lock);
+	ast_cond_destroy(&async_data->cond);
+}
+
+/*!
+ * \brief Allocation/initialization for async_resolution_data
+ *
+ * The DNS core mandates that a query's user data has to be ao2 allocated,
+ * so this is a helper method for doing that.
+ *
+ * \retval NULL Failed allocation
+ * \retval non-NULL Newly allocated async_resolution_data
+ */
+static struct async_resolution_data *async_data_alloc(void)
+{
+	struct async_resolution_data *async_data;
+
+	async_data = ao2_alloc(sizeof(*async_data), async_data_destructor);
+	if (!async_data) {
+		return NULL;
+	}
+
+	async_data->complete = 0;
+	ast_mutex_init(&async_data->lock);
+	ast_cond_init(&async_data->cond, NULL);
+
+	return async_data;
+}
+
+/*!
+ * \brief Async DNS callback
+ *
+ * This is called when an async query completes, either because it resolved or
+ * because it was canceled. In our case, this callback is used to signal to the
+ * test that it can continue
+ *
+ * \param query The DNS query that has completed
+ */
+static void async_callback(const struct ast_dns_query *query)
+{
+	struct async_resolution_data *async_data = ast_dns_query_get_data(query);
+
+	ast_mutex_lock(&async_data->lock);
+	async_data->complete = 1;
+	ast_cond_signal(&async_data->cond);
+	ast_mutex_unlock(&async_data->lock);
+}
+
+AST_TEST_DEFINE(resolver_resolve_async)
+{
+	RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup); 
+	RAII_VAR(struct ast_dns_query *, query, NULL, ao2_cleanup);
+	struct ast_dns_result *result;
+	enum ast_test_result_state res = AST_TEST_PASS;
+	struct timespec timeout;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "resolver_resolve_async";
+		info->category = "/main/dns/";
+		info->summary = "Test a nominal asynchronous DNS resolution";
+		info->description =
+			"This test performs an asynchronous DNS resolution of a domain. The goal of this\n"
+			"test is not to check the records for accuracy. Rather, the goal is to ensure that\n"
+			"the resolver is called into as expected, that we regain control before the query\n"
+			"is completed, and to ensure that nothing tried to cancel the resolution.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (ast_dns_resolver_register(&test_resolver)) {
+		ast_test_status_update(test, "Unable to register test resolver\n");
+		return AST_TEST_FAIL;
+	}
+
+	resolver_data_init();
+
+	async_data = async_data_alloc();
+	if (!async_data) {
+		ast_test_status_update(test, "Failed to allocate asynchronous data\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+	query = ast_dns_resolve_async("asterisk.org", ns_t_a, ns_c_in, async_callback, async_data);
+	if (!query) {
+		ast_test_status_update(test, "Asynchronous resolution of address failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+	if (!test_resolver_data.resolve_called) {
+		ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+	if (test_resolver_data.canceled) {
+		ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+	clock_gettime(CLOCK_REALTIME, &timeout);
+	timeout.tv_sec += 10;
+	ast_mutex_lock(&async_data->lock);
+	while (!async_data->complete) {
+		if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {
+			break;
+		}
+	}
+	ast_mutex_unlock(&async_data->lock);
+
+	if (!async_data->complete) {
+		ast_test_status_update(test, "Asynchronous resolution timed out\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+	if (!test_resolver_data.resolution_complete) {
+		ast_test_status_update(test, "Asynchronous resolution completed early?\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	
+	result = ast_dns_query_get_result(query);
+	if (!result) {
+		ast_test_status_update(test, "Asynchronous resolution yielded no result\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+	if (!ast_dns_result_get_records(result)) {
+		ast_test_status_update(test, "Asynchronous result had no records\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+
+cleanup:
+	ast_dns_resolver_unregister(&test_resolver);
+	resolver_data_cleanup();
+	return res;
+}
+
+/*! Stub async resolution callback */
 static void stub_callback(const struct ast_dns_query *query)
 {
 	return;




More information about the svn-commits mailing list