1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207 |
- // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #include <config.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <string.h>
- #include <boost/lexical_cast.hpp>
- #include <boost/bind.hpp>
- #include <boost/date_time/posix_time/posix_time_types.hpp>
- #include <gtest/gtest.h>
- #include <exceptions/exceptions.h>
- #include <dns/tests/unittest_util.h>
- #include <dns/rcode.h>
- #include <dns/buffer.h>
- #include <dns/message.h>
- // IMPORTANT: We shouldn't directly use ASIO definitions in this test.
- // In particular, we must not include asio.hpp in this file.
- // The asiolink module is primarily intended to be a wrapper that hide the
- // details of the underlying implementations. We need to test the wrapper
- // level behaviors. In addition, some compilers reject to compile this file
- // if we include asio.hpp unless we specify a special compiler option.
- // If we need to test something at the level of underlying ASIO and need
- // their definition, that test should go to asiolink/internal/tests.
- #include <asiolink/asiolink.h>
- #include <asiolink/iosocket.h>
- using isc::UnitTestUtil;
- using namespace std;
- using namespace asiolink;
- using namespace isc::dns;
- namespace {
- const char* const TEST_SERVER_PORT = "53535";
- const char* const TEST_CLIENT_PORT = "53536";
- const char* const TEST_IPV6_ADDR = "::1";
- const char* const TEST_IPV4_ADDR = "127.0.0.1";
- // This data is intended to be valid as a DNS/TCP-like message: the first
- // two octets encode the length of the rest of the data. This is crucial
- // for the tests below.
- const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
- // TODO: Consider this margin
- const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
- boost::posix_time::milliseconds(50);
- TEST(IOAddressTest, fromText) {
- IOAddress io_address_v4("192.0.2.1");
- EXPECT_EQ("192.0.2.1", io_address_v4.toText());
- IOAddress io_address_v6("2001:db8::1234");
- EXPECT_EQ("2001:db8::1234", io_address_v6.toText());
- // bogus IPv4 address-like input
- EXPECT_THROW(IOAddress("192.0.2.2.1"), IOError);
- // bogus IPv4 address-like input: out-of-range octet
- EXPECT_THROW(IOAddress("192.0.2.300"), IOError);
- // bogus IPv6 address-like input
- EXPECT_THROW(IOAddress("2001:db8:::1234"), IOError);
- // bogus IPv6 address-like input
- EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
- }
- TEST(IOAddressTest, Equality) {
- EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
- EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
- EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
- EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
- EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
- EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
- EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
- EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
- EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
- EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
- }
- TEST(IOEndpointTest, createUDPv4) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5300);
- EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
- EXPECT_EQ(5300, ep->getPort());
- EXPECT_EQ(AF_INET, ep->getFamily());
- EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
- EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
- }
- TEST(IOEndpointTest, createTCPv4) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5301);
- EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
- EXPECT_EQ(5301, ep->getPort());
- EXPECT_EQ(AF_INET, ep->getFamily());
- EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
- EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
- }
- TEST(IOEndpointTest, createUDPv6) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5302);
- EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
- EXPECT_EQ(5302, ep->getPort());
- EXPECT_EQ(AF_INET6, ep->getFamily());
- EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
- EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
- }
- TEST(IOEndpointTest, createTCPv6) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303);
- EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
- EXPECT_EQ(5303, ep->getPort());
- EXPECT_EQ(AF_INET6, ep->getFamily());
- EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
- EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
- }
- TEST(IOEndpointTest, createIPProto) {
- EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
- 5300)->getAddress().toText(),
- IOError);
- }
- TEST(IOSocketTest, dummySockets) {
- EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
- EXPECT_EQ(IPPROTO_TCP, IOSocket::getDummyTCPSocket().getProtocol());
- EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
- EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
- }
- TEST(IOServiceTest, badPort) {
- IOService io_service;
- EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *"5300.0", true, false, NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
- }
- TEST(IOServiceTest, badAddress) {
- IOService io_service;
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
- }
- TEST(IOServiceTest, unavailableAddress) {
- IOService io_service;
- // These addresses should generally be unavailable as a valid local
- // address, although there's no guarantee in theory.
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
- // Some OSes would simply reject binding attempt for an AF_INET6 socket
- // to an IPv4-mapped IPv6 address. Even if those that allow it, since
- // the corresponding IPv4 address is the same as the one used in the
- // AF_INET socket case above, it should at least show the same result
- // as the previous one.
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
- }
- TEST(IOServiceTest, duplicateBind_v6) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
- // IPv6, "any" address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
- delete dns_service;
- }
- TEST(IOServiceTest, duplicateBind_v6_address) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
- // IPv6, specific address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
- delete dns_service;
- }
- TEST(IOServiceTest, duplicateBind_v4) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
- // IPv4, "any" address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
- delete dns_service;
- }
- TEST(IOServiceTest, duplicateBind_v4_address) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
- // IPv4, specific address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
- delete dns_service;
- }
- // Disabled because IPv4-mapped addresses don't seem to be working with
- // the IOService constructor
- TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
- IOService io_service;
- // Duplicate bind on IPv4-mapped IPv6 address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
- delete dns_service;
- // XXX:
- // Currently, this throws an "invalid argument" exception. I have
- // not been able to get IPv4-mapped addresses to work.
- dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
- delete dns_service;
- }
- // This function returns an addrinfo structure for use by tests, using
- // different addresses and ports depending on whether we're testing
- // IPv4 or v6, TCP or UDP, and client or server operation.
- struct addrinfo*
- resolveAddress(const int family, const int protocol, const bool client) {
- const char* const addr = (family == AF_INET6) ?
- TEST_IPV6_ADDR : TEST_IPV4_ADDR;
- const char* const port = client ? TEST_CLIENT_PORT : TEST_SERVER_PORT;
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = family;
- hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
- hints.ai_protocol = protocol;
- hints.ai_flags = AI_NUMERICSERV;
- struct addrinfo* res;
- const int error = getaddrinfo(addr, port, &hints, &res);
- if (error != 0) {
- isc_throw(IOError, "getaddrinfo failed: " << gai_strerror(error));
- }
- return (res);
- }
- // This fixture is a framework for various types of network operations
- // using the ASIO interfaces. Each test case creates an IOService object,
- // opens a local "client" socket for testing, sends data via the local socket
- // to the service that would run in the IOService object.
- // A mock callback function (an ASIOCallBack object) is registered with the
- // IOService object, so the test code should be able to examine the data
- // received on the server side. It then checks the received data matches
- // expected parameters.
- // If initialization parameters of the IOService should be modified, the test
- // case can do it using the setDNSService() method.
- // Note: the set of tests in ASIOLinkTest use actual network services and may
- // involve undesirable side effects such as blocking.
- class ASIOLinkTest : public ::testing::Test {
- protected:
- ASIOLinkTest();
- ~ASIOLinkTest() {
- if (res_ != NULL) {
- freeaddrinfo(res_);
- }
- if (sock_ != -1) {
- close(sock_);
- }
- delete dns_service_;
- delete callback_;
- delete io_service_;
- }
- // Send a test UDP packet to a mock server
- void sendUDP(const int family) {
- res_ = resolveAddress(family, IPPROTO_UDP, false);
- sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
- if (sock_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- const int cc = sendto(sock_, test_data, sizeof(test_data), 0,
- res_->ai_addr, res_->ai_addrlen);
- if (cc != sizeof(test_data)) {
- isc_throw(IOError, "unexpected sendto result: " << cc);
- }
- io_service_->run();
- }
- // Send a test TCP packet to a mock server
- void sendTCP(const int family) {
- res_ = resolveAddress(family, IPPROTO_TCP, false);
- sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
- if (sock_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- if (connect(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
- isc_throw(IOError, "failed to connect to the test server");
- }
- const int cc = send(sock_, test_data, sizeof(test_data), 0);
- if (cc != sizeof(test_data)) {
- isc_throw(IOError, "unexpected send result: " << cc);
- }
- io_service_->run();
- }
- // Receive a UDP packet from a mock server; used for testing
- // recursive lookup. The caller must place a RecursiveQuery
- // on the IO Service queue before running this routine.
- void recvUDP(const int family, void* buffer, size_t& size) {
- res_ = resolveAddress(family, IPPROTO_UDP, true);
- sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
- if (sock_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
- isc_throw(IOError, "bind failed: " << strerror(errno));
- }
- // The IO service queue should have a RecursiveQuery object scheduled
- // to run at this point. This call will cause it to begin an
- // async send, then return.
- io_service_->run_one();
- // ... and this one will block until the send has completed
- io_service_->run_one();
- // Now we attempt to recv() whatever was sent.
- // XXX: there's no guarantee the receiving socket can immediately get
- // the packet. Normally we can perform blocking recv to wait for it,
- // but in theory it's even possible that the packet is lost.
- // In order to prevent the test from hanging in such a worst case
- // we add an ad hoc timeout.
- const struct timeval timeo = { 10, 0 };
- int recv_options = 0;
- if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
- sizeof(timeo))) {
- if (errno == ENOPROTOOPT) {
- // Workaround for Solaris: it doesn't accept SO_RCVTIMEO
- // with the error of ENOPROTOOPT. Since this is a workaround
- // for rare error cases anyway, we simply switch to the
- // "don't wait" mode. If we still find an error in recv()
- // can happen often we'll consider a more complete solution.
- recv_options = MSG_DONTWAIT;
- } else {
- isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
- }
- }
- const int ret = recv(sock_, buffer, size, recv_options);
- if (ret < 0) {
- isc_throw(IOError, "recvfrom failed: " << strerror(errno));
- }
-
- // Pass the message size back via the size parameter
- size = ret;
- }
- // Set up an IO Service queue using the specified address
- void setDNSService(const char& address) {
- delete dns_service_;
- dns_service_ = NULL;
- delete io_service_;
- io_service_ = new IOService();
- callback_ = new ASIOCallBack(this);
- dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, address, callback_, NULL, NULL);
- }
- // Set up an IO Service queue using the "any" address, on IPv4 if
- // 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
- void setDNSService(const bool use_ipv4, const bool use_ipv6) {
- delete dns_service_;
- dns_service_ = NULL;
- delete io_service_;
- io_service_ = new IOService();
- callback_ = new ASIOCallBack(this);
- dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
- NULL, NULL);
- }
- // Set up empty DNS Service
- // Set up an IO Service queue without any addresses
- void setDNSService() {
- delete dns_service_;
- dns_service_ = NULL;
- delete io_service_;
- io_service_ = new IOService();
- callback_ = new ASIOCallBack(this);
- dns_service_ = new DNSService(*io_service_, callback_, NULL, NULL);
- }
- // Run a simple server test, on either IPv4 or IPv6, and over either
- // UDP or TCP. Calls the sendUDP() or sendTCP() methods, which will
- // start the IO Service queue. The UDPServer or TCPServer that was
- // created by setIOService() will receive the test packet and issue a
- // callback, which enables us to check that the data it received
- // matches what we sent.
- void doTest(const int family, const int protocol) {
- if (protocol == IPPROTO_UDP) {
- sendUDP(family);
- } else {
- sendTCP(family);
- }
- // There doesn't seem to be an effective test for the validity of
- // 'native'.
- // One thing we are sure is it must be different from our local socket.
- EXPECT_NE(sock_, callback_native_);
- EXPECT_EQ(protocol, callback_protocol_);
- EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
- callback_address_);
- const uint8_t* expected_data =
- protocol == IPPROTO_UDP ? test_data : test_data + 2;
- const size_t expected_datasize =
- protocol == IPPROTO_UDP ? sizeof(test_data) :
- sizeof(test_data) - 2;
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
- callback_data_.size(),
- expected_data, expected_datasize);
- }
- protected:
- // This is a nonfunctional mockup of a DNSServer object. Its purpose
- // is to resume after a recursive query or other asynchronous call
- // has completed.
- class MockServer : public DNSServer {
- public:
- explicit MockServer(IOService& io_service,
- SimpleCallback* checkin = NULL,
- DNSLookup* lookup = NULL,
- DNSAnswer* answer = NULL) :
- io_(io_service),
- message_(new Message(Message::PARSE)),
- answer_message_(new Message(Message::RENDER)),
- respbuf_(new OutputBuffer(0)),
- checkin_(checkin), lookup_(lookup), answer_(answer)
- {}
- void operator()(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {}
- void resume(const bool) {
- // should never be called in our tests
- }
- DNSServer* clone() {
- MockServer* s = new MockServer(*this);
- return (s);
- }
- inline void asyncLookup() {
- if (lookup_) {
- (*lookup_)(*io_message_, message_, answer_message_,
- respbuf_, this);
- }
- }
- protected:
- IOService& io_;
- bool done_;
- private:
- // Currently unused; these will be used for testing
- // asynchronous lookup calls via the asyncLookup() method
- boost::shared_ptr<asiolink::IOMessage> io_message_;
- isc::dns::MessagePtr message_;
- isc::dns::MessagePtr answer_message_;
- isc::dns::OutputBufferPtr respbuf_;
- // Callback functions provided by the caller
- const SimpleCallback* checkin_;
- const DNSLookup* lookup_;
- const DNSAnswer* answer_;
- };
- // This version of mock server just stops the io_service when it is resumed
- class MockServerStop : public MockServer {
- public:
- explicit MockServerStop(IOService& io_service, bool* done) :
- MockServer(io_service),
- done_(done)
- {}
- void resume(const bool done) {
- *done_ = done;
- io_.stop();
- }
- DNSServer* clone() {
- return (new MockServerStop(*this));
- }
- private:
- bool* done_;
- };
- private:
- class ASIOCallBack : public SimpleCallback {
- public:
- ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
- void operator()(const IOMessage& io_message) const {
- test_obj_->callBack(io_message);
- }
- private:
- ASIOLinkTest* test_obj_;
- };
- void callBack(const IOMessage& io_message) {
- callback_protocol_ = io_message.getSocket().getProtocol();
- callback_native_ = io_message.getSocket().getNative();
- callback_address_ =
- io_message.getRemoteEndpoint().getAddress().toText();
- callback_data_.assign(
- static_cast<const uint8_t*>(io_message.getData()),
- static_cast<const uint8_t*>(io_message.getData()) +
- io_message.getDataSize());
- io_service_->stop();
- }
- protected:
- // We use a pointer for io_service_, because for some tests we
- // need to recreate a new one within one onstance of this class
- IOService* io_service_;
- DNSService* dns_service_;
- ASIOCallBack* callback_;
- int callback_protocol_;
- int callback_native_;
- string callback_address_;
- vector<uint8_t> callback_data_;
- int sock_;
- struct addrinfo* res_;
- };
- ASIOLinkTest::ASIOLinkTest() :
- dns_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
- {
- io_service_ = new IOService();
- setDNSService(true, true);
- }
- TEST_F(ASIOLinkTest, v6UDPSend) {
- doTest(AF_INET6, IPPROTO_UDP);
- }
- TEST_F(ASIOLinkTest, v6TCPSend) {
- doTest(AF_INET6, IPPROTO_TCP);
- }
- TEST_F(ASIOLinkTest, v4UDPSend) {
- doTest(AF_INET, IPPROTO_UDP);
- }
- TEST_F(ASIOLinkTest, v4TCPSend) {
- doTest(AF_INET, IPPROTO_TCP);
- }
- TEST_F(ASIOLinkTest, v6UDPSendSpecific) {
- // Explicitly set a specific address to be bound to the socket.
- // The subsequent test does not directly ensures the underlying socket
- // is bound to the expected address, but the success of the tests should
- // reasonably suggest it works as intended.
- // Specifying an address also implicitly means the service runs in a
- // single address-family mode. In tests using TCP we can confirm that
- // by trying to make a connection and seeing a failure. In UDP, it'd be
- // more complicated because we need to use a connected socket and catch
- // an error on a subsequent read operation. We could do it, but for
- // simplicity we only tests the easier cases for now.
- setDNSService(*TEST_IPV6_ADDR);
- doTest(AF_INET6, IPPROTO_UDP);
- }
- TEST_F(ASIOLinkTest, v6TCPSendSpecific) {
- setDNSService(*TEST_IPV6_ADDR);
- doTest(AF_INET6, IPPROTO_TCP);
- EXPECT_THROW(sendTCP(AF_INET), IOError);
- }
- TEST_F(ASIOLinkTest, v4UDPSendSpecific) {
- setDNSService(*TEST_IPV4_ADDR);
- doTest(AF_INET, IPPROTO_UDP);
- }
- TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
- setDNSService(*TEST_IPV4_ADDR);
- doTest(AF_INET, IPPROTO_TCP);
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
- }
- TEST_F(ASIOLinkTest, v6AddServer) {
- setDNSService();
- dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
- doTest(AF_INET6, IPPROTO_TCP);
- EXPECT_THROW(sendTCP(AF_INET), IOError);
- }
- TEST_F(ASIOLinkTest, v4AddServer) {
- setDNSService();
- dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
- doTest(AF_INET, IPPROTO_TCP);
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
- }
- TEST_F(ASIOLinkTest, DISABLED_clearServers) {
- // FIXME: Enable when clearServers actually close the sockets
- // See #388
- setDNSService();
- dns_service_->clearServers();
- EXPECT_THROW(sendTCP(AF_INET), IOError);
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
- }
- TEST_F(ASIOLinkTest, v6TCPOnly) {
- // Open only IPv6 TCP socket. A subsequent attempt of establishing an
- // IPv4/TCP connection should fail. See above for why we only test this
- // for TCP.
- setDNSService(false, true);
- EXPECT_THROW(sendTCP(AF_INET), IOError);
- }
- TEST_F(ASIOLinkTest, v4TCPOnly) {
- setDNSService(true, false);
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
- }
- vector<pair<string, uint16_t> >
- singleAddress(const string &address, uint16_t port) {
- vector<pair<string, uint16_t> > result;
- result.push_back(pair<string, uint16_t>(address, port));
- return (result);
- }
- TEST_F(ASIOLinkTest, recursiveSetupV4) {
- setDNSService(true, false);
- uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port)));
- }
- TEST_F(ASIOLinkTest, recursiveSetupV6) {
- setDNSService(false, true);
- uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
- singleAddress(TEST_IPV6_ADDR, port),
- singleAddress(TEST_IPV6_ADDR,port)));
- }
- // XXX:
- // This is very inadequate unit testing. It should be generalized into
- // a routine that can do this with variable address family, address, and
- // port, and with the various callbacks defined in such a way as to ensure
- // full code coverage including error cases.
- TEST_F(ASIOLinkTest, forwarderSend) {
- setDNSService(true, false);
- // Note: We use the test prot plus one to ensure we aren't binding
- // to the same port as the actual server
- uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- MockServer server(*io_service_);
- RecursiveQuery rq(*dns_service_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port));
- Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- rq.resolve(q, answer, buffer, &server);
- char data[4096];
- size_t size = sizeof(data);
- ASSERT_NO_THROW(recvUDP(AF_INET, data, size));
- Message m(Message::PARSE);
- InputBuffer ibuf(data, size);
- // Make sure we can parse the message that was sent
- EXPECT_NO_THROW(m.parseHeader(ibuf));
- EXPECT_NO_THROW(m.fromWire(ibuf));
- // Check that the question sent matches the one we wanted
- QuestionPtr q2 = *m.beginQuestion();
- EXPECT_EQ(q.getName(), q2->getName());
- EXPECT_EQ(q.getType(), q2->getType());
- EXPECT_EQ(q.getClass(), q2->getClass());
- }
- int
- createTestSocket()
- {
- struct addrinfo* res_ = resolveAddress(AF_INET, IPPROTO_UDP, true);
- int sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
- if (sock_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
- isc_throw(IOError, "failed to bind test socket");
- }
- return sock_;
- }
- int
- setSocketTimeout(int sock_, size_t tv_sec, size_t tv_usec) {
- const struct timeval timeo = { tv_sec, tv_usec };
- int recv_options = 0;
- if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
- if (errno == ENOPROTOOPT) { // see ASIOLinkTest::recvUDP()
- recv_options = MSG_DONTWAIT;
- } else {
- isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
- }
- }
- return recv_options;
- }
- // try to read from the socket max time
- // *num is incremented for every succesfull read
- // returns true if it can read max times, false otherwise
- bool tryRead(int sock_, int recv_options, size_t max, int* num) {
- size_t i = 0;
- do {
- char inbuff[512];
- if (recv(sock_, inbuff, sizeof(inbuff), recv_options) < 0) {
- return false;
- } else {
- ++i;
- ++*num;
- }
- } while (i < max);
- return true;
- }
- // Test it tries the correct amount of times before giving up
- TEST_F(ASIOLinkTest, forwardQueryTimeout) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
- // Prepare the socket
- sock_ = createTestSocket();
- // Prepare the server
- bool done(true);
- MockServerStop server(*io_service_, &done);
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- RecursiveQuery query(*dns_service_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 10, 4000, 3000, 2);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- query.resolve(question, answer, buffer, &server);
- // Run the test
- io_service_->run();
- // Read up to 3 packets. Use some ad hoc timeout to prevent an infinite
- // block (see also recvUDP()).
- int recv_options = setSocketTimeout(sock_, 10, 0);
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 3, &num);
- // The query should fail
- EXPECT_FALSE(done);
- EXPECT_EQ(3, num);
- EXPECT_TRUE(read_success);
- }
- // If we set client timeout to lower than querytimeout, we should
- // get a failure answer, but still see retries
- // (no actual answer is given here yet)
- TEST_F(ASIOLinkTest, forwardClientTimeout) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
- sock_ = createTestSocket();
- // Prepare the server
- bool done(true);
- MockServerStop server(*io_service_, &done);
- MessagePtr answer(new Message(Message::RENDER));
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- // Set it up to retry twice before client timeout fires
- // Since the lookup timer has not fired, it should retry
- // a third time
- RecursiveQuery query(*dns_service_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 50, 120, 1000, 3);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- query.resolve(question, answer, buffer, &server);
- // Run the test
- io_service_->run();
- // we know it'll fail, so make it a shorter timeout
- int recv_options = setSocketTimeout(sock_, 1, 0);
- // Try to read 5 times, should stop after 3 reads
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 5, &num);
- // The query should fail (for resolver it should send back servfail,
- // but currently, and perhaps for forwarder in general, the effect
- // will be the same as on a lookup timeout, i.e. no answer is sent
- // back)
- EXPECT_FALSE(done);
- EXPECT_EQ(3, num);
- EXPECT_FALSE(read_success);
- }
- // If we set lookup timeout to lower than querytimeout*retries, we should
- // fail before the full amount of retries
- TEST_F(ASIOLinkTest, forwardLookupTimeout) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
- // Prepare the socket
- sock_ = createTestSocket();
- // Prepare the server
- bool done(true);
- MockServerStop server(*io_service_, &done);
- MessagePtr answer(new Message(Message::RENDER));
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- // Set up the test so that it will retry 5 times, but the lookup
- // timeout will fire after only 3 normal timeouts
- RecursiveQuery query(*dns_service_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 50, 4000, 120, 5);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- query.resolve(question, answer, buffer, &server);
- // Run the test
- io_service_->run();
- int recv_options = setSocketTimeout(sock_, 1, 0);
- // Try to read 5 times, should stop after 3 reads
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 5, &num);
- // The query should fail
- EXPECT_FALSE(done);
- EXPECT_EQ(3, num);
- EXPECT_FALSE(read_success);
- }
- // as mentioned above, we need a more better framework for this,
- // in addition to that, this sends out queries into the world
- // (which we should catch somehow and fake replies for)
- // for the skeleton code, it shouldn't be too much of a problem
- // Ok so even we don't all have access to the DNS world right now,
- // so disabling these tests too.
- TEST_F(ASIOLinkTest, DISABLED_recursiveSendOk) {
- setDNSService(true, false);
- bool done;
-
- MockServerStop server(*io_service_, &done);
- vector<pair<string, uint16_t> > empty_vector;
- RecursiveQuery rq(*dns_service_, empty_vector, empty_vector, 10000, 0);
- Question q(Name("www.isc.org"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- rq.resolve(q, answer, buffer, &server);
- io_service_->run();
- // Check that the answer we got matches the one we wanted
- EXPECT_EQ(Rcode::NOERROR(), answer->getRcode());
- ASSERT_EQ(1, answer->getRRCount(Message::SECTION_ANSWER));
- RRsetPtr a = *answer->beginSection(Message::SECTION_ANSWER);
- EXPECT_EQ(q.getName(), a->getName());
- EXPECT_EQ(q.getType(), a->getType());
- EXPECT_EQ(q.getClass(), a->getClass());
- EXPECT_EQ(1, a->getRdataCount());
- }
- // see comments at previous test
- TEST_F(ASIOLinkTest, DISABLED_recursiveSendNXDOMAIN) {
- setDNSService(true, false);
- bool done;
-
- MockServerStop server(*io_service_, &done);
- vector<pair<string, uint16_t> > empty_vector;
- RecursiveQuery rq(*dns_service_, empty_vector, empty_vector, 10000, 0);
- Question q(Name("wwwdoesnotexist.isc.org"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- rq.resolve(q, answer, buffer, &server);
- io_service_->run();
- // Check that the answer we got matches the one we wanted
- EXPECT_EQ(Rcode::NXDOMAIN(), answer->getRcode());
- EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
- }
- // This fixture is for testing IntervalTimer. Some callback functors are
- // registered as callback function of the timer to test if they are called
- // or not.
- class IntervalTimerTest : public ::testing::Test {
- protected:
- IntervalTimerTest() : io_service_() {}
- ~IntervalTimerTest() {}
- class TimerCallBack : public std::unary_function<void, void> {
- public:
- TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
- void operator()() const {
- test_obj_->timer_called_ = true;
- test_obj_->io_service_.stop();
- return;
- }
- private:
- IntervalTimerTest* test_obj_;
- };
- class TimerCallBackCounter : public std::unary_function<void, void> {
- public:
- TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
- counter_ = 0;
- }
- void operator()() {
- ++counter_;
- return;
- }
- int counter_;
- private:
- IntervalTimerTest* test_obj_;
- };
- class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
- public:
- TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
- IntervalTimer* timer,
- TimerCallBackCounter& counter)
- : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
- {}
- void operator()() {
- ++count_;
- if (count_ == 1) {
- // First time of call back.
- // Store the value of counter_.counter_.
- prev_counter_ = counter_.counter_;
- delete timer_;
- } else if (count_ == 2) {
- // Second time of call back.
- // Stop io_service to stop all timers.
- test_obj_->io_service_.stop();
- // Compare the value of counter_.counter_ with stored one.
- // If TimerCallBackCounter was not called (expected behavior),
- // they are same.
- if (counter_.counter_ == prev_counter_) {
- test_obj_->timer_cancel_success_ = true;
- }
- }
- return;
- }
- private:
- IntervalTimerTest* test_obj_;
- IntervalTimer* timer_;
- TimerCallBackCounter& counter_;
- int count_;
- int prev_counter_;
- };
- class TimerCallBackCanceller {
- public:
- TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
- counter_(counter), itimer_(itimer)
- {}
- void operator()() {
- ++counter_;
- itimer_.cancel();
- }
- private:
- unsigned int& counter_;
- IntervalTimer& itimer_;
- };
- class TimerCallBackOverwriter : public std::unary_function<void, void> {
- public:
- TimerCallBackOverwriter(IntervalTimerTest* test_obj,
- IntervalTimer& timer)
- : test_obj_(test_obj), timer_(timer), count_(0)
- {}
- void operator()() {
- ++count_;
- if (count_ == 1) {
- // First time of call back.
- // Call setup() to update callback function to TimerCallBack.
- test_obj_->timer_called_ = false;
- timer_.setup(TimerCallBack(test_obj_), 100);
- } else if (count_ == 2) {
- // Second time of call back.
- // If it reaches here, re-setup() is failed (unexpected).
- // We should stop here.
- test_obj_->io_service_.stop();
- }
- return;
- }
- private:
- IntervalTimerTest* test_obj_;
- IntervalTimer& timer_;
- int count_;
- };
- protected:
- IOService io_service_;
- bool timer_called_;
- bool timer_cancel_success_;
- };
- TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
- // Create asio_link::IntervalTimer and setup.
- IntervalTimer itimer(io_service_);
- // expect throw if call back function is empty
- EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
- isc::InvalidParameter);
- // expect throw if interval is not greater than 0
- EXPECT_THROW(itimer.setup(TimerCallBack(this), 0), isc::BadValue);
- EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
- }
- TEST_F(IntervalTimerTest, startIntervalTimer) {
- // Create asio_link::IntervalTimer and setup.
- // Then run IOService and test if the callback function is called.
- IntervalTimer itimer(io_service_);
- timer_called_ = false;
- // store start time
- boost::posix_time::ptime start;
- start = boost::posix_time::microsec_clock::universal_time();
- // setup timer
- itimer.setup(TimerCallBack(this), 100);
- EXPECT_EQ(100, itimer.getInterval());
- io_service_.run();
- // reaches here after timer expired
- // delta: difference between elapsed time and 100 milliseconds.
- boost::posix_time::time_duration delta =
- (boost::posix_time::microsec_clock::universal_time() - start)
- - boost::posix_time::millisec(100);
- if (delta.is_negative()) {
- delta.invert_sign();
- }
- // expect TimerCallBack is called; timer_called_ is true
- EXPECT_TRUE(timer_called_);
- // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
- EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
- }
- TEST_F(IntervalTimerTest, destructIntervalTimer) {
- // This code isn't exception safe, but we'd rather keep the code
- // simpler and more readable as this is only for tests and if it throws
- // the program would immediately terminate anyway.
- // The call back function will not be called after the timer is
- // destroyed.
- //
- // There are two timers:
- // itimer_counter (A)
- // (Calls TimerCallBackCounter)
- // - increments internal counter in callback function
- // itimer_canceller (B)
- // (Calls TimerCallBackCancelDeleter)
- // - first time of callback, it stores the counter value of
- // callback_canceller and destroys itimer_counter
- // - second time of callback, it compares the counter value of
- // callback_canceller with stored value
- // if they are same the timer was not called; expected result
- // if they are different the timer was called after destroyed
- //
- // 0 100 200 300 400 500 600 (ms)
- // (A) i--------+----x
- // ^
- // |destroy itimer_counter
- // (B) i-------------+--------------s
- // ^stop io_service
- // and check if itimer_counter have been
- // stopped
- // itimer_counter will be deleted in TimerCallBackCancelDeleter
- IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
- IntervalTimer itimer_canceller(io_service_);
- timer_cancel_success_ = false;
- TimerCallBackCounter callback_canceller(this);
- itimer_counter->setup(callback_canceller, 200);
- itimer_canceller.setup(
- TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
- 300);
- io_service_.run();
- EXPECT_TRUE(timer_cancel_success_);
- }
- TEST_F(IntervalTimerTest, cancel) {
- // Similar to destructIntervalTimer test, but the first timer explicitly
- // cancels itself on first callback.
- IntervalTimer itimer_counter(io_service_);
- IntervalTimer itimer_watcher(io_service_);
- unsigned int counter = 0;
- itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
- itimer_watcher.setup(TimerCallBack(this), 200);
- io_service_.run();
- EXPECT_EQ(1, counter);
- EXPECT_EQ(0, itimer_counter.getInterval());
- // canceling an already canceled timer shouldn't cause any surprise.
- EXPECT_NO_THROW(itimer_counter.cancel());
- }
- TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
- // Calling setup() multiple times updates call back function and interval.
- //
- // There are two timers:
- // itimer (A)
- // (Calls TimerCallBackCounter / TimerCallBack)
- // - increments internal counter in callback function
- // (TimerCallBackCounter)
- // interval: 300 milliseconds
- // - io_service_.stop() (TimerCallBack)
- // interval: 100 milliseconds
- // itimer_overwriter (B)
- // (Calls TimerCallBackOverwriter)
- // - first time of callback, it calls setup() to change call back
- // function to TimerCallBack and interval of itimer to 100
- // milliseconds
- // after 300 + 100 milliseconds from the beginning of this test,
- // TimerCallBack() will be called and io_service_ stops.
- // - second time of callback, it means the test fails.
- //
- // 0 100 200 300 400 500 600 700 800 (ms)
- // (A) i-------------+----C----s
- // ^ ^stop io_service
- // |change call back function
- // (B) i------------------+-------------------S
- // ^(stop io_service on fail)
- //
- IntervalTimer itimer(io_service_);
- IntervalTimer itimer_overwriter(io_service_);
- // store start time
- boost::posix_time::ptime start;
- start = boost::posix_time::microsec_clock::universal_time();
- itimer.setup(TimerCallBackCounter(this), 300);
- itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
- io_service_.run();
- // reaches here after timer expired
- // if interval is updated, it takes
- // 400 milliseconds for TimerCallBackOverwriter
- // + 100 milliseconds for TimerCallBack (stop)
- // = 500 milliseconds.
- // otherwise (test fails), it takes
- // 400 milliseconds for TimerCallBackOverwriter
- // + 400 milliseconds for TimerCallBackOverwriter (stop)
- // = 800 milliseconds.
- // delta: difference between elapsed time and 400 + 100 milliseconds
- boost::posix_time::time_duration delta =
- (boost::posix_time::microsec_clock::universal_time() - start)
- - boost::posix_time::millisec(400 + 100);
- if (delta.is_negative()) {
- delta.invert_sign();
- }
- // expect callback function is updated: TimerCallBack is called
- EXPECT_TRUE(timer_called_);
- // expect interval is updated
- EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
- }
- }
|