// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2011 CZ NIC // // 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 <exceptions/exceptions.h> #include <dns/name.h> #include <dns/rdata.h> #include <dns/rdataclass.h> #include <dns/rrclass.h> #include <dns/rrsetlist.h> #include <dns/rrttl.h> #include <dns/masterload.h> #include <datasrc/memory_datasrc.h> #include <gtest/gtest.h> using namespace isc::dns; using namespace isc::dns::rdata; using namespace isc::datasrc; namespace { // Commonly used result codes (Who should write the prefix all the time) using result::SUCCESS; using result::EXIST; class MemoryDataSrcTest : public ::testing::Test { protected: MemoryDataSrcTest() : rrclass(RRClass::IN()) {} RRClass rrclass; MemoryDataSrc memory_datasrc; }; TEST_F(MemoryDataSrcTest, add_find_Zone) { // test add zone // Bogus zone (NULL) EXPECT_THROW(memory_datasrc.addZone(ZonePtr()), isc::InvalidParameter); // add zones with different names one by one EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("a"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::CH(), Name("b"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("c"))))); // add zones with the same name suffix EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::CH(), Name("x.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::CH(), Name("o.w.y.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::CH(), Name("p.w.y.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("q.w.y.d.e.f"))))); // add super zone and its subzone EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::CH(), Name("g.h"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("i.g.h"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("z.d.e.f"))))); EXPECT_EQ(result::SUCCESS, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("j.z.d.e.f"))))); // different zone class isn't allowed. EXPECT_EQ(result::EXIST, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::CH(), Name("q.w.y.d.e.f"))))); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, memory_datasrc.addZone( ZonePtr(new MemoryZone(RRClass::IN(), Name("Q.W.Y.d.E.f"))))); // test find zone EXPECT_EQ(result::SUCCESS, memory_datasrc.findZone(Name("a")).code); EXPECT_EQ(Name("a"), memory_datasrc.findZone(Name("a")).zone->getOrigin()); EXPECT_EQ(result::SUCCESS, memory_datasrc.findZone(Name("j.z.d.e.f")).code); EXPECT_EQ(Name("j.z.d.e.f"), memory_datasrc.findZone(Name("j.z.d.e.f")).zone->getOrigin()); // NOTFOUND EXPECT_EQ(result::NOTFOUND, memory_datasrc.findZone(Name("d.e.f")).code); EXPECT_EQ(ConstZonePtr(), memory_datasrc.findZone(Name("d.e.f")).zone); EXPECT_EQ(result::NOTFOUND, memory_datasrc.findZone(Name("w.y.d.e.f")).code); EXPECT_EQ(ConstZonePtr(), memory_datasrc.findZone(Name("w.y.d.e.f")).zone); // there's no exact match. the result should be the longest match, // and the code should be PARTIALMATCH. EXPECT_EQ(result::PARTIALMATCH, memory_datasrc.findZone(Name("j.g.h")).code); EXPECT_EQ(Name("g.h"), memory_datasrc.findZone(Name("g.h")).zone->getOrigin()); EXPECT_EQ(result::PARTIALMATCH, memory_datasrc.findZone(Name("z.i.g.h")).code); EXPECT_EQ(Name("i.g.h"), memory_datasrc.findZone(Name("z.i.g.h")).zone->getOrigin()); } TEST_F(MemoryDataSrcTest, getZoneCount) { EXPECT_EQ(0, memory_datasrc.getZoneCount()); memory_datasrc.addZone( ZonePtr(new MemoryZone(rrclass, Name("example.com")))); EXPECT_EQ(1, memory_datasrc.getZoneCount()); // duplicate add. counter shouldn't change memory_datasrc.addZone( ZonePtr(new MemoryZone(rrclass, Name("example.com")))); EXPECT_EQ(1, memory_datasrc.getZoneCount()); // add one more memory_datasrc.addZone( ZonePtr(new MemoryZone(rrclass, Name("example.org")))); EXPECT_EQ(2, memory_datasrc.getZoneCount()); } /// \brief Test fixture for the MemoryZone class class MemoryZoneTest : public ::testing::Test { public: MemoryZoneTest() : class_(RRClass::IN()), origin_("example.org"), ns_name_("ns.example.org"), cname_name_("cname.example.org"), dname_name_("dname.example.org"), child_ns_name_("child.example.org"), child_glue_name_("ns.child.example.org"), grandchild_ns_name_("grand.child.example.org"), grandchild_glue_name_("ns.grand.child.example.org"), child_dname_name_("dname.child.example.org"), zone_(class_, origin_), rr_out_(new RRset(Name("example.com"), class_, RRType::A(), RRTTL(300))), rr_ns_(new RRset(origin_, class_, RRType::NS(), RRTTL(300))), rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))), rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))), rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300))), rr_cname_(new RRset(cname_name_, class_, RRType::CNAME(), RRTTL(300))), rr_cname_a_(new RRset(cname_name_, class_, RRType::A(), RRTTL(300))), rr_dname_(new RRset(dname_name_, class_, RRType::DNAME(), RRTTL(300))), rr_dname_a_(new RRset(dname_name_, class_, RRType::A(), RRTTL(300))), rr_dname_ns_(new RRset(dname_name_, class_, RRType::NS(), RRTTL(300))), rr_dname_apex_(new RRset(origin_, class_, RRType::DNAME(), RRTTL(300))), rr_child_ns_(new RRset(child_ns_name_, class_, RRType::NS(), RRTTL(300))), rr_child_glue_(new RRset(child_glue_name_, class_, RRType::A(), RRTTL(300))), rr_grandchild_ns_(new RRset(grandchild_ns_name_, class_, RRType::NS(), RRTTL(300))), rr_grandchild_glue_(new RRset(grandchild_glue_name_, class_, RRType::AAAA(), RRTTL(300))), rr_child_dname_(new RRset(child_dname_name_, class_, RRType::DNAME(), RRTTL(300))) { } // Some data to test with const RRClass class_; const Name origin_, ns_name_, cname_name_, dname_name_, child_ns_name_, child_glue_name_, grandchild_ns_name_, grandchild_glue_name_, child_dname_name_; // The zone to torture by tests MemoryZone zone_; /* * Some RRsets to put inside the zone. * They are empty, but the MemoryZone does not have a reason to look * inside anyway. We will check it finds them and does not change * the pointer. */ ConstRRsetPtr // Out of zone RRset rr_out_, // NS of example.org rr_ns_, // A of ns.example.org rr_ns_a_, // AAAA of ns.example.org rr_ns_aaaa_, // A of example.org rr_a_; RRsetPtr rr_cname_; // CNAME in example.org (RDATA will be added) ConstRRsetPtr rr_cname_a_; // for mixed CNAME + A case RRsetPtr rr_dname_; // DNAME in example.org (RDATA will be added) ConstRRsetPtr rr_dname_a_; // for mixed DNAME + A case ConstRRsetPtr rr_dname_ns_; // for mixed DNAME + NS case ConstRRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex ConstRRsetPtr rr_child_ns_; // NS of a child domain (for delegation) ConstRRsetPtr rr_child_glue_; // glue RR of the child domain ConstRRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual) ConstRRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut ConstRRsetPtr rr_child_dname_; // A DNAME under NS /** * \brief Test one find query to the zone. * * Asks a query to the zone and checks it does not throw and returns * expected results. It returns nothing, it just signals failures * to GTEST. * * \param name The name to ask for. * \param rrtype The RRType to ask of. * \param result The expected code of the result. * \param check_answer Should a check against equality of the answer be * done? * \param answer The expected rrset, if any should be returned. * \param zone Check different MemoryZone object than zone_ (if NULL, * uses zone_) */ void findTest(const Name& name, const RRType& rrtype, Zone::Result result, bool check_answer = true, const ConstRRsetPtr& answer = ConstRRsetPtr(), RRsetList* target = NULL, MemoryZone* zone = NULL, Zone::FindOptions options = Zone::FIND_DEFAULT) { if (!zone) { zone = &zone_; } // The whole block is inside, because we need to check the result and // we can't assign to FindResult EXPECT_NO_THROW({ Zone::FindResult find_result(zone->find(name, rrtype, target, options)); // Check it returns correct answers EXPECT_EQ(result, find_result.code); if (check_answer) { EXPECT_EQ(answer, find_result.rrset); } }); } }; /** * \brief Test MemoryZone::MemoryZone constructor. * * Takes the created zone and checks its properties they are the same * as passed parameters. */ TEST_F(MemoryZoneTest, constructor) { ASSERT_EQ(class_, zone_.getClass()); ASSERT_EQ(origin_, zone_.getOrigin()); } /** * \brief Test adding. * * We test that it throws at the correct moments and the correct exceptions. * And we test the return value. */ TEST_F(MemoryZoneTest, add) { // This one does not belong to this zone EXPECT_THROW(zone_.add(rr_out_), MemoryZone::OutOfZone); // Test null pointer EXPECT_THROW(zone_.add(ConstRRsetPtr()), MemoryZone::NullRRset); // Now put all the data we have there. It should throw nothing EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_))); // Try putting there something twice, it should be rejected EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_a_))); } TEST_F(MemoryZoneTest, addMultipleCNAMEs) { rr_cname_->addRdata(generic::CNAME("canonical1.example.org.")); rr_cname_->addRdata(generic::CNAME("canonical2.example.org.")); EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError); } TEST_F(MemoryZoneTest, addCNAMEThenOther) { rr_cname_->addRdata(generic::CNAME("canonical.example.org.")); EXPECT_EQ(SUCCESS, zone_.add(rr_cname_)); EXPECT_THROW(zone_.add(rr_cname_a_), MemoryZone::AddError); } TEST_F(MemoryZoneTest, addOtherThenCNAME) { rr_cname_->addRdata(generic::CNAME("canonical.example.org.")); EXPECT_EQ(SUCCESS, zone_.add(rr_cname_a_)); EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError); } TEST_F(MemoryZoneTest, findCNAME) { // install CNAME RR rr_cname_->addRdata(generic::CNAME("canonical.example.org.")); EXPECT_EQ(SUCCESS, zone_.add(rr_cname_)); // Find A RR of the same. Should match the CNAME findTest(cname_name_, RRType::NS(), Zone::CNAME, true, rr_cname_); // Find the CNAME itself. Should result in normal SUCCESS findTest(cname_name_, RRType::CNAME(), Zone::SUCCESS, true, rr_cname_); } TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) { // There's nothing special when we find a CNAME under a zone cut // (with FIND_GLUE_OK). The behavior is different from BIND 9, // so we test this case explicitly. EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)); RRsetPtr rr_cname_under_cut_(new RRset(Name("cname.child.example.org"), class_, RRType::CNAME(), RRTTL(300))); EXPECT_EQ(SUCCESS, zone_.add(rr_cname_under_cut_)); findTest(Name("cname.child.example.org"), RRType::AAAA(), Zone::CNAME, true, rr_cname_under_cut_, NULL, NULL, Zone::FIND_GLUE_OK); } // Two DNAMEs at single domain are disallowed by RFC 2672, section 3) // Having a CNAME there is disallowed too, but it is tested by // addOtherThenCNAME and addCNAMEThenOther. TEST_F(MemoryZoneTest, addMultipleDNAMEs) { rr_dname_->addRdata(generic::DNAME("dname1.example.org.")); rr_dname_->addRdata(generic::DNAME("dname2.example.org.")); EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError); } /* * These two tests ensure that we can't have DNAME and NS at the same * node with the exception of the apex of zone (forbidden by RFC 2672) */ TEST_F(MemoryZoneTest, addDNAMEThenNS) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_))); EXPECT_THROW(zone_.add(rr_dname_ns_), MemoryZone::AddError); } TEST_F(MemoryZoneTest, addNSThenDNAME) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_ns_))); EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError); } // It is allowed to have NS and DNAME at apex TEST_F(MemoryZoneTest, DNAMEAndNSAtApex) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_apex_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); // The NS should be possible to be found, below should be DNAME, not // delegation findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); findTest(child_ns_name_, RRType::A(), Zone::DNAME, true, rr_dname_apex_); } TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_apex_))); } // TODO: Test (and implement) adding data under DNAME. That is forbidden by // 2672 as well. // Search under a DNAME record. It should return the DNAME TEST_F(MemoryZoneTest, findBelowDNAME) { rr_dname_->addRdata(generic::DNAME("target.example.org.")); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_))); findTest(Name("below.dname.example.org"), RRType::A(), Zone::DNAME, true, rr_dname_); } // Search at the domain with DNAME. It should act as DNAME isn't there, DNAME // influences only the data below (see RFC 2672, section 3) TEST_F(MemoryZoneTest, findAtDNAME) { rr_dname_->addRdata(generic::DNAME("target.example.org.")); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_a_))); findTest(dname_name_, RRType::A(), Zone::SUCCESS, true, rr_dname_a_); findTest(dname_name_, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_); findTest(dname_name_, RRType::TXT(), Zone::NXRRSET, true); } // Try searching something that is both under NS and DNAME, without and with // GLUE_OK mode (it should stop at the NS and DNAME respectively). TEST_F(MemoryZoneTest, DNAMEUnderNS) { zone_.add(rr_child_ns_); zone_.add(rr_child_dname_); Name lowName("below.dname.child.example.org."); findTest(lowName, RRType::A(), Zone::DELEGATION, true, rr_child_ns_); findTest(lowName, RRType::A(), Zone::DNAME, true, rr_child_dname_, NULL, NULL, Zone::FIND_GLUE_OK); } // Test adding child zones and zone cut handling TEST_F(MemoryZoneTest, delegationNS) { // add in-zone data EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); // install a zone cut EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_))); // below the zone cut findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION, true, rr_child_ns_); // at the zone cut findTest(Name("child.example.org"), RRType::A(), Zone::DELEGATION, true, rr_child_ns_); findTest(Name("child.example.org"), RRType::NS(), Zone::DELEGATION, true, rr_child_ns_); // finding NS for the apex (origin) node. This must not be confused // with delegation due to the existence of an NS RR. findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); // unusual case of "nested delegation": the highest cut should be used. EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_))); findTest(Name("www.grand.child.example.org"), RRType::A(), Zone::DELEGATION, true, rr_child_ns_); // note: !rr_grandchild_ns_ } TEST_F(MemoryZoneTest, findAny) { EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_))); // origin RRsetList origin_rrsets; findTest(origin_, RRType::ANY(), Zone::SUCCESS, true, ConstRRsetPtr(), &origin_rrsets); EXPECT_EQ(2, origin_rrsets.size()); EXPECT_EQ(rr_a_, origin_rrsets.findRRset(RRType::A(), RRClass::IN())); EXPECT_EQ(rr_ns_, origin_rrsets.findRRset(RRType::NS(), RRClass::IN())); // out zone name RRsetList out_rrsets; findTest(Name("example.com"), RRType::ANY(), Zone::NXDOMAIN, true, ConstRRsetPtr(), &out_rrsets); EXPECT_EQ(0, out_rrsets.size()); RRsetList glue_child_rrsets; findTest(child_glue_name_, RRType::ANY(), Zone::SUCCESS, true, ConstRRsetPtr(), &glue_child_rrsets); EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(), RRClass::IN())); EXPECT_EQ(1, glue_child_rrsets.size()); // TODO: test NXRRSET case after rbtree non-terminal logic has // been implemented // add zone cut EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_))); // zone cut RRsetList child_rrsets; findTest(child_ns_name_, RRType::ANY(), Zone::DELEGATION, true, rr_child_ns_, &child_rrsets); EXPECT_EQ(0, child_rrsets.size()); // glue for this zone cut RRsetList new_glue_child_rrsets; findTest(child_glue_name_, RRType::ANY(), Zone::DELEGATION, true, rr_child_ns_, &new_glue_child_rrsets); EXPECT_EQ(0, new_glue_child_rrsets.size()); } TEST_F(MemoryZoneTest, glue) { // install zone data: // a zone cut EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_))); // glue for this cut EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_))); // a nested zone cut (unusual) EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_))); // glue under the deeper zone cut EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_))); // by default glue is hidden due to the zone cut findTest(child_glue_name_, RRType::A(), Zone::DELEGATION, true, rr_child_ns_); // If we do it in the "glue OK" mode, we should find the exact match. findTest(child_glue_name_, RRType::A(), Zone::SUCCESS, true, rr_child_glue_, NULL, NULL, Zone::FIND_GLUE_OK); // glue OK + NXRRSET case findTest(child_glue_name_, RRType::AAAA(), Zone::NXRRSET, true, ConstRRsetPtr(), NULL, NULL, Zone::FIND_GLUE_OK); // glue OK + NXDOMAIN case findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION, true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK); // TODO: // glue name would match a wildcard under a zone cut: wildcard match // shouldn't happen under a cut and result must be PARTIALMATCH // (This case cannot be tested yet) // nested cut case. The glue should be found. findTest(grandchild_glue_name_, RRType::AAAA(), Zone::SUCCESS, true, rr_grandchild_glue_, NULL, NULL, Zone::FIND_GLUE_OK); // A non-existent name in nested cut. This should result in delegation // at the highest zone cut. findTest(Name("www.grand.child.example.org"), RRType::TXT(), Zone::DELEGATION, true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK); } /** * \brief Test searching. * * Check it finds or does not find correctly and does not throw exceptions. * \todo This doesn't do any kind of CNAME and so on. If it isn't * directly there, it just tells it doesn't exist. */ TEST_F(MemoryZoneTest, find) { // Fill some data inside // Now put all the data we have there. It should throw nothing EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_))); EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_))); // These two should be successful findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); findTest(ns_name_, RRType::A(), Zone::SUCCESS, true, rr_ns_a_); // These domain exist but don't have the provided RRType findTest(origin_, RRType::AAAA(), Zone::NXRRSET); findTest(ns_name_, RRType::NS(), Zone::NXRRSET); // These domains don't exist (and one is out of the zone) findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN); findTest(Name("example.net"), RRType::A(), Zone::NXDOMAIN); } TEST_F(MemoryZoneTest, load) { // Put some data inside the zone EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_.add(rr_ns_))); // Loading with different origin should fail EXPECT_THROW(zone_.load(TEST_DATA_DIR "/root.zone"), MasterLoadError); // See the original data is still there, survived the exception findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_); // Create correct zone MemoryZone rootzone(class_, Name(".")); // Try putting something inside EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, rootzone.add(rr_ns_aaaa_))); // Load the zone. It should overwrite/remove the above RRset EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone")); // Now see there are some rrsets (we don't look inside, though) findTest(Name("."), RRType::SOA(), Zone::SUCCESS, false, ConstRRsetPtr(), NULL, &rootzone); findTest(Name("."), RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(), NULL, &rootzone); findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false, ConstRRsetPtr(), NULL, &rootzone); // But this should no longer be here findTest(ns_name_, RRType::AAAA(), Zone::NXDOMAIN, true, ConstRRsetPtr(), NULL, &rootzone); // Try loading zone that is wrong in a different way EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"), MasterLoadError); } TEST_F(MemoryZoneTest, swap) { // build one zone with some data MemoryZone zone1(class_, origin_); EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_)); EXPECT_EQ(result::SUCCESS, zone1.add(rr_ns_aaaa_)); // build another zone of a different RR class with some other data const Name other_origin("version.bind"); ASSERT_NE(origin_, other_origin); // make sure these two are different MemoryZone zone2(RRClass::CH(), other_origin); EXPECT_EQ(result::SUCCESS, zone2.add(RRsetPtr(new RRset(Name("version.bind"), RRClass::CH(), RRType::TXT(), RRTTL(0))))); zone1.swap(zone2); EXPECT_EQ(other_origin, zone1.getOrigin()); EXPECT_EQ(origin_, zone2.getOrigin()); EXPECT_EQ(RRClass::CH(), zone1.getClass()); EXPECT_EQ(RRClass::IN(), zone2.getClass()); // make sure the zone data is swapped, too findTest(origin_, RRType::NS(), Zone::NXDOMAIN, false, ConstRRsetPtr(), NULL, &zone1); findTest(other_origin, RRType::TXT(), Zone::SUCCESS, false, ConstRRsetPtr(), NULL, &zone1); findTest(origin_, RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(), NULL, &zone2); findTest(other_origin, RRType::TXT(), Zone::NXDOMAIN, false, ConstRRsetPtr(), NULL, &zone2); } TEST_F(MemoryZoneTest, getFileName) { // for an empty zone the file name should also be empty. EXPECT_TRUE(zone_.getFileName().empty()); // if loading a zone fails the file name shouldn't be set. EXPECT_THROW(zone_.load(TEST_DATA_DIR "/root.zone"), MasterLoadError); EXPECT_TRUE(zone_.getFileName().empty()); // after a successful load, the specified file name should be set MemoryZone rootzone(class_, Name(".")); EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone")); EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName()); // overriding load, which will fail EXPECT_THROW(rootzone.load(TEST_DATA_DIR "/duplicate_rrset.zone"), MasterLoadError); // the file name should be intact. EXPECT_EQ(TEST_DATA_DIR "/root.zone", rootzone.getFileName()); // After swap, file names should also be swapped. zone_.swap(rootzone); EXPECT_EQ(TEST_DATA_DIR "/root.zone", zone_.getFileName()); EXPECT_TRUE(rootzone.getFileName().empty()); } }