Browse Source

Merge branch 'trac1605'

Stephen Morris 13 years ago
parent
commit
d908fc0e44

+ 1 - 0
src/lib/datasrc/Makefile.am

@@ -21,6 +21,7 @@ libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
 libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
 libdatasrc_la_SOURCES += query.h query.cc
 libdatasrc_la_SOURCES += cache.h cache.cc
+libdatasrc_la_SOURCES += rbnode_rrset.h
 libdatasrc_la_SOURCES += rbtree.h
 libdatasrc_la_SOURCES += zonetable.h zonetable.cc
 libdatasrc_la_SOURCES += zone.h

+ 9 - 3
src/lib/datasrc/memory_datasrc.cc

@@ -34,6 +34,7 @@
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/rbtree.h>
+#include <datasrc/rbnode_rrset.h>
 #include <datasrc/logger.h>
 #include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
@@ -419,14 +420,19 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * access is without the impl_-> and it will get inlined anyway.
      */
     // Implementation of InMemoryZoneFinder::add
-    result::Result add(const ConstRRsetPtr& rrset, ZoneData& zone_data) {
+    result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data) {
         // Sanitize input.  This will cause an exception to be thrown
         // if the input RRset is empty.
-        addValidation(rrset);
+        addValidation(rawrrset);
 
         // OK, can add the RRset.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
-            arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
+            arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
+
+        // ... although instead of loading the RRset directly, we encapsulate
+        // it within an RBNodeRRset.  This contains additional information that
+        // speeds up queries.
+        ConstRRsetPtr rrset(new internal::RBNodeRRset(rawrrset));
 
         if (rrset->getType() == RRType::NSEC3()) {
             return (addNSEC3(rrset, zone_data));

+ 209 - 0
src/lib/datasrc/rbnode_rrset.h

@@ -0,0 +1,209 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// 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.
+
+#ifndef __RBNODE_RRSET_H
+#define __RBNODE_RRSET_H
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+#include <util/buffer.h>
+
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace internal {
+
+/// \brief Special RRset for optimizing memory datasource requirement
+///
+/// To speed up the performance of the in-memory data source, at load time
+/// associate relevant "additional section" data with each RRset in the
+/// data source.
+///
+/// This class, derived from AbstractRRset, holds a "const" pointer to the
+/// underlying RRset object.  All calls to methods on the class are passed to
+/// the underlying object.  However, there are some restrictions:
+///
+/// - Calls to methods that change attributes of the underlying RRset (such as
+///   TTL or Name) cause an exception to be thrown.  The in-memory data source
+///   does not allow modification of these attributes.  In theory, it is a bad
+///   practice in that it doesn't preserve the assumed behavior of the base
+///   class.  In practice, however, it should be acceptable because this
+///   class is effectively hidden from applications and will only be given
+///   to them as a const pointer to the base class via find() variants.
+///   So the application cannot call non const methods anyway unless it
+///   intentionally breaks the constness.
+///
+/// - Calls that add the pointer to the associated RRSIG to the RRset are
+///   allowed (even though the pointer is to a "const" RRset).  The reason here
+///   is that RRSIGs are added to the in-memory data source after the
+///   RBNodeRRset objects have been created.  Thus there has to be the
+///   capability of modifying this information.
+///
+/// The class is not derived from RRset itself to simplify coding: part of the
+/// loading of the memory data source is handled in the BIND 10 "libdns++"
+/// code, which creates RRsets and passes them to the data source code.  This
+/// does not have to be altered if encapsulation, rather than inheritance, is
+/// used.
+///
+/// \note This class is exposed in this separate header file so that test code
+/// can refer to its definition, and only for that purpose.  Otherwise this is
+/// essentially a private class of the in-memory data source implementation,
+/// and an application shouldn't directly refer to this class.
+/// 
+// Note: non-Doxygen-documented methods are documented in the base class.
+
+class RBNodeRRset : public isc::dns::AbstractRRset {
+
+private:
+    // Note: The copy constructor and the assignment operator are intentionally
+    // defined as private as we would normally not duplicate a RBNodeRRset.
+    // (We use the "private" method instead of inheriting from
+    // boost::noncopyable so as to avoid multiple inheritance.)
+    RBNodeRRset(const RBNodeRRset& source);
+    RBNodeRRset& operator=(const RBNodeRRset& source);
+
+public:
+    /// \brief Usual Constructor
+    ///
+    /// Creates an RBNodeRRset from the pointer to the RRset passed to it.
+    ///
+    /// \param rrset Pointer to underlying RRset encapsulated by this object.
+    explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset) : rrset_(rrset)
+    {}
+
+    /// \brief Destructor
+    virtual ~RBNodeRRset() {}
+
+    // Getter and Setter Methods
+    //
+    // The getter methods pass the call through to the underlying RRset.  The
+    // setter methods thrown an exception - this specialisation of the RRset
+    // object does not expect the underlying RRset to be modified.
+
+    virtual unsigned int getRdataCount() const {
+        return (rrset_->getRdataCount());
+    }
+
+    virtual const isc::dns::Name& getName() const {
+        return (rrset_->getName());
+    }
+
+    virtual const isc::dns::RRClass& getClass() const {
+        return (rrset_->getClass());
+    }
+
+    virtual const isc::dns::RRType& getType() const {
+        return (rrset_->getType());
+    }
+
+    virtual const isc::dns::RRTTL& getTTL() const {
+        return (rrset_->getTTL());
+    }
+
+    virtual void setName(const isc::dns::Name&) {
+        isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
+    }
+
+    virtual void setTTL(const isc::dns::RRTTL&) {
+        isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
+    }
+
+    virtual std::string toText() const {
+        return (rrset_->toText());
+    }
+
+    virtual unsigned int toWire(
+            isc::dns::AbstractMessageRenderer& renderer) const {
+        return (rrset_->toWire(renderer));
+    }
+
+    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const {
+        return (rrset_->toWire(buffer));
+    }
+
+    virtual void addRdata(isc::dns::rdata::ConstRdataPtr) {
+        isc_throw(isc::NotImplemented,
+                  "RBNodeRRset::addRdata() not supported");
+    }
+
+    virtual void addRdata(const isc::dns::rdata::Rdata&) {
+        isc_throw(isc::NotImplemented,
+                  "RBNodeRRset::addRdata() not supported");
+    }
+
+    virtual isc::dns::RdataIteratorPtr getRdataIterator() const {
+        return (rrset_->getRdataIterator());
+    }
+
+    virtual isc::dns::RRsetPtr getRRsig() const {
+        return (rrset_->getRRsig());
+    }
+
+    // With all the RRsig methods, we have the problem that we store the
+    // underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
+    // but we need to modify it by adding or removing an RRSIG.  We overcome
+    // this by temporarily violating the "const" nature of the RRset to add the
+    // data.
+
+    virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(rdata);
+    }
+
+    virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(rdata);
+    }
+
+    virtual void addRRsig(const AbstractRRset& sigs) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(sigs);
+    }
+
+    virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(sigs);
+    }
+
+    virtual void addRRsig(const isc::dns::RRsetPtr& sigs) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(sigs);
+    }
+
+    virtual void removeRRsig() {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->removeRRsig();
+    }
+
+    /// \brief Return underlying RRset pointer
+    ///
+    /// ... mainly for testing.
+    isc::dns::ConstRRsetPtr getUnderlyingRRset() const {
+        return (rrset_);
+    }
+
+private:
+    isc::dns::ConstRRsetPtr rrset_;     ///< Underlying RRset
+};
+
+}   // namespace internal
+}   // namespace datasrc
+}   // namespace isc
+
+#endif  // __RBNODE_RRSET_H

+ 7 - 4
src/lib/datasrc/tests/Makefile.am

@@ -79,6 +79,7 @@ run_unittests_sqlite3_LDADD = $(common_ldadd)
 # In-memory datasource tests
 run_unittests_memory_SOURCES = $(common_sources)
 run_unittests_memory_SOURCES += memory_datasrc_unittest.cc
+run_unittests_memory_SOURCES += rbnode_rrset_unittest.cc
 run_unittests_memory_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
 
 run_unittests_memory_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -109,19 +110,21 @@ endif
 endif
 
 EXTRA_DIST =  testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/diffs.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/example.com.signed
 EXTRA_DIST += testdata/example.org
 EXTRA_DIST += testdata/example.org.nsec3-signed
 EXTRA_DIST += testdata/example.org.nsec3-signed-noparam
 EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/mkbrokendb.c
 EXTRA_DIST += testdata/root.zone
+EXTRA_DIST += testdata/rrset_toWire1
+EXTRA_DIST += testdata/rrset_toWire2
+EXTRA_DIST += testdata/rwtest.sqlite3
 EXTRA_DIST += testdata/sql1.example.com.signed
 EXTRA_DIST += testdata/sql2.example.com.signed
 EXTRA_DIST += testdata/test-root.sqlite3
 EXTRA_DIST += testdata/test.sqlite3
 EXTRA_DIST += testdata/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3

+ 16 - 7
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -172,14 +172,23 @@ TEST_F(InMemoryClientTest, iterator) {
     EXPECT_EQ(result::SUCCESS, zone->add(aRRsetA));
     EXPECT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
     EXPECT_EQ(result::SUCCESS, zone->add(subRRsetA));
-    // Check it with full zone, one by one.
-    // It should be in ascending order in case of InMemory data source
-    // (isn't guaranteed in general)
+
+    // Check it with full zone.
+    vector<ConstRRsetPtr> expected_rrsets;
+    expected_rrsets.push_back(aRRsetA);
+    expected_rrsets.push_back(aRRsetAAAA);
+    expected_rrsets.push_back(subRRsetA);
+
     iterator = memory_client.getIterator(Name("a"));
-    EXPECT_EQ(aRRsetA, iterator->getNextRRset());
-    EXPECT_EQ(aRRsetAAAA, iterator->getNextRRset());
-    EXPECT_EQ(subRRsetA, iterator->getNextRRset());
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+    vector<ConstRRsetPtr> actual_rrsets;
+    ConstRRsetPtr actual;
+    while ((actual = iterator->getNextRRset()) != NULL) {
+        actual_rrsets.push_back(actual);
+    }
+
+    rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                actual_rrsets.begin(), actual_rrsets.end());
+
 }
 
 TEST_F(InMemoryClientTest, iterator_separate_rrs) {

+ 258 - 0
src/lib/datasrc/tests/rbnode_rrset_unittest.cc

@@ -0,0 +1,258 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// 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 <stdexcept>
+
+#include <exceptions/exceptions.h>
+#include <dns/rdataclass.h>
+#include <datasrc/rbnode_rrset.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+using isc::UnitTestUtil;
+
+using namespace isc;
+using namespace isc::datasrc;
+using namespace isc::datasrc::internal;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::testutils;
+using namespace isc::util;
+using namespace std;
+
+// These tests are very similar to those for RRset - indeed, this file was
+// created from those tests.  However, the significant difference in behaviour
+// between RRset and RBNodeRRset - that the "set" methods in the latter mostly
+// result in exceptions being thrown - preclude use of full type
+// parameterisation of the tests.
+
+namespace {
+const char* const RRSIG_TXT =
+    "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
+    "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
+    "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
+    "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
+    "f49t+sXKPzbipN9g+s1ZPiIyofc=";
+
+class RBNodeRRsetTest : public ::testing::Test {
+protected:
+    RBNodeRRsetTest() :
+        test_name("test.example.com"),
+        test_domain("example.com"),
+        test_nsname("ns.example.com"),
+        rrset_a(ConstRRsetPtr(new RRset(
+                test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+        rrset_a_empty(ConstRRsetPtr(new RRset(
+                      test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+        rrset_ns(ConstRRsetPtr(new RRset(
+                 test_domain, RRClass::IN(), RRType::NS(), RRTTL(86400)))),
+        rrset_ch_txt(ConstRRsetPtr(new RRset(
+                     test_domain, RRClass::CH(), RRType::TXT(), RRTTL(0)))),
+        rrset_siga(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+                   RRTTL(3600)))
+
+    {
+        // Add a couple of Rdata elements to the A RRset.  The easiest way to
+        // do this is to override the "const" restrictions.  As this is a test,
+        // we don't feel too bad about doing so.
+        AbstractRRset* a_rrset =
+            const_cast<AbstractRRset*>(rrset_a.getUnderlyingRRset().get());
+        a_rrset->addRdata(in::A("192.0.2.1"));
+        a_rrset->addRdata(in::A("192.0.2.2"));
+
+        // Create the RRSIG corresponding to the rrset_a record.  The RDATA
+        // won't match the A record it covers, although it is internally
+        // self-consistent.
+        AbstractRRset* sig_rrset =
+            const_cast<AbstractRRset*>(rrset_siga.get());
+        sig_rrset->addRdata(generic::RRSIG(RRSIG_TXT));
+    }
+
+    const Name test_name;
+    const Name test_domain;
+    const Name test_nsname;
+
+    RBNodeRRset rrset_a;
+    RBNodeRRset rrset_a_empty;
+    const RBNodeRRset rrset_ns;
+    const RBNodeRRset rrset_ch_txt;
+
+    ConstRRsetPtr rrset_siga;
+};
+
+TEST_F(RBNodeRRsetTest, getRdataCount) {
+    EXPECT_EQ(0, rrset_a_empty.getRdataCount());
+    EXPECT_EQ(2, rrset_a.getRdataCount());
+}
+
+TEST_F(RBNodeRRsetTest, getName) {
+    EXPECT_EQ(test_name, rrset_a.getName());
+    EXPECT_EQ(test_domain, rrset_ns.getName());
+}
+
+TEST_F(RBNodeRRsetTest, getClass) {
+    EXPECT_EQ(RRClass("IN"), rrset_a.getClass());
+    EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass());
+}
+
+TEST_F(RBNodeRRsetTest, getType) {
+    EXPECT_EQ(RRType("A"), rrset_a.getType());
+    EXPECT_EQ(RRType("NS"), rrset_ns.getType());
+    EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType());
+}
+
+TEST_F(RBNodeRRsetTest, getTTL) {
+    EXPECT_EQ(RRTTL(3600), rrset_a.getTTL());
+    EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL());
+    EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL());
+}
+
+TEST_F(RBNodeRRsetTest, setName) {
+    EXPECT_THROW(rrset_a.setName(test_nsname), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, setTTL) {
+    EXPECT_THROW(rrset_a.setTTL(RRTTL(86400)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, toText) {
+    EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+              "test.example.com. 3600 IN A 192.0.2.2\n",
+              rrset_a.toText());
+
+    // toText() cannot be performed for an empty RRset.
+    EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
+}
+
+// Note: although the next two tests are essentially the same and used common
+// test code, they use different test data: the MessageRenderer produces
+// compressed wire data whereas the OutputBuffer does not.
+
+template <typename T>
+void
+performToWireTest(T& dataHolder, const RBNodeRRset& rrset,
+                  const RBNodeRRset& rrset_empty, const char* testdata)
+{
+    rrset.toWire(dataHolder);
+
+    std::vector<unsigned char> wiredata;
+    UnitTestUtil::readWireData(testdata, wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, dataHolder.getData(),
+                        dataHolder.getLength(), &wiredata[0], wiredata.size());
+
+    // toWire() cannot be performed for an empty RRset.
+    dataHolder.clear();
+    EXPECT_THROW(rrset_empty.toWire(dataHolder), EmptyRRset);
+}
+
+TEST_F(RBNodeRRsetTest, toWireRenderer) {
+    MessageRenderer renderer;
+    performToWireTest(renderer, rrset_a, rrset_a_empty, "rrset_toWire2");
+}
+
+TEST_F(RBNodeRRsetTest, toWireBuffer) {
+    OutputBuffer buffer(0);
+    performToWireTest(buffer, rrset_a, rrset_a_empty, "rrset_toWire1");
+}
+
+TEST_F(RBNodeRRsetTest, addRdata) {
+    EXPECT_THROW(rrset_a.addRdata(in::A("192.0.2.3")), NotImplemented);
+
+    // Check the same goes for trying to add the wrong type of data
+    EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, addRdataPtr) {
+    EXPECT_THROW(rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
+                                                    rrset_a_empty.getClass(),
+                                                    "192.0.2.1")),
+                 NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, getRDataIterator) {
+    RdataIteratorPtr it = rrset_a.getRdataIterator();
+    for (int i = 0; i < 2; ++i) {
+        ASSERT_FALSE(it->isLast());
+        ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1")));
+
+        it->next();
+        ASSERT_FALSE(it->isLast());
+        ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2")));
+
+        it->next();
+        ASSERT_TRUE(it->isLast());
+
+        // Should be able repeat the iteration by calling first().
+        it->first();
+    }
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST_F(RBNodeRRsetTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << rrset_a;
+    EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+              "test.example.com. 3600 IN A 192.0.2.2\n", oss.str());
+}
+
+// addRRSIG tests.
+TEST_F(RBNodeRRsetTest, addRRsigConstRdataPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    ConstRdataPtr data = createRdata(rrset_siga->getType(),
+                                     rrset_siga->getClass(), RRSIG_TXT);
+    rrset_a.addRRsig(data);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRdataPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    RdataPtr data = createRdata(rrset_siga->getType(), rrset_siga->getClass(),
+                                RRSIG_TXT);
+    rrset_a.addRRsig(data);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigAbstractRRset) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(*(rrset_siga.get()));
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigConstantRRsetPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(rrset_siga);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRRsetPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    RRsetPtr rrsig(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+                   RRTTL(3600)));
+    rrsig->addRdata(generic::RRSIG(RRSIG_TXT));
+    rrset_a.addRRsig(rrsig);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, removeRRsig) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(*(rrset_siga.get()));
+    EXPECT_TRUE(rrset_a.getRRsig());
+    rrset_a.removeRRsig();
+    EXPECT_FALSE(rrset_a.getRRsig());
+}
+
+}   // Anonymous namespace

+ 23 - 0
src/lib/datasrc/tests/testdata/rrset_toWire1

@@ -0,0 +1,23 @@
+#
+# Rendering an IN/A RRset containing 2 RRs:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+#
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6  7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: mostly the same except the RDATA
+04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02

+ 26 - 0
src/lib/datasrc/tests/testdata/rrset_toWire2

@@ -0,0 +1,26 @@
+#
+# Rendering an IN/A RRset and NS RRset as follows:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+# example.com. 1D IN NS ns.example.com.
+# Names will be compressed when possible.
+#
+# 0  1  2  3  4  5
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6  7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: the owner name is compresed
+c0 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02