diff options
Diffstat (limited to 'src/lib/dns/tests/rrcollator_unittest.cc')
-rw-r--r-- | src/lib/dns/tests/rrcollator_unittest.cc | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/lib/dns/tests/rrcollator_unittest.cc b/src/lib/dns/tests/rrcollator_unittest.cc new file mode 100644 index 0000000..4247911 --- /dev/null +++ b/src/lib/dns/tests/rrcollator_unittest.cc @@ -0,0 +1,208 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/name.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> +#include <dns/rrclass.h> +#include <dns/rrcollator.h> +#include <dns/rdata.h> +#include <dns/rrset.h> +#include <dns/rrttl.h> + +#include <gtest/gtest.h> + +#include <functional> +#include <sstream> +#include <vector> + +using std::vector; +using namespace isc::dns; +using namespace isc::dns::rdata; +namespace ph = std::placeholders; + +namespace { + +typedef RRCollator::AddRRsetCallback AddRRsetCallback; + +void +addRRset(const RRsetPtr& rrset, vector<ConstRRsetPtr>* to_append, + const bool* do_throw) { + if (*do_throw) { + isc_throw(isc::Unexpected, "faked failure"); + } + to_append->push_back(rrset); +} + +class RRCollatorTest : public ::testing::Test { +protected: + RRCollatorTest() : + origin_("example.com"), rrclass_(RRClass::IN()), rrttl_(3600), + throw_from_callback_(false), + collator_(std::bind(addRRset, ph::_1, &rrsets_, &throw_from_callback_)), + rr_callback_(collator_.getCallback()), + a_rdata1_(createRdata(RRType::A(), rrclass_, "192.0.2.1")), + a_rdata2_(createRdata(RRType::A(), rrclass_, "192.0.2.2")), + txt_rdata_(createRdata(RRType::TXT(), rrclass_, "test")), + sig_rdata1_(createRdata(RRType::RRSIG(), rrclass_, + "A 5 3 3600 20000101000000 20000201000000 " + "12345 example.com. FAKE")), + sig_rdata2_(createRdata(RRType::RRSIG(), rrclass_, + "NS 5 3 3600 20000101000000 20000201000000 " + "12345 example.com. FAKE")) + {} + + void checkRRset(const Name& expected_name, const RRClass& expected_class, + const RRType& expected_type, const RRTTL& expected_ttl, + const vector<ConstRdataPtr>& expected_rdatas) { + SCOPED_TRACE(expected_name.toText(true) + "/" + + expected_class.toText() + "/" + expected_type.toText()); + + // This test always clears rrsets_ to confirm RRsets are added + // one-by-one + ASSERT_EQ(1, rrsets_.size()); + + ConstRRsetPtr actual = rrsets_[0]; + EXPECT_EQ(expected_name, actual->getName()); + EXPECT_EQ(expected_class, actual->getClass()); + EXPECT_EQ(expected_type, actual->getType()); + EXPECT_EQ(expected_ttl, actual->getTTL()); + ASSERT_EQ(expected_rdatas.size(), actual->getRdataCount()); + vector<ConstRdataPtr>::const_iterator it = expected_rdatas.begin(); + for (RdataIteratorPtr rit = actual->getRdataIterator(); + !rit->isLast(); + rit->next()) { + EXPECT_EQ(0, rit->getCurrent().compare(**it)); + ++it; + } + + rrsets_.clear(); + } + + const Name origin_; + const RRClass rrclass_; + const RRTTL rrttl_; + vector<ConstRRsetPtr> rrsets_; + bool throw_from_callback_; + RRCollator collator_; + AddRRCallback rr_callback_; + const RdataPtr a_rdata1_, a_rdata2_, txt_rdata_, sig_rdata1_, sig_rdata2_; + vector<ConstRdataPtr> rdatas_; // placeholder for expected data +}; + +TEST_F(RRCollatorTest, basicCases) { + // Add two RRs belonging to the same RRset. These will be buffered. + rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata1_); + EXPECT_TRUE(rrsets_.empty()); // not yet given as an RRset + rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata2_); + EXPECT_TRUE(rrsets_.empty()); // still not given + + // Add another type of RR. This completes the construction of the A RRset, + // which will be given via the callback. + rr_callback_(origin_, rrclass_, RRType::TXT(), rrttl_, txt_rdata_); + rdatas_.push_back(a_rdata1_); + rdatas_.push_back(a_rdata2_); + checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_); + + // Add the same type of RR but of different name. This should make another + // callback for the previous TXT RR. + rr_callback_(Name("txt.example.com"), rrclass_, RRType::TXT(), rrttl_, + txt_rdata_); + rdatas_.clear(); + rdatas_.push_back(txt_rdata_); + checkRRset(origin_, rrclass_, RRType::TXT(), rrttl_, rdatas_); + + // Add the same type and name of RR but of different class (rare case + // in practice) + rr_callback_(Name("txt.example.com"), RRClass::CH(), RRType::TXT(), rrttl_, + txt_rdata_); + rdatas_.clear(); + rdatas_.push_back(txt_rdata_); + checkRRset(Name("txt.example.com"), rrclass_, RRType::TXT(), rrttl_, + rdatas_); + + // Tell the collator we are done, then we'll see the last RR as an RRset. + collator_.flush(); + checkRRset(Name("txt.example.com"), RRClass::CH(), RRType::TXT(), rrttl_, + rdatas_); + + // Redundant flush() will be no-op. + collator_.flush(); + EXPECT_TRUE(rrsets_.empty()); +} + +TEST_F(RRCollatorTest, minTTLFirst) { + // RRs of the same RRset but has different TTLs. The first RR has + // the smaller TTL, which should be used for the TTL of the RRset. + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(10), a_rdata1_); + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(20), a_rdata2_); + rdatas_.push_back(a_rdata1_); + rdatas_.push_back(a_rdata2_); + collator_.flush(); + checkRRset(origin_, rrclass_, RRType::A(), RRTTL(10), rdatas_); +} + +TEST_F(RRCollatorTest, maxTTLFirst) { + // RRs of the same RRset but has different TTLs. The second RR has + // the smaller TTL, which should be used for the TTL of the RRset. + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(20), a_rdata1_); + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(10), a_rdata2_); + rdatas_.push_back(a_rdata1_); + rdatas_.push_back(a_rdata2_); + collator_.flush(); + checkRRset(origin_, rrclass_, RRType::A(), RRTTL(10), rdatas_); +} + +TEST_F(RRCollatorTest, addRRSIGs) { + // RRSIG is special; they are also distinguished by their covered types. + rr_callback_(origin_, rrclass_, RRType::RRSIG(), rrttl_, sig_rdata1_); + rr_callback_(origin_, rrclass_, RRType::RRSIG(), rrttl_, sig_rdata2_); + + rdatas_.push_back(sig_rdata1_); + checkRRset(origin_, rrclass_, RRType::RRSIG(), rrttl_, rdatas_); +} + +TEST_F(RRCollatorTest, emptyFlush) { + collator_.flush(); + EXPECT_TRUE(rrsets_.empty()); +} + +TEST_F(RRCollatorTest, throwFromCallback) { + // Adding an A RR + rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata1_); + + // Adding a TXT RR, which would trigger RRset callback, but in this test + // it throws. The added TXT RR will be effectively lost. + throw_from_callback_ = true; + EXPECT_THROW(rr_callback_(origin_, rrclass_, RRType::TXT(), rrttl_, + txt_rdata_), isc::Unexpected); + + // We'll only see the A RR. + throw_from_callback_ = false; + collator_.flush(); + rdatas_.push_back(a_rdata1_); + checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_); +} + +TEST_F(RRCollatorTest, withMasterLoader) { + // Test a simple case with MasterLoader. There shouldn't be anything + // special, but that's the mainly intended usage of the collator, so we + // check it explicitly. + std::istringstream ss("example.com. 3600 IN A 192.0.2.1\n"); + MasterLoader loader(ss, origin_, rrclass_, + MasterLoaderCallbacks::getNullCallbacks(), + collator_.getCallback()); + loader.load(); + collator_.flush(); + rdatas_.push_back(a_rdata1_); + checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_); +} + +} |