[asterisk-commits] mmichelson: branch group/dns r432531 - /team/group/dns/tests/test_dns.c
SVN commits to the Asterisk project
asterisk-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 asterisk-commits
mailing list