summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/tests/rrcollator_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dns/tests/rrcollator_unittest.cc')
-rw-r--r--src/lib/dns/tests/rrcollator_unittest.cc208
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_);
+}
+
+}