Author: file Date: Sat Apr 4 18:58:13 2015 New Revision: 433995 URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=433995 Log: Add unit tests for DNS query set functionality.
Added: team/group/dns_pjsip/tests/test_dns_query_set.c (with props) Modified: team/group/dns_pjsip/include/asterisk/dns_internal.h team/group/dns_pjsip/main/dns_core.c team/group/dns_pjsip/main/dns_query_set.c team/group/dns_pjsip/main/dns_recurring.c team/group/dns_pjsip/main/dns_srv.c Modified: team/group/dns_pjsip/include/asterisk/dns_internal.h URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/include/asterisk/dns_internal.h?view=diff&rev=433995&r1=433994&r2=433995 ============================================================================== --- team/group/dns_pjsip/include/asterisk/dns_internal.h (original) +++ team/group/dns_pjsip/include/asterisk/dns_internal.h Sat Apr 4 18:58:13 2015 @@ -136,6 +136,28 @@ char name[0]; }; +/*! \brief A DNS query set query, which includes its state */ +struct dns_query_set_query { + /*! \brief Whether the query started successfully or not */ + unsigned int started; + /*! \brief THe query itself */ + struct ast_dns_query *query; +}; + +/*! \brief A set of DNS queries */ +struct ast_dns_query_set { + /*! \brief DNS queries */ + AST_VECTOR(, struct dns_query_set_query) queries; + /*! \brief The total number of completed queries */ + int queries_completed; + /*! \brief The total number of cancelled queries */ + int queries_cancelled; + /*! \brief Callback to invoke upon completion */ + ast_dns_query_set_callback callback; + /*! \brief User-specific data */ + void *user_data; +}; + /*! \brief An active DNS query */ struct ast_dns_query_active { /*! \brief The underlying DNS query */ Modified: team/group/dns_pjsip/main/dns_core.c URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/main/dns_core.c?view=diff&rev=433995&r1=433994&r2=433995 ============================================================================== --- team/group/dns_pjsip/main/dns_core.c (original) +++ team/group/dns_pjsip/main/dns_core.c Sat Apr 4 18:58:13 2015 @@ -40,6 +40,7 @@ #include "asterisk/dns_srv.h" #include "asterisk/dns_tlsa.h" #include "asterisk/dns_recurring.h" +#include "asterisk/dns_query_set.h" #include "asterisk/dns_resolver.h" #include "asterisk/dns_internal.h" Modified: team/group/dns_pjsip/main/dns_query_set.c URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/main/dns_query_set.c?view=diff&rev=433995&r1=433994&r2=433995 ============================================================================== --- team/group/dns_pjsip/main/dns_query_set.c (original) +++ team/group/dns_pjsip/main/dns_query_set.c Sat Apr 4 18:58:13 2015 @@ -40,28 +40,6 @@ #include "asterisk/dns_internal.h" #include "asterisk/dns_resolver.h" -/*! \brief A DNS query, which includes its state */ -struct dns_query_set_query { - /*! \brief Whether the query started successfully or not */ - unsigned int started; - /*! \brief THe query itself */ - struct ast_dns_query *query; -}; - -/*! \brief A set of DNS queries */ -struct ast_dns_query_set { - /*! \brief DNS queries */ - AST_VECTOR(, struct dns_query_set_query) queries; - /*! \brief The total number of completed queries */ - int queries_completed; - /*! \brief The total number of cancelled queries */ - int queries_cancelled; - /*! \brief Callback to invoke upon completion */ - ast_dns_query_set_callback callback; - /*! \brief User-specific data */ - void *user_data; -}; - /*! \brief Release all queries held in a query set */ static void dns_query_set_release(struct ast_dns_query_set *query_set) { @@ -113,7 +91,7 @@ } /* All queries have been completed, invoke final callback */ - if (query_set->queries_cancelled != query_set->queries_completed) { + if (query_set->queries_cancelled != AST_VECTOR_SIZE(&query_set->queries)) { query_set->callback(query_set); } @@ -242,23 +220,20 @@ int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set) { int idx; + size_t query_count = AST_VECTOR_SIZE(&query_set->queries); for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) { struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx); if (query->started) { if (!query->query->resolver->cancel(query->query)) { - ast_atomic_fetchadd_int(&query_set->queries_cancelled, +1); + query_set->queries_cancelled++; dns_query_set_callback(query->query); } } else { - ast_atomic_fetchadd_int(&query_set->queries_cancelled, +1); + query_set->queries_cancelled++; } } - if (query_set->queries_cancelled == query_set->queries_completed) { - dns_query_set_release(query_set); - } - - return (query_set->queries_cancelled == query_set->queries_completed) ? 0 : -1; + return (query_set->queries_cancelled == query_count) ? 0 : -1; } Modified: team/group/dns_pjsip/main/dns_recurring.c URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/main/dns_recurring.c?view=diff&rev=433995&r1=433994&r2=433995 ============================================================================== --- team/group/dns_pjsip/main/dns_recurring.c (original) +++ team/group/dns_pjsip/main/dns_recurring.c Sat Apr 4 18:58:13 2015 @@ -33,10 +33,12 @@ #include "asterisk/astobj2.h" #include "asterisk/linkedlists.h" +#include "asterisk/vector.h" #include "asterisk/sched.h" #include "asterisk/strings.h" #include "asterisk/dns_core.h" #include "asterisk/dns_recurring.h" +#include "asterisk/dns_query_set.h" #include "asterisk/dns_internal.h" #include <arpa/nameser.h> Modified: team/group/dns_pjsip/main/dns_srv.c URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/main/dns_srv.c?view=diff&rev=433995&r1=433994&r2=433995 ============================================================================== --- team/group/dns_pjsip/main/dns_srv.c (original) +++ team/group/dns_pjsip/main/dns_srv.c Sat Apr 4 18:58:13 2015 @@ -38,6 +38,8 @@ #include "asterisk/dns_core.h" #include "asterisk/dns_srv.h" #include "asterisk/linkedlists.h" +#include "asterisk/vector.h" +#include "asterisk/dns_query_set.h" #include "asterisk/dns_internal.h" #include "asterisk/utils.h" Added: team/group/dns_pjsip/tests/test_dns_query_set.c URL: http://svnview.digium.com/svn/asterisk/team/group/dns_pjsip/tests/test_dns_query_set.c?view=auto&rev=433995 ============================================================================== --- team/group/dns_pjsip/tests/test_dns_query_set.c (added) +++ team/group/dns_pjsip/tests/test_dns_query_set.c Sat Apr 4 18:58:13 2015 @@ -1,0 +1,365 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2015, Digium, Inc. + * + * Joshua Colp <jc...@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include "asterisk/test.h" +#include "asterisk/module.h" +#include "asterisk/vector.h" +#include "asterisk/dns_core.h" +#include "asterisk/dns_resolver.h" +#include "asterisk/dns_query_set.h" +#include "asterisk/dns_internal.h" + +struct query_set_data { + /*! Boolean indicator if query set has completed */ + int query_set_complete; + /*! Number of times resolve() method has been called */ + int resolves; + /*! Number of times resolve() method is allowed to be called */ + int resolves_allowed; + /*! Number of times cancel() method has been called */ + int cancel; + /*! Number of times cancel() method is allowed to be called */ + int cancel_allowed; + ast_mutex_t lock; + ast_cond_t cond; +}; + +static void query_set_data_destructor(void *obj) +{ + struct query_set_data *qsdata = obj; + + ast_mutex_destroy(&qsdata->lock); + ast_cond_destroy(&qsdata->cond); +} + +static struct query_set_data *query_set_data_alloc(void) +{ + struct query_set_data *qsdata; + + qsdata = ao2_alloc(sizeof(*qsdata), query_set_data_destructor); + if (!qsdata) { + return NULL; + } + + ast_mutex_init(&qsdata->lock); + ast_cond_init(&qsdata->cond, NULL); + + return qsdata; +} + +#define DNS_ANSWER "Yes sirree" +#define DNS_ANSWER_SIZE strlen(DNS_ANSWER) + +/*! + * \brief Thread that performs asynchronous resolution. + * + * This thread uses the query's user data to determine how to + * perform the resolution. If the allowed number of resolutions + * has not been reached then this will succeed, otherwise the + * query is expected to have been canceled. + * + * \param dns_query The ast_dns_query that is being performed + * \return NULL + */ +static void *resolution_thread(void *dns_query) +{ + struct ast_dns_query *query = dns_query; + struct ast_dns_query_set *query_set = ast_dns_query_get_data(query); + struct query_set_data *qsdata = query_set->user_data; + + ast_assert(qsdata != NULL); + + ast_dns_resolver_set_result(query, 0, 0, ns_r_noerror, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE); + ast_dns_resolver_completed(query); + + ao2_ref(query, -1); + return NULL; +} + +/*! + * \brief Resolver's resolve() method + * + * \param query The query that is to be resolved + * \retval 0 Successfully created thread to perform the resolution + * \retval non-zero Failed to create resolution thread + */ +static int query_set_resolve(struct ast_dns_query *query) +{ + struct ast_dns_query_set *query_set = ast_dns_query_get_data(query); + struct query_set_data *qsdata = query_set->user_data; + pthread_t resolver_thread; + + /* Only the queries which will not be canceled actually start a thread */ + if (qsdata->resolves++ < qsdata->cancel_allowed) { + return 0; + } + + return ast_pthread_create_detached(&resolver_thread, NULL, resolution_thread, ao2_bump(query)); +} + +/*! + * \brief Resolver's cancel() method + * + * \param query The query to cancel + * \return 0 + */ +static int query_set_cancel(struct ast_dns_query *query) +{ + struct ast_dns_query_set *query_set = ast_dns_query_get_data(query); + struct query_set_data *qsdata = query_set->user_data; + int res = -1; + + if (qsdata->cancel++ < qsdata->cancel_allowed) { + res = 0; + } + + return res; +} + +static struct ast_dns_resolver query_set_resolver = { + .name = "query_set", + .priority = 0, + .resolve = query_set_resolve, + .cancel = query_set_cancel, +}; + +/*! + * \brief Callback which is invoked upon query set completion + * + * \param query_set The query set + */ +static void query_set_callback(const struct ast_dns_query_set *query_set) +{ + struct query_set_data *qsdata = ast_dns_query_set_get_data(query_set); + + ast_mutex_lock(&qsdata->lock); + qsdata->query_set_complete = 1; + ast_cond_signal(&qsdata->cond); + ast_mutex_unlock(&qsdata->lock); +} + +/*! + * \brief Framework for running a query set DNS test + * + * This function serves as a common way of testing various numbers of queries in a + * query set and optional canceling of them. + * + * \param test The test being run + * \param resolve The number of queries that should be allowed to complete resolution + * \param cancel The number of queries that should be allowed to be canceled + */ +static enum ast_test_result_state query_set_test(struct ast_test *test, int resolve, int cancel) +{ + int total = resolve + cancel; + RAII_VAR(struct ast_dns_query_set *, query_set, NULL, ao2_cleanup); + RAII_VAR(struct query_set_data *, qsdata, NULL, ao2_cleanup); + enum ast_test_result_state res = AST_TEST_PASS; + int idx; + struct timespec timeout; + + if (ast_dns_resolver_register(&query_set_resolver)) { + ast_test_status_update(test, "Failed to register query set DNS resolver\n"); + return AST_TEST_FAIL; + } + + qsdata = query_set_data_alloc(); + if (!qsdata) { + ast_test_status_update(test, "Failed to allocate data necessary for query set test\n"); + res = AST_TEST_FAIL; + goto cleanup; + } + + query_set = ast_dns_query_set_create(); + if (!query_set) { + ast_test_status_update(test, "Failed to create DNS query set\n"); + res = AST_TEST_FAIL; + goto cleanup; + } + + qsdata->resolves_allowed = resolve; + qsdata->cancel_allowed = cancel; + + for (idx = 0; idx < total; ++idx) { + if (ast_dns_query_set_add(query_set, "asterisk.org", ns_t_a, ns_c_in)) { + ast_test_status_update(test, "Failed to add query to DNS query set\n"); + res = AST_TEST_FAIL; + goto cleanup; + } + } + + if (ast_dns_query_set_num_queries(query_set) != total) { + ast_test_status_update(test, "DNS query set does not contain the correct number of queries\n"); + res = AST_TEST_FAIL; + goto cleanup; + } + + ast_dns_query_set_resolve_async(query_set, query_set_callback, qsdata); + + if (cancel && (cancel == total)) { + if (ast_dns_query_set_resolve_cancel(query_set)) { + ast_test_status_update(test, "Failed to cancel DNS query set when it should be cancellable\n"); + res = AST_TEST_FAIL; + } + + if (qsdata->query_set_complete) { + ast_test_status_update(test, "Query set callback was invoked despite all queries being cancelled\n"); + res = AST_TEST_FAIL; + } + + goto cleanup; + } else if (cancel) { + if (!ast_dns_query_set_resolve_cancel(query_set)) { + ast_test_status_update(test, "Successfully cancelled DNS query set when it should not be possible\n"); + res = AST_TEST_FAIL; + goto cleanup; + } + } + + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_sec += 10; + + ast_mutex_lock(&qsdata->lock); + while (!qsdata->query_set_complete) { + if (ast_cond_timedwait(&qsdata->cond, &qsdata->lock, &timeout) == ETIMEDOUT) { + break; + } + } + ast_mutex_unlock(&qsdata->lock); + + if (!qsdata->query_set_complete) { + ast_test_status_update(test, "Query set did not complete when it should have\n"); + res = AST_TEST_FAIL; + goto cleanup; + } + + for (idx = 0; idx < ast_dns_query_set_num_queries(query_set); ++idx) { + const struct ast_dns_query *query = ast_dns_query_set_get(query_set, idx); + + if (strcmp(ast_dns_query_get_name(query), "asterisk.org")) { + ast_test_status_update(test, "Query did not have expected name\n"); + res = AST_TEST_FAIL; + } + if (ast_dns_query_get_rr_type(query) != ns_t_a) { + ast_test_status_update(test, "Query did not have expected type\n"); + res = AST_TEST_FAIL; + } + if (ast_dns_query_get_rr_class(query) != ns_c_in) { + ast_test_status_update(test, "Query did not have expected class\n"); + res = AST_TEST_FAIL; + } + } + +cleanup: + ast_dns_resolver_unregister(&query_set_resolver); + return res; +} + +AST_TEST_DEFINE(query_set) +{ + switch (cmd) { + case TEST_INIT: + info->name = "query_set"; + info->category = "/main/dns/query_set/"; + info->summary = "Test nominal asynchronous DNS query set\n"; + info->description = + "This tests nominal query set in the following ways:\n" + "\t* Multiple queries are added to a query set\n" + "\t* The mock resolver is configured to respond to all queries\n" + "\t* Asynchronous resolution of the query set is started\n" + "\t* The mock resolver responds to all queries\n" + "\t* We ensure that the query set callback is invoked upon completion\n"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + return query_set_test(test, 4, 0); +} + +AST_TEST_DEFINE(query_set_nominal_cancel) +{ + switch (cmd) { + case TEST_INIT: + info->name = "query_set_nominal_cancel"; + info->category = "/main/dns/query_set/"; + info->summary = "Test nominal asynchronous DNS query set cancellation\n"; + info->description = + "This tests nominal query set cancellation in the following ways:\n" + "\t* Multiple queries are added to a query set\n" + "\t* The mock resolver is configured to NOT respond to any queries\n" + "\t* Asynchronous resolution of the query set is started\n" + "\t* The query set is canceled and is confirmed to return with success\n"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + return query_set_test(test, 0, 4); +} + +AST_TEST_DEFINE(query_set_off_nominal_cancel) +{ + switch (cmd) { + case TEST_INIT: + info->name = "query_set_off_nominal_cancel"; + info->category = "/main/dns/query_set/"; + info->summary = "Test off-nominal asynchronous DNS query set cancellation\n"; + info->description = + "This tests nominal query set cancellation in the following ways:\n" + "\t* Multiple queries are added to a query set\n" + "\t* The mock resolver is configured to respond to half the queries\n" + "\t* Asynchronous resolution of the query set is started\n" + "\t* The query set is canceled and is confirmed to return failure\n" + "\t* The query set callback is confirmed to run, since it could not be fully canceled\n"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + return query_set_test(test, 2, 2); +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(query_set); + AST_TEST_UNREGISTER(query_set_nominal_cancel); + AST_TEST_UNREGISTER(query_set_off_nominal_cancel); + + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(query_set); + AST_TEST_REGISTER(query_set_nominal_cancel); + AST_TEST_REGISTER(query_set_off_nominal_cancel); + + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS query set tests"); Propchange: team/group/dns_pjsip/tests/test_dns_query_set.c ------------------------------------------------------------------------------ svn:eol-style = native Propchange: team/group/dns_pjsip/tests/test_dns_query_set.c ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Propchange: team/group/dns_pjsip/tests/test_dns_query_set.c ------------------------------------------------------------------------------ svn:mime-type = text/plain -- _____________________________________________________________________ -- 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