Parcourir la source

[2438] Merge remote-tracking branch 'origin/trac2432' into trac2438

JINMEI Tatuya il y a 12 ans
Parent
commit
d078381e41

+ 4 - 0
src/lib/dns/Makefile.am

@@ -127,6 +127,8 @@ libb10_dns___la_SOURCES += tsigrecord.h tsigrecord.cc
 libb10_dns___la_SOURCES += character_string.h character_string.cc
 libb10_dns___la_SOURCES += master_loader_callbacks.h master_loader_callbacks.cc
 libb10_dns___la_SOURCES += master_loader.h
+libb10_dns___la_SOURCES += rrset_collection_base.h
+libb10_dns___la_SOURCES += rrset_collection.h rrset_collection.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/char_string.h
 libb10_dns___la_SOURCES += rdata/generic/detail/char_string.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
@@ -170,6 +172,8 @@ libdns___include_HEADERS = \
 	rdata.h \
 	rrparamregistry.h \
 	rrset.h \
+	rrset_collection_base.h \
+	rrset_collection.h \
 	rrttl.h \
 	tsigkey.h
 # Purposely not installing these headers:

+ 128 - 0
src/lib/dns/rrset_collection.cc

@@ -0,0 +1,128 @@
+// 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 <dns/rrset_collection.h>
+#include <dns/master_loader_callbacks.h>
+#include <dns/master_loader.h>
+#include <dns/rrcollator.h>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/bind.hpp>
+
+using namespace isc;
+
+namespace isc {
+namespace dns {
+
+void
+RRsetCollection::loaderCallback(const std::string&, size_t, const std::string&)
+{
+     // We just ignore callbacks for errors and warnings.
+}
+
+void
+RRsetCollection::addRRset(RRsetPtr rrset) {
+    const CollectionKey key(rrset->getClass(), rrset->getType(),
+                            rrset->getName());
+    CollectionMap::const_iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        isc_throw(InvalidParameter,
+                  "RRset for " << rrset->getName() << "/" << rrset->getClass()
+                  << " with type " << rrset->getType() << " already exists");
+    }
+
+    rrsets_.insert(std::pair<CollectionKey, RRsetPtr>(key, rrset));
+}
+
+template<typename T>
+void
+RRsetCollection::constructHelper(T source, const isc::dns::Name& origin,
+                                 const isc::dns::RRClass& rrclass)
+{
+    RRCollator collator(boost::bind(&RRsetCollection::addRRset, this, _1));
+    MasterLoaderCallbacks callbacks
+        (boost::bind(&RRsetCollection::loaderCallback, this, _1, _2, _3),
+         boost::bind(&RRsetCollection::loaderCallback, this, _1, _2, _3));
+    MasterLoader loader(source, origin, rrclass, callbacks,
+                        collator.getCallback(),
+                        MasterLoader::DEFAULT);
+    loader.load();
+    collator.flush();
+}
+
+RRsetCollection::RRsetCollection(const char* filename, const Name& origin,
+                                 const RRClass& rrclass)
+{
+    constructHelper<const char*>(filename, origin, rrclass);
+}
+
+RRsetCollection::RRsetCollection(std::istream& input_stream, const Name& origin,
+                                 const RRClass& rrclass)
+{
+    constructHelper<std::istream&>(input_stream, origin, rrclass);
+}
+
+RRsetPtr
+RRsetCollection::find(const Name& name, const RRClass& rrclass,
+                      const RRType& rrtype) {
+    const CollectionKey key(rrclass, rrtype, name);
+    CollectionMap::iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        return (it->second);
+    }
+    return (RRsetPtr());
+}
+
+ConstRRsetPtr
+RRsetCollection::find(const Name& name, const RRClass& rrclass,
+                      const RRType& rrtype) const
+{
+    const CollectionKey key(rrclass, rrtype, name);
+    CollectionMap::const_iterator it = rrsets_.find(key);
+    if (it != rrsets_.end()) {
+        return (it->second);
+    }
+    return (ConstRRsetPtr());
+}
+
+bool
+RRsetCollection::removeRRset(const Name& name, const RRClass& rrclass,
+                             const RRType& rrtype)
+{
+    const CollectionKey key(rrclass, rrtype, name);
+
+    CollectionMap::iterator it = rrsets_.find(key);
+    if (it == rrsets_.end()) {
+        return (false);
+    }
+
+    rrsets_.erase(it);
+    return (true);
+}
+
+RRsetCollectionBase::IterPtr
+RRsetCollection::getBeginning() {
+    CollectionMap::iterator it = rrsets_.begin();
+    return (RRsetCollectionBase::IterPtr(new DnsIter(it)));
+}
+
+RRsetCollectionBase::IterPtr
+RRsetCollection::getEnd() {
+    CollectionMap::iterator it = rrsets_.end();
+    return (RRsetCollectionBase::IterPtr(new DnsIter(it)));
+}
+
+} // end of namespace dns
+} // end of namespace isc

+ 172 - 0
src/lib/dns/rrset_collection.h

@@ -0,0 +1,172 @@
+// 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 RRSET_COLLECTION_H
+#define RRSET_COLLECTION_H 1
+
+#include <dns/rrset_collection_base.h>
+#include <dns/rrclass.h>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+
+#include <map>
+
+namespace isc {
+namespace dns {
+
+/// \brief libdns++ implementation of RRsetCollectionBase using an STL
+/// container.
+class RRsetCollection : public RRsetCollectionBase {
+public:
+    /// \brief Constructor.
+    ///
+    /// This constructor creates an empty collection without any data in
+    /// it. RRsets can be added to the collection with the \c addRRset()
+    /// method.
+    RRsetCollection() {}
+
+    /// \brief Constructor.
+    ///
+    /// The \c origin and \c rrclass arguments are required for the zone
+    /// loading, but \c RRsetCollection itself does not do any
+    /// validation, and the collection of RRsets does not have to form a
+    /// valid zone. The constructor throws MasterLoaderError if there is
+    /// an error during loading.
+    ///
+    /// \param filename Name of a file containing a collection of RRs in
+    /// the master file format (which may or may not form a valid zone).
+    /// \param origin The zone origin.
+    /// \param rrclass The zone class.
+    RRsetCollection(const char* filename, const isc::dns::Name& origin,
+                    const isc::dns::RRClass& rrclass);
+
+    /// \brief Constructor.
+    ///
+    /// This constructor is similar to the previous one, but instead of
+    /// taking a filename to load a zone from, it takes an input
+    /// stream. The constructor throws MasterLoaderError if there is an
+    /// error during loading.
+    ///
+    /// \param input_stream The input stream to load from.
+    /// \param origin The zone origin.
+    /// \param rrclass The zone class.
+    RRsetCollection(std::istream& input_stream, const isc::dns::Name& origin,
+                    const isc::dns::RRClass& rrclass);
+
+    /// \brief Destructor
+    virtual ~RRsetCollection() {}
+
+    /// \brief Add an RRset to the collection.
+    ///
+    /// Does not do any validation whether \c rrset belongs to a
+    /// particular zone or not. A reference to \c rrset is taken in an
+    /// internally managed \c shared_ptr, so even if the caller's
+    /// \c RRsetPtr is destroyed, the RRset it wrapped is still alive
+    /// and managed by the \c RRsetCollection. It throws an
+    /// \c isc::InvalidParameter exception if an rrset with the same
+    /// class, type and name already exists.
+    ///
+    /// Callers must not modify the RRset after adding it to the
+    /// collection, as the rrset is indexed internally by the
+    /// collection.
+    void addRRset(isc::dns::RRsetPtr rrset);
+
+    /// \brief Remove an RRset from the collection.
+    ///
+    /// RRset(s) matching the \c name, \c rrclass and \c rrtype are
+    /// removed from the collection.
+    ///
+    /// \returns \c true if a matching RRset was deleted, \c false if no
+    /// such RRset exists.
+    bool removeRRset(const isc::dns::Name& name,
+                     const isc::dns::RRClass& rrclass,
+                     const isc::dns::RRType& rrtype);
+
+    /// \brief Find a matching RRset in the collection.
+    ///
+    /// Returns the RRset in the collection that exactly matches the
+    /// given \c name, \c rrclass and \c rrtype.  If no matching RRset
+    /// is found, \c NULL is returned.
+    ///
+    /// \param name The name of the RRset to search for.
+    /// \param rrclass The class of the RRset to search for.
+    /// \param rrtype The type of the RRset to search for.
+    /// \returns The RRset if found, \c NULL otherwise.
+    virtual isc::dns::ConstRRsetPtr find(const isc::dns::Name& name,
+                                         const isc::dns::RRClass& rrclass,
+                                         const isc::dns::RRType& rrtype) const;
+
+    /// \brief Find a matching RRset in the collection (non-const
+    /// variant).
+    ///
+    /// See above for a description of the method and arguments.
+    isc::dns::RRsetPtr find(const isc::dns::Name& name,
+                            const isc::dns::RRClass& rrclass,
+                            const isc::dns::RRType& rrtype);
+
+private:
+    template<typename T>
+    void constructHelper(T source, const isc::dns::Name& origin,
+                         const isc::dns::RRClass& rrclass);
+    void loaderCallback(const std::string&, size_t, const std::string&);
+
+    typedef boost::tuple<isc::dns::RRClass, isc::dns::RRType, isc::dns::Name>
+        CollectionKey;
+    typedef std::map<CollectionKey, isc::dns::RRsetPtr> CollectionMap;
+
+    CollectionMap rrsets_;
+
+protected:
+    class DnsIter : public RRsetCollectionBase::Iter {
+    public:
+        DnsIter(CollectionMap::iterator& iter) :
+            iter_(iter)
+        {}
+
+        virtual const isc::dns::AbstractRRset& getValue() {
+            isc::dns::RRsetPtr& rrset = iter_->second;
+            return (*rrset);
+        }
+
+        virtual IterPtr getNext() {
+            CollectionMap::iterator it = iter_;
+            it++;
+            return (RRsetCollectionBase::IterPtr(new DnsIter(it)));
+        }
+
+        virtual bool equals(Iter& other) {
+            const DnsIter* other_real = dynamic_cast<DnsIter*>(&other);
+            if (other_real == NULL) {
+                return (false);
+            }
+            return (iter_ == other_real->iter_);
+        }
+
+    private:
+        CollectionMap::iterator iter_;
+    };
+
+    virtual RRsetCollectionBase::IterPtr getBeginning();
+    virtual RRsetCollectionBase::IterPtr getEnd();
+};
+
+} // end of namespace dns
+} // end of namespace isc
+
+#endif  // RRSET_COLLECTION_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 163 - 0
src/lib/dns/rrset_collection_base.h

@@ -0,0 +1,163 @@
+// 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 RRSET_COLLECTION_BASE_H
+#define RRSET_COLLECTION_BASE_H 1
+
+#include <dns/rrset.h>
+#include <dns/name.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <iterator>
+
+namespace isc {
+namespace dns {
+
+/// \brief Generic class to represent a set of RRsets.
+///
+/// This is a generic container and the stored set of RRsets does not
+/// necessarily form a valid zone (e.g. there doesn't necessarily have
+/// to be an SOA at the "origin"). Instead, it will be used to represent
+/// a single zone for the purpose of zone loading/checking. It provides
+/// a simple find() method to find an RRset for the given name and type
+/// (and maybe class) and a way to iterate over all RRsets.
+///
+/// See \c RRsetCollection for a simple libdns++ implementation using an
+/// STL container. libdatasrc will have another implementation.
+class RRsetCollectionBase {
+public:
+    /// \brief Find a matching RRset in the collection.
+    ///
+    /// Returns the RRset in the collection that exactly matches the
+    /// given \c name, \c rrclass and \c rrtype.  If no matching RRset
+    /// is found, \c NULL is returned.
+    ///
+    /// \param name The name of the RRset to search for.
+    /// \param rrtype The type of the RRset to search for.
+    /// \param rrclass The class of the RRset to search for.
+    /// \returns The RRset if found, \c NULL otherwise.
+    virtual isc::dns::ConstRRsetPtr find
+        (const isc::dns::Name& name, const isc::dns::RRClass& rrclass,
+         const isc::dns::RRType& rrtype)
+        const = 0;
+
+    /// \brief Destructor
+    virtual ~RRsetCollectionBase() {}
+
+protected:
+    class Iter; // forward declaration
+
+    /// \brief Wraps Iter with a reference count.
+    typedef boost::shared_ptr<Iter> IterPtr;
+
+    /// \brief A helper iterator interface for \c RRsetCollectionBase.
+    ///
+    /// This is a protected iterator class that is a helper interface
+    /// used by the public iterator.  Derived classes of
+    /// \c RRsetCollectionBase are supposed to implement this class and
+    /// the \c getBeginning() and \c getEnd() methods, so that the
+    /// public interator interface can be provided. This is a forward
+    /// iterator only.
+    class Iter {
+    public:
+        /// \brief Returns the \c AbstractRRset currently pointed to by
+        /// the iterator.
+        virtual const isc::dns::AbstractRRset& getValue() = 0;
+
+        /// \brief Returns an \c IterPtr wrapping an Iter pointing to
+        /// the next \c AbstractRRset in sequence in the collection.
+        virtual IterPtr getNext() = 0;
+
+        /// \brief Check if another iterator is equal to this one.
+        ///
+        /// Returns \c true if this iterator is equal to \c other,
+        /// \c false otherwise. Note that if \c other is not the same
+        /// type as \c this, or cannot be compared meaningfully, the
+        /// method must return \c false.
+        ///
+        /// \param other The other iterator to compare against.
+        /// \returns \c true if equal, \c false otherwise.
+        virtual bool equals(Iter& other) = 0;
+    };
+
+    /// \brief Returns an \c IterPtr wrapping an Iter pointing to the
+    /// beginning of the collection.
+    virtual IterPtr getBeginning() = 0;
+
+    /// \brief Returns an \c IterPtr wrapping an Iter pointing past the
+    /// end of the collection.
+    virtual IterPtr getEnd() = 0;
+
+public:
+    /// \brief A forward \c std::iterator for \c RRsetCollectionBase.
+    ///
+    /// It behaves like a \c std::iterator forward iterator, so please
+    /// see its documentation for usage.
+    class Iterator : std::iterator<std::forward_iterator_tag,
+                                   const isc::dns::AbstractRRset>
+    {
+    public:
+        explicit Iterator(IterPtr iter) :
+            iter_(iter)
+        {}
+
+        reference operator*() {
+            return (iter_->getValue());
+        }
+
+        Iterator& operator++() {
+            iter_ = iter_->getNext();
+            return (*this);
+        }
+
+        Iterator operator++(int) {
+            Iterator tmp(iter_);
+            ++*this;
+            return (tmp);
+        }
+
+        bool operator==(const Iterator& other) const {
+            return (iter_->equals(*other.iter_));
+        }
+
+        bool operator!=(const Iterator& other) const {
+            return (!iter_->equals(*other.iter_));
+        }
+
+    private:
+        IterPtr iter_;
+    };
+
+    /// \brief Returns an iterator pointing to the beginning of the
+    /// collection.
+    Iterator begin() {
+      return Iterator(getBeginning());
+    }
+
+    /// \brief Returns an iterator pointing past the end of the
+    /// collection.
+    Iterator end() {
+      return Iterator(getEnd());
+    }
+};
+
+} // end of namespace dns
+} // end of namespace isc
+
+#endif  // RRSET_COLLECTION_BASE_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -75,6 +75,7 @@ run_unittests_SOURCES += tsigkey_unittest.cc
 run_unittests_SOURCES += tsigrecord_unittest.cc
 run_unittests_SOURCES += character_string_unittest.cc
 run_unittests_SOURCES += master_loader_callbacks_test.cc
+run_unittests_SOURCES += rrset_collection_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 # We shouldn't need to include BOTAN_LIBS here, but there

+ 249 - 0
src/lib/dns/tests/rrset_collection_unittest.cc

@@ -0,0 +1,249 @@
+// 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 <dns/rrset_collection.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <fstream>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace std;
+
+namespace {
+
+class RRsetCollectionTest : public ::testing::Test {
+public:
+    RRsetCollectionTest() :
+        rrclass("IN"),
+        origin("example.org"),
+        collection(TEST_DATA_SRCDIR "/example.org", origin, rrclass)
+    {}
+
+    const RRClass rrclass;
+    const Name origin;
+    RRsetCollection collection;
+};
+
+TEST_F(RRsetCollectionTest, istreamConstructor) {
+    std::ifstream fs(TEST_DATA_SRCDIR "/example.org");
+    RRsetCollection collection2(fs, origin, rrclass);
+
+    RRsetCollectionBase::Iterator iter = collection.begin();
+    RRsetCollectionBase::Iterator iter2 = collection2.begin();
+    while (iter != collection.end()) {
+         EXPECT_TRUE(iter2 != collection2.end());
+         EXPECT_EQ((*iter).toText(), (*iter2).toText());
+         ++iter;
+         ++iter2;
+    }
+    EXPECT_TRUE(iter2 == collection2.end());
+}
+
+template <typename T, typename TP>
+void doFind(T& collection, const RRClass& rrclass) {
+    // Test the find() that returns ConstRRsetPtr
+    TP rrset = collection.find(Name("www.example.org"), rrclass, RRType::A());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRTTL(3600), rrset->getTTL());
+    EXPECT_EQ(RRClass("IN"), rrset->getClass());
+    EXPECT_EQ(Name("www.example.org"), rrset->getName());
+
+    // foo.example.org doesn't exist
+    rrset = collection.find(Name("foo.example.org"), rrclass, RRType::A());
+    EXPECT_FALSE(rrset);
+
+    // www.example.org exists, but not with MX
+    rrset = collection.find(Name("www.example.org"), rrclass, RRType::MX());
+    EXPECT_FALSE(rrset);
+
+    // www.example.org exists, with AAAA
+    rrset = collection.find(Name("www.example.org"), rrclass, RRType::AAAA());
+    EXPECT_TRUE(rrset);
+
+    // www.example.org with AAAA does not exist in RRClass::CH()
+    rrset = collection.find(Name("www.example.org"), RRClass::CH(),
+                            RRType::AAAA());
+    EXPECT_FALSE(rrset);
+}
+
+TEST_F(RRsetCollectionTest, findConst) {
+    // Test the find() that returns ConstRRsetPtr
+    const RRsetCollection& ccln = collection;
+    doFind<const RRsetCollection, ConstRRsetPtr>(ccln, rrclass);
+}
+
+TEST_F(RRsetCollectionTest, find) {
+    // Test the find() that returns RRsetPtr
+    doFind<RRsetCollection, RRsetPtr>(collection, rrclass);
+}
+
+void
+doAddAndRemove(RRsetCollection& collection, const RRClass& rrclass) {
+    // foo.example.org/A doesn't exist
+    RRsetPtr rrset_found = collection.find(Name("foo.example.org"), rrclass,
+                                           RRType::A());
+    EXPECT_FALSE(rrset_found);
+
+    // Add foo.example.org/A
+    RRsetPtr rrset(new BasicRRset(Name("foo.example.org"), rrclass, RRType::A(),
+                                  RRTTL(7200)));
+    rrset->addRdata(in::A("192.0.2.1"));
+    collection.addRRset(rrset);
+
+    // foo.example.org/A should now exist
+    rrset_found = collection.find(Name("foo.example.org"), rrclass,
+                                  RRType::A());
+    EXPECT_TRUE(rrset_found);
+    EXPECT_EQ(RRType::A(), rrset_found->getType());
+    EXPECT_EQ(RRTTL(7200), rrset_found->getTTL());
+    EXPECT_EQ(RRClass("IN"), rrset_found->getClass());
+    EXPECT_EQ(Name("foo.example.org"), rrset_found->getName());
+
+    // The collection must not be empty.
+    EXPECT_TRUE(collection.end() != collection.begin());
+
+    // Adding a duplicate RRset must throw.
+    EXPECT_THROW({
+        collection.addRRset(rrset);
+    }, isc::InvalidParameter);
+
+    // Remove foo.example.org/A, which should pass
+    bool exists = collection.removeRRset(Name("foo.example.org"),
+                                         rrclass, RRType::A());
+    EXPECT_TRUE(exists);
+
+    // foo.example.org/A should not exist now
+    rrset_found = collection.find(Name("foo.example.org"), rrclass,
+                                  RRType::A());
+    EXPECT_FALSE(rrset_found);
+
+    // Removing foo.example.org/A should fail now
+    exists = collection.removeRRset(Name("foo.example.org"),
+                                    rrclass, RRType::A());
+    EXPECT_FALSE(exists);
+}
+
+TEST_F(RRsetCollectionTest, addAndRemove) {
+    doAddAndRemove(collection, rrclass);
+}
+
+TEST_F(RRsetCollectionTest, empty) {
+    RRsetCollection cln;
+
+    // Here, cln is empty.
+    EXPECT_TRUE(cln.end() == cln.begin());
+
+    doAddAndRemove(cln, rrclass);
+
+    // cln should be empty again here, after the add and remove
+    // operations.
+    EXPECT_TRUE(cln.end() == cln.begin());
+}
+
+TEST_F(RRsetCollectionTest, iteratorTest) {
+    // The collection must not be empty.
+    EXPECT_TRUE(collection.end() != collection.begin());
+
+    // Here, we just count the records and do some basic tests on them.
+    size_t count = 0;
+    for (RRsetCollection::Iterator it = collection.begin();
+         it != collection.end(); ++it) {
+         ++count;
+         const AbstractRRset& rrset = *it;
+         EXPECT_EQ(rrclass, rrset.getClass());
+         EXPECT_EQ(RRTTL(3600), rrset.getTTL());
+    }
+
+    // example.org master file has SOA, NS, A, AAAA
+    EXPECT_EQ(4, count);
+}
+
+// This is a dummy class which is used in iteratorCompareDifferent test
+// to compare iterators from different RRsetCollectionBase
+// implementations.
+class MyRRsetCollection : public RRsetCollectionBase {
+public:
+    MyRRsetCollection()
+    {}
+
+    virtual isc::dns::ConstRRsetPtr find(const isc::dns::Name&,
+                                         const isc::dns::RRClass&,
+                                         const isc::dns::RRType&) const {
+        return (ConstRRsetPtr());
+    }
+
+    typedef std::list<isc::dns::RRset> MyCollection;
+
+protected:
+    class MyIter : public RRsetCollectionBase::Iter {
+    public:
+        MyIter(MyCollection::iterator& iter) :
+            iter_(iter)
+        {}
+
+        virtual const isc::dns::AbstractRRset& getValue() {
+            return (*iter_);
+        }
+
+        virtual IterPtr getNext() {
+            MyCollection::iterator it = iter_;
+            it++;
+            return (RRsetCollectionBase::IterPtr(new MyIter(it)));
+        }
+
+        virtual bool equals(Iter& other) {
+            const MyIter* other_real = dynamic_cast<MyIter*>(&other);
+            if (other_real == NULL) {
+                return (false);
+            }
+            return (iter_ == other_real->iter_);
+        }
+
+    private:
+        MyCollection::iterator iter_;
+    };
+
+    virtual RRsetCollectionBase::IterPtr getBeginning() {
+        MyCollection::iterator it = dummy_list_.begin();
+        return (RRsetCollectionBase::IterPtr(new MyIter(it)));
+    }
+
+    virtual RRsetCollectionBase::IterPtr getEnd() {
+        MyCollection::iterator it = dummy_list_.end();
+        return (RRsetCollectionBase::IterPtr(new MyIter(it)));
+    }
+
+private:
+    MyCollection dummy_list_;
+};
+
+TEST_F(RRsetCollectionTest, iteratorCompareDifferent) {
+    // Create objects of two different RRsetCollectionBase
+    // implementations.
+    RRsetCollection cln1;
+    MyRRsetCollection cln2;
+
+    // Comparing two iterators from different RRsetCollectionBase
+    // implementations must not throw.
+    EXPECT_TRUE(cln2.begin() != cln1.begin());
+    EXPECT_TRUE(cln1.end() != cln2.end());
+}
+
+} // namespace