created and passed first unit tests for DNS; finished recovery from PingScheduler...
[pingcheck] / test / test_dns.cpp
index 8ce9793..3c60bf3 100644 (file)
@@ -26,68 +26,272 @@ on this file might be covered by the GNU General Public License.
 #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() );
 
@@ -102,4 +306,35 @@ BOOST_AUTO_TEST_CASE( create_v4 )
     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()