#include <boost/test/unit_test.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/address.hpp>
+#include <boost/bind.hpp>
+
+#include <logfunc.hpp>
#include "host/pingprotocol.h"
#include "dns/hostaddress.h"
#include "dns/dnsmaster.h"
#include "dns/dnscache.h"
#include "dns/resolverbase.h"
+#include "dns/cname.h"
+
+using boost::asio::ip::address;
//------------------------------------------------------------------------------
-// helper functions
+// Global Test fixture (created once for test suite)
//------------------------------------------------------------------------------
-void create_master();
+// constants for master
+address name_server = address::from_string("127.0.0.1");
+int resolved_ip_ttl_threshold = 3;
+int max_address_resolution_attempts = 2;
+std::string cache_file = DnsCache::DoNotUseCacheFile;
+
+// rely on boost that there will only be one instance
+struct GlobalFixture;
+GlobalFixture *global_fixture;
-// TODO: try to run this only once for test suite
-void create_master()
+
+struct GlobalFixture
{
- // only create if it does not exist yet
- DnsMasterItem master = DnsMaster::get_instance();
- if ( !master )
+
+ GlobalFixture()
+ : IoService()
+ , Cache()
+ , Master()
{
- boost::asio::ip::address name_server =
- boost::asio::ip::address::from_string("127.0.0.1");
- int resolved_ip_ttl_threshold = 3;
- int max_address_resolution_attempts = 2;
- std::string cache_file = DO_NOT_USE_CACHE_FILE;
- IoServiceItem io_serv;
-
- // create it
- DnsMaster::create_master(io_serv,
+ BOOST_TEST_MESSAGE("Create global fixture");
+
+ // setup logging so we can see output from out code
+ I2n::Logger::enable_stderr_log( true );
+ I2n::Logger::set_log_level( I2n::Logger::LogLevel::Debug );
+ I2n::Logger::GlobalLogger.info() << "Logging enabled for DnsTest";
+
+ // IoService
+ IoServiceItem io_service_temp( new boost::asio::io_service() );
+ io_service_temp.swap( IoService );
+ io_service_temp.reset();
+
+ // DNS Cache
+ DnsCacheItem cache_temp = DnsCacheItem(
+ new DnsCache(IoService, cache_file) );
+ cache_temp.swap( Cache );
+ cache_temp.reset();
+ fill_cache();
+
+ // create master
+ DnsMaster::create_master(IoService,
name_server,
resolved_ip_ttl_threshold,
max_address_resolution_attempts,
- cache_file);
- master = DnsMaster::get_instance();
-
- // simple checks
- BOOST_CHECK_EQUAL( master->get_resolved_ip_ttl_threshold(),
- resolved_ip_ttl_threshold );
- BOOST_CHECK_EQUAL( master->get_max_address_resolution_attempts(),
- max_address_resolution_attempts );
+ Cache);
+ Master = DnsMaster::get_instance();
+
+ // remember this instance, so we can later access all these variables
+ if (global_fixture == 0)
+ global_fixture = this;
}
-}
+
+ ~GlobalFixture()
+ {
+ BOOST_TEST_MESSAGE("Destructing global fixture");
+ IoService->stop();
+ IoService.reset();
+ Master.reset();
+ }
+
+ void fill_cache()
+ {
+ BOOST_TEST_MESSAGE( "Filling cache..." );
+ {
+ HostAddress ip(address::from_string("192.168.42.1"), 61);
+ HostAddressVec ips;
+ ips.push_back(ip);
+ Cache->update("host1.test", ips);
+ }
+
+ {
+ HostAddress ip1(address::from_string("192.168.42.2"), 92);
+ HostAddress ip2(address::from_string("192.168.42.3"), 93);
+ HostAddressVec ips;
+ ips.push_back(ip1);
+ ips.push_back(ip2);
+ Cache->update("host2_3.test", ips);
+ }
+
+ { // cname.test --> host1.test
+ Cname cname("host1.test", 35);
+ Cache->update("cname.test", cname);
+ }
+
+ { // cname2.test --> cname.test --> host1.test
+ Cname cname("cname.test", 33);
+ Cache->update("cname2.test", cname);
+ }
+
+ { // cname3.test --> cname2.test --> cname.test --> host1.test
+ Cname cname("cname2.test", 37);
+ Cache->update("cname3.test", cname);
+ }
+ BOOST_TEST_MESSAGE( "Done filling cache." );
+ }
+
+ // these variables will not be available in test cases:
+ IoServiceItem IoService;
+ DnsCacheItem Cache;
+ DnsMasterItem Master;
+};
+
+// this causes above fixture to be created only once before tests start and
+// destructed after tests end; however, variables are not accessible in test
+// cases
+BOOST_GLOBAL_FIXTURE( GlobalFixture )
+
+
+// using this as suite-level fixture makes variable Master accessible in all
+// test cases
+struct TestFixture
+{
+ IoServiceItem IoService;
+ DnsCacheItem Cache;
+ DnsMasterItem Master;
+
+ TestFixture()
+ : IoService()
+ , Cache()
+ , Master()
+ {
+ BOOST_TEST_MESSAGE("Create test-level fixture");
+ GlobalFixture *global = global_fixture;
+ IoService = global->IoService;
+ Cache = global->Cache;
+ Master = global->Master;
+ }
+
+ virtual ~TestFixture() {}
+};
//------------------------------------------------------------------------------
// test suite
//------------------------------------------------------------------------------
-BOOST_AUTO_TEST_SUITE( TestDns )
+BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
BOOST_AUTO_TEST_CASE( create_master )
{
- create_master();
+ // simple checks
+ BOOST_CHECK_EQUAL( Master->get_resolved_ip_ttl_threshold(),
+ resolved_ip_ttl_threshold );
+ BOOST_CHECK_EQUAL( Master->get_max_address_resolution_attempts(),
+ max_address_resolution_attempts );
}
-BOOST_AUTO_TEST_CASE( create_v4 )
+//------------------------------------------------------------------------------
+// test Cache
+//------------------------------------------------------------------------------
+BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
+
+BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
{
- create_master();
+ HostAddressVec ips = Cache->get_ips("host1.test");
+ BOOST_CHECK_EQUAL( ips.size(), 1 );
+ HostAddress ip = ips.front();
+ BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
+ BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
+}
+
+
+BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
+{
+ HostAddressVec ips = Cache->get_ips("host2_3.test");
+ BOOST_CHECK_EQUAL( ips.size(), 2 );
+ HostAddress ip = ips[0];
+ BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
+ BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
+ ip = ips[1];
+ BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
+ BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
+}
+BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
+{
+ HostAddressVec ips = Cache->get_ips("cname.test");
+ BOOST_CHECK( ips.empty() );
+
+ Cname cname = Cache->get_cname("cname.test");
+ BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
+ BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
+}
+
+BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
+{
+ // should get IP from host1 but ttl from cname since is smaller
+ HostAddressVec ips = Cache->get_ips_recursive("cname.test");
+ BOOST_CHECK_EQUAL( ips.size(), 1 );
+ HostAddress ip = ips.front();
+ BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
+ BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
+}
+
+BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
+{
+ // should get IP from host1 but ttl from cname2 since is smaller
+ HostAddressVec ips = Cache->get_ips_recursive("cname3.test");
+ BOOST_CHECK_EQUAL( ips.size(), 1 );
+ HostAddress ip = ips.front();
+ BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
+ BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 );
+}
+
+BOOST_AUTO_TEST_CASE( cache_skip_test1 )
+{
+ // build a cname chain where first one is out of date
+ { // skip_chain_first -(120)-> skip_chain_second -(0)-> skip_chain_third
+ // -(120)-> skip_chain_fourth -(60)-> IPs
+ Cname first("skip_chain_second.test", 120);
+ Cache->update("skip_chain_first.test", first);
+ Cname second("skip_chain_third.test", 0);
+ Cache->update("skip_chain_second.test", second);
+ Cname third("skip_chain_fourth.test", 120);
+ Cache->update("skip_chain_third.test", third);
+ HostAddressVec ips;
+ ips.push_back( HostAddress( address::from_string("192.168.42.4"), 60) );
+ Cache->update("skip_chain_fourth.test", ips);
+ }
+
+ // normal recursive call should give nothing since all are outdated
+ bool check_up_to_date = true;
+ HostAddressVec ips = Cache->get_ips_recursive("skip_chain_first.test",
+ check_up_to_date);
+ BOOST_CHECK( ips.empty() );
+
+ // now try to skip
+ std::string first_outdated = Cache->get_first_outdated_cname(
+ "skip_chain_first.test", 5);
+ BOOST_CHECK_EQUAL( first_outdated, "skip_chain_third.test" );
+}
+
+
+BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache
+
+
+// -----------------------------------------------------------------------------
+// test resolver
+// -----------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
+
+void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
+ const bool was_success, const int cname_count);
+
+BOOST_AUTO_TEST_CASE( create_resolver_v4 )
+{
// create resolver
std::string hostname = "www.intra2net.com";
- ResolverItem resolver = DnsMaster::get_instance()
- ->get_resolver_for(hostname, PingProtocol_ICMP);
+ ResolverItem resolver = Master->get_resolver_for(hostname,
+ PingProtocol_ICMP);
BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
BOOST_CHECK( !resolver->is_resolving() );
BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
}
+
+void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
+ const bool was_success, const int cname_count)
+{
+ resolver->cancel_resolve();
+ io_serv->stop();
+ BOOST_TEST_MESSAGE( "Stopped io_service" );
+}
+
+
+BOOST_AUTO_TEST_CASE( do_resolve )
+{
+ // create resolver
+ std::string hostname = "www.intra2net.com";
+ ResolverItem resolver = Master->get_resolver_for(hostname,
+ PingProtocol_ICMP);
+
+ // set callback
+ callback_type callback = boost::bind( resolve_callback, IoService, resolver,
+ _1, _2 );
+ // start resolving
+ resolver->async_resolve(callback);
+ IoService->run();
+ // this will block until io service is stopped in resolve_callback
+
+ // check for result
+ BOOST_CHECK( resolver->have_up_to_date_ip() );
+}
+
+BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver
+
BOOST_AUTO_TEST_SUITE_END()