From c8586fd938d5d8fd3db117aa9a496b9a95ca0bfc Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 20 Jul 2009 16:40:22 +0200 Subject: Add async resolver tests Add some basic unit tests of the async resolver module. One of the tests resolves a name on the Internet, therefore it is off by default and is turned on with the -n switch. --- server/Makefile.am | 14 +- server/tests/resolv-tests.c | 442 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 server/tests/resolv-tests.c diff --git a/server/Makefile.am b/server/Makefile.am index 4ee344bbd..4398259b5 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -52,7 +52,8 @@ sssdlibexec_PROGRAMS = \ if BUILD_TESTS noinst_PROGRAMS = \ sysdb-tests \ - stress-tests + stress-tests \ + resolv-tests endif sssdlib_LTLIBRARIES = \ @@ -328,6 +329,17 @@ stress_tests_SOURCES = \ stress_tests_LDADD = \ $(SSSD_LIBS) +resolv_tests_SOURCES = \ + tests/resolv-tests.c \ + $(SSSD_UTIL_OBJ) \ + $(SSSD_RESOLV_OBJ) +resolv_tests_CFLAGS = \ + $(CHECK_CFLAGS) +resolv_tests_LDADD = \ + $(SSSD_LIBS) \ + $(CHECK_LIBS) \ + $(CARES_LIBS) + endif #BUILD_TESTS #################### diff --git a/server/tests/resolv-tests.c b/server/tests/resolv-tests.c new file mode 100644 index 000000000..4b3a19e3d --- /dev/null +++ b/server/tests/resolv-tests.c @@ -0,0 +1,442 @@ +/* + SSSD + + Async resolver tests + + Authors: + Martin Nagy + Jakub Hrozek + + Copyright (C) Red Hat, Inc 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include "util/util.h" + +/* Interface under test */ +#include "resolv/async_resolv.h" + +#ifndef tevent_req_set_callback +#define tevent_req_set_callback(req, func, data) \ + do { req->async.fn = func; req->async.private_data = data; } while(0) +#endif + +#ifndef tevent_req_callback_data +#define tevent_req_callback_data(req, type) ((type *)req->async.private_data) +#endif + +int use_net_test; + +struct resolv_test_ctx { + struct tevent_context *ev; + struct resolv_ctx *resolv; + + int error; + bool done; +}; + +static int setup_resolv_test(struct resolv_test_ctx **ctx) +{ + struct resolv_test_ctx *test_ctx; + int ret; + + test_ctx = talloc_zero(NULL, struct resolv_test_ctx); + if (test_ctx == NULL) { + fail("Could not allocate memory for test context"); + talloc_free(test_ctx); + return ENOMEM; + } + + test_ctx->ev = tevent_context_init(NULL); + if (test_ctx->ev == NULL) { + fail("Could not init tevent context"); + talloc_free(test_ctx); + return EFAULT; + } + + ret = resolv_init(NULL, test_ctx->ev, &test_ctx->resolv); + if (ret != EOK) { + fail("Could not init resolv context"); + talloc_free(test_ctx); + return ret; + } + + *ctx = test_ctx; + return EOK; +} + +static int test_loop(struct resolv_test_ctx *data) +{ + while (!data->done) + tevent_loop_once(data->ev); + + return data->error; +} + +static void test_localhost(struct tevent_req *req) +{ + int recv_status; + int status; + const struct hostent *hostent; + int i; + struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, + struct resolv_test_ctx); + + test_ctx->done = true; + + recv_status = resolv_gethostbyname_recv(req, &status, NULL, &hostent); + if (recv_status != EOK) { + DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); + test_ctx->error = recv_status; + return; + } + DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); + + test_ctx->error = ENOENT; + for (i = 0; hostent->h_addr_list[i]; i++) { + char addr_buf[256]; + inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], addr_buf, sizeof(addr_buf)); + + /* test that localhost resolves to 127.0.0.1 or ::1 */ + if (strcmp(addr_buf, "127.0.0.1") == 0 || strcmp(addr_buf, "::1") == 0) { + test_ctx->error = EOK; + } + } +} + +START_TEST(test_resolv_localhost) +{ + struct resolv_test_ctx *test_ctx; + int ret = EOK; + struct tevent_req *req; + const char *hostname = "localhost.localdomain"; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + req = resolv_gethostbyname_send(test_ctx->ev, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_localhost, test_ctx); + ret = test_loop(test_ctx); + } + + fail_unless(ret == EOK); + + talloc_zfree(test_ctx); +} +END_TEST + +static void test_negative(struct tevent_req *req) +{ + int recv_status; + int status; + const struct hostent *hostent; + struct resolv_test_ctx *test_ctx; + + test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); + test_ctx->done = true; + + recv_status = resolv_gethostbyname_recv(req, &status, NULL, &hostent); + if (recv_status == EOK) { + DEBUG(7, ("resolv_gethostbyname_recv succeeded in a negative test")); + return; + } + + test_ctx->error = status; + DEBUG(2, ("resolv_gethostbyname_recv status: %d: %s\n", status, resolv_strerror(status))); +} + +START_TEST(test_resolv_negative) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "sssd.foo"; + struct resolv_test_ctx *test_ctx; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + req = resolv_gethostbyname_send(test_ctx->ev, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_negative, test_ctx); + ret = test_loop(test_ctx); + } + + fail_unless(ret != EOK); + fail_unless(test_ctx->error == ARES_ENOTFOUND); + talloc_zfree(test_ctx); +} +END_TEST + +static void test_internet(struct tevent_req *req) +{ + int recv_status; + int status; + const struct hostent *hostent; + struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, + struct resolv_test_ctx); + + test_ctx->done = true; + + recv_status = resolv_gethostbyname_recv(req, &status, NULL, &hostent); + if (recv_status != EOK) { + DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); + test_ctx->error = recv_status; + return; + } + DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); + + test_ctx->error = (hostent->h_length == 0) ? ENOENT : EOK; +} + +START_TEST(test_resolv_internet) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "redhat.com"; + struct resolv_test_ctx *test_ctx; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + req = resolv_gethostbyname_send(test_ctx->ev, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_internet, test_ctx); + ret = test_loop(test_ctx); + } + + fail_unless(ret == EOK); + talloc_zfree(test_ctx); +} +END_TEST + +static void resolv_free_context(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct resolv_ctx *rctx = talloc_get_type(ptr, struct resolv_ctx); + DEBUG(7, ("freeing the context\n")); + + talloc_free(rctx); +} + +static void resolv_free_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct resolv_test_ctx *tctx = talloc_get_type(ptr, struct resolv_test_ctx); + DEBUG(7, ("marking test as done\n")); + + tctx->error = EOK; + tctx->done = true; +} + +START_TEST(test_resolv_free_context) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "redhat.com"; + struct resolv_test_ctx *test_ctx; + struct tevent_timer *free_timer, *terminate_timer; + struct timeval free_tv, terminate_tv; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + fail("Error calling resolv_gethostbyname_send"); + return; + } + + gettimeofday(&free_tv, NULL); + free_tv.tv_sec += 1; + free_tv.tv_usec = 0; + terminate_tv.tv_sec = free_tv.tv_sec + 1; + terminate_tv.tv_usec = 0; + + free_timer = tevent_add_timer(test_ctx->ev, test_ctx, free_tv, resolv_free_context, test_ctx->resolv); + if (free_timer == NULL) { + fail("Error calling tevent_add_timer"); + return; + } + + terminate_timer = tevent_add_timer(test_ctx->ev, test_ctx, terminate_tv, resolv_free_done, test_ctx); + if (terminate_timer == NULL) { + fail("Error calling tevent_add_timer"); + return; + } + + ret = test_loop(test_ctx); + fail_unless(ret == EOK); + + talloc_zfree(test_ctx); +} +END_TEST + +static void resolv_free_req(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct tevent_req *req = talloc_get_type(ptr, struct tevent_req); + DEBUG(7, ("freeing the request\n")); + + talloc_free(req); +} + +START_TEST(test_resolv_free_req) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "redhat.com"; + struct resolv_test_ctx *test_ctx; + struct tevent_timer *free_timer, *terminate_timer; + struct timeval free_tv, terminate_tv; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname, AF_INET); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + fail("Error calling resolv_gethostbyname_send"); + return; + } + + gettimeofday(&free_tv, NULL); + free_tv.tv_sec += 1; + free_tv.tv_usec = 0; + terminate_tv.tv_sec = free_tv.tv_sec + 1; + terminate_tv.tv_usec = 0; + + free_timer = tevent_add_timer(test_ctx->ev, test_ctx, free_tv, resolv_free_req, req); + if (free_timer == NULL) { + fail("Error calling tevent_add_timer"); + return; + } + + terminate_timer = tevent_add_timer(test_ctx->ev, test_ctx, terminate_tv, resolv_free_done, test_ctx); + if (terminate_timer == NULL) { + fail("Error calling tevent_add_timer"); + return; + } + + ret = test_loop(test_ctx); + fail_unless(ret == EOK); + + talloc_zfree(test_ctx); +} +END_TEST + +Suite *create_resolv_suite(void) +{ + Suite *s = suite_create("resolv"); + + TCase *tc_resolv = tcase_create("RESOLV Tests"); + + /* Do some testing */ + tcase_add_test(tc_resolv, test_resolv_localhost); + tcase_add_test(tc_resolv, test_resolv_negative); + if (use_net_test) { + tcase_add_test(tc_resolv, test_resolv_internet); + } + tcase_add_test(tc_resolv, test_resolv_free_context); + tcase_add_test(tc_resolv, test_resolv_free_req); + + /* Add all test cases to the test suite */ + suite_add_tcase(s, tc_resolv); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int failure_count; + Suite *resolv_suite; + SRunner *sr; + int debug; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, + { "use-net-test", 'n', POPT_ARG_NONE, 0, 'n', "Run tests that need an active internet connection", NULL }, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case 'n': + use_net_test = 1; + break; + + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + debug_level = debug; + + resolv_suite = create_resolv_suite(); + sr = srunner_create(resolv_suite); + srunner_run_all(sr, CK_VERBOSE); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + -- cgit