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; -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits