1 // Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <dns/rrset_collection.h>
10 #include <dns/rrttl.h>
11 #include <dns/rdataclass.h>
12 
13 #include <gtest/gtest.h>
14 
15 #include <list>
16 #include <fstream>
17 
18 using namespace isc::dns;
19 using namespace isc::dns::rdata;
20 using namespace std;
21 
22 namespace {
23 
24 class RRsetCollectionTest : public ::testing::Test {
25 public:
RRsetCollectionTest()26     RRsetCollectionTest() :
27         rrclass("IN"),
28         origin("example.org"),
29         collection(TEST_DATA_SRCDIR "/example.org", origin, rrclass)
30     {}
31 
32     const RRClass rrclass;
33     const Name origin;
34     RRsetCollection collection;
35 };
36 
TEST_F(RRsetCollectionTest,istreamConstructor)37 TEST_F(RRsetCollectionTest, istreamConstructor) {
38     std::ifstream fs(TEST_DATA_SRCDIR "/example.org");
39     RRsetCollection collection2(fs, origin, rrclass);
40 
41     RRsetCollectionBase::Iterator iter = collection.begin();
42     RRsetCollectionBase::Iterator iter2 = collection2.begin();
43     while (iter != collection.end()) {
44          ASSERT_TRUE(iter2 != collection2.end());
45          EXPECT_EQ((*iter).toText(), (*iter2).toText());
46          ++iter;
47          ++iter2;
48     }
49     ASSERT_TRUE(iter2 == collection2.end());
50 }
51 
52 template <typename T, typename TP>
doFind(T & collection,const RRClass & rrclass)53 void doFind(T& collection, const RRClass& rrclass) {
54     // Test the find() that returns ConstRRsetPtr
55     TP rrset = collection.find(Name("www.example.org"), rrclass, RRType::A());
56     EXPECT_TRUE(rrset);
57     EXPECT_EQ(RRType::A(), rrset->getType());
58     EXPECT_EQ(RRTTL(3600), rrset->getTTL());
59     EXPECT_EQ(RRClass("IN"), rrset->getClass());
60     EXPECT_EQ(Name("www.example.org"), rrset->getName());
61 
62     // foo.example.org doesn't exist
63     rrset = collection.find(Name("foo.example.org"), rrclass, RRType::A());
64     EXPECT_FALSE(rrset);
65 
66     // www.example.org exists, but not with MX
67     rrset = collection.find(Name("www.example.org"), rrclass, RRType::MX());
68     EXPECT_FALSE(rrset);
69 
70     // www.example.org exists, with AAAA
71     rrset = collection.find(Name("www.example.org"), rrclass, RRType::AAAA());
72     EXPECT_TRUE(rrset);
73 
74     // www.example.org with AAAA does not exist in RRClass::CH()
75     rrset = collection.find(Name("www.example.org"), RRClass::CH(),
76                             RRType::AAAA());
77     EXPECT_FALSE(rrset);
78 }
79 
TEST_F(RRsetCollectionTest,findConst)80 TEST_F(RRsetCollectionTest, findConst) {
81     // Test the find() that returns ConstRRsetPtr
82     const RRsetCollection& ccln = collection;
83     doFind<const RRsetCollection, ConstRRsetPtr>(ccln, rrclass);
84 }
85 
TEST_F(RRsetCollectionTest,find)86 TEST_F(RRsetCollectionTest, find) {
87     // Test the find() that returns RRsetPtr
88     doFind<RRsetCollection, RRsetPtr>(collection, rrclass);
89 }
90 
91 void
doAddAndRemove(RRsetCollection & collection,const RRClass & rrclass)92 doAddAndRemove(RRsetCollection& collection, const RRClass& rrclass) {
93     // foo.example.org/A doesn't exist
94     RRsetPtr rrset_found = collection.find(Name("foo.example.org"), rrclass,
95                                            RRType::A());
96     EXPECT_FALSE(rrset_found);
97 
98     // Add foo.example.org/A
99     RRsetPtr rrset(new BasicRRset(Name("foo.example.org"), rrclass, RRType::A(),
100                                   RRTTL(7200)));
101     rrset->addRdata(in::A("192.0.2.1"));
102     collection.addRRset(rrset);
103 
104     // foo.example.org/A should now exist
105     rrset_found = collection.find(Name("foo.example.org"), rrclass,
106                                   RRType::A());
107     EXPECT_TRUE(rrset_found);
108     EXPECT_EQ(RRType::A(), rrset_found->getType());
109     EXPECT_EQ(RRTTL(7200), rrset_found->getTTL());
110     EXPECT_EQ(RRClass("IN"), rrset_found->getClass());
111     EXPECT_EQ(Name("foo.example.org"), rrset_found->getName());
112 
113     // The collection must not be empty.
114     EXPECT_TRUE(collection.end() != collection.begin());
115 
116     // Adding a duplicate RRset must throw.
117     EXPECT_THROW({
118         collection.addRRset(rrset);
119     }, isc::InvalidParameter);
120 
121     // Remove foo.example.org/A, which should pass
122     EXPECT_TRUE(collection.removeRRset(Name("foo.example.org"),
123                                        rrclass, RRType::A()));
124     // foo.example.org/A should not exist now
125     rrset_found = collection.find(Name("foo.example.org"), rrclass,
126                                   RRType::A());
127     EXPECT_FALSE(rrset_found);
128 
129     // Removing foo.example.org/A should fail now
130     EXPECT_FALSE(collection.removeRRset(Name("foo.example.org"),
131                                         rrclass, RRType::A()));
132 }
133 
TEST_F(RRsetCollectionTest,addAndRemove)134 TEST_F(RRsetCollectionTest, addAndRemove) {
135     doAddAndRemove(collection, rrclass);
136 }
137 
TEST_F(RRsetCollectionTest,empty)138 TEST_F(RRsetCollectionTest, empty) {
139     RRsetCollection cln;
140 
141     // Here, cln is empty.
142     EXPECT_TRUE(cln.end() == cln.begin());
143 
144     doAddAndRemove(cln, rrclass);
145 
146     // cln should be empty again here, after the add and remove
147     // operations.
148     EXPECT_TRUE(cln.end() == cln.begin());
149 }
150 
TEST_F(RRsetCollectionTest,iteratorTest)151 TEST_F(RRsetCollectionTest, iteratorTest) {
152     // The collection must not be empty.
153     EXPECT_TRUE(collection.end() != collection.begin());
154 
155     // Here, we just count the records and do some basic tests on them.
156     size_t count = 0;
157     for (RRsetCollection::Iterator it = collection.begin();
158          it != collection.end(); ++it) {
159          ++count;
160          const AbstractRRset& rrset = *it;
161          EXPECT_EQ(rrclass, rrset.getClass());
162          EXPECT_EQ(RRTTL(3600), rrset.getTTL());
163     }
164 
165     // example.org master file has SOA, NS, A, AAAA
166     EXPECT_EQ(4, count);
167 }
168 
169 // This is a dummy class which is used in iteratorCompareDifferent test
170 // to compare iterators from different RRsetCollectionBase
171 // implementations.
172 class MyRRsetCollection : public RRsetCollectionBase {
173 public:
MyRRsetCollection()174     MyRRsetCollection()
175     {}
176 
find(const isc::dns::Name &,const isc::dns::RRClass &,const isc::dns::RRType &) const177     virtual isc::dns::ConstRRsetPtr find(const isc::dns::Name&,
178                                          const isc::dns::RRClass&,
179                                          const isc::dns::RRType&) const {
180         return (ConstRRsetPtr());
181     }
182 
183     typedef std::list<isc::dns::RRset> MyCollection;
184 
185 protected:
186     class MyIter : public RRsetCollectionBase::Iter {
187     public:
MyIter(MyCollection::iterator & iter)188         MyIter(MyCollection::iterator& iter) :
189             iter_(iter)
190         {}
191 
getValue()192         virtual const isc::dns::AbstractRRset& getValue() {
193             return (*iter_);
194         }
195 
getNext()196         virtual IterPtr getNext() {
197             MyCollection::iterator it = iter_;
198             ++it;
199             return (RRsetCollectionBase::IterPtr(new MyIter(it)));
200         }
201 
equals(Iter & other)202         virtual bool equals(Iter& other) {
203             const MyIter* other_real = dynamic_cast<MyIter*>(&other);
204             if (other_real == NULL) {
205                 return (false);
206             }
207             return (iter_ == other_real->iter_);
208         }
209 
210     private:
211         MyCollection::iterator iter_;
212     };
213 
getBeginning()214     virtual RRsetCollectionBase::IterPtr getBeginning() {
215         MyCollection::iterator it = dummy_list_.begin();
216         return (RRsetCollectionBase::IterPtr(new MyIter(it)));
217     }
218 
getEnd()219     virtual RRsetCollectionBase::IterPtr getEnd() {
220         MyCollection::iterator it = dummy_list_.end();
221         return (RRsetCollectionBase::IterPtr(new MyIter(it)));
222     }
223 
224 private:
225     MyCollection dummy_list_;
226 };
227 
TEST_F(RRsetCollectionTest,iteratorCompareDifferent)228 TEST_F(RRsetCollectionTest, iteratorCompareDifferent) {
229     // Create objects of two different RRsetCollectionBase
230     // implementations.
231     RRsetCollection cln1;
232     MyRRsetCollection cln2;
233 
234     // Comparing two iterators from different RRsetCollectionBase
235     // implementations must not throw.
236     EXPECT_TRUE(cln2.begin() != cln1.begin());
237     EXPECT_TRUE(cln1.end() != cln2.end());
238 }
239 
240 } // namespace
241