// Copyright (C) 2010 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 #include #include #include #include #include #include #include #include #include #include #include #include using isc::UnitTestUtil; using namespace std; using namespace isc::dns; using namespace isc::util; using namespace isc::dns::rdata; namespace { class RRsetTest : public ::testing::Test { protected: RRsetTest() : buffer(0), test_name("test.example.com"), test_domain("example.com"), test_nsname("ns.example.com"), rrset_a(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)), rrset_a_empty(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)), rrset_ns(test_domain, RRClass::IN(), RRType::NS(), RRTTL(86400)), rrset_ch_txt(test_domain, RRClass::CH(), RRType::TXT(), RRTTL(0)) { rrset_a.addRdata(in::A("192.0.2.1")); rrset_a.addRdata(in::A("192.0.2.2")); } OutputBuffer buffer; MessageRenderer renderer; Name test_name; Name test_domain; Name test_nsname; RRset rrset_a; RRset rrset_a_empty; RRset rrset_ns; RRset rrset_ch_txt; std::vector wiredata; // max number of Rdata objects added to a test RRset object. // this is an arbitrary chosen limit, but should be sufficiently large // in practice and reasonable even as an extreme test case. static const int MAX_RDATA_COUNT = 100; }; TEST_F(RRsetTest, getRdataCount) { for (int i = 0; i < MAX_RDATA_COUNT; ++i) { EXPECT_EQ(i, rrset_a_empty.getRdataCount()); rrset_a_empty.addRdata(in::A("192.0.2.1")); } } TEST_F(RRsetTest, getName) { EXPECT_EQ(test_name, rrset_a.getName()); EXPECT_EQ(test_domain, rrset_ns.getName()); } TEST_F(RRsetTest, getClass) { EXPECT_EQ(RRClass("IN"), rrset_a.getClass()); EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass()); } TEST_F(RRsetTest, 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(RRsetTest, 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(RRsetTest, setTTL) { rrset_a.setTTL(RRTTL(86400)); EXPECT_EQ(RRTTL(86400), rrset_a.getTTL()); rrset_a.setTTL(RRTTL(0)); EXPECT_EQ(RRTTL(0), rrset_a.getTTL()); } TEST_F(RRsetTest, setName) { rrset_a.setName(test_nsname); EXPECT_EQ(test_nsname, rrset_a.getName()); } TEST_F(RRsetTest, isSameKind) { RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)); RRset rrset_x(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)); RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600)); RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600)); RRset rrset_p(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600)); EXPECT_TRUE(rrset_w.isSameKind(rrset_w)); EXPECT_TRUE(rrset_w.isSameKind(rrset_x)); EXPECT_FALSE(rrset_w.isSameKind(rrset_y)); EXPECT_FALSE(rrset_w.isSameKind(rrset_z)); EXPECT_FALSE(rrset_w.isSameKind(rrset_p)); } // Utility function to create an add an RRset to a vector of RRsets for the // "less" test. It's only purpose is to allow the RRset creation to be // written with arguments in an order that reflects the RRset ordering. void addRRset(std::vector& vec, const RRType& rrtype, const RRClass& rrclass, const char* rrname) { vec.push_back(ConstRRsetPtr(new RRset(Name(rrname), rrclass, rrtype, RRTTL(3600)))); } TEST_F(RRsetTest, lthan) { // Check values of type codes: this effectively documents the expected // order of the rrsets created. ASSERT_EQ(1, RRType::A().getCode()); ASSERT_EQ(2, RRType::NS().getCode()); ASSERT_EQ(1, RRClass::IN().getCode()); ASSERT_EQ(3, RRClass::CH().getCode()); // Create a vector of RRsets in ascending sort order. std::vector rrsets; addRRset(rrsets, RRType::A(), RRClass::IN(), "alpha.com"); addRRset(rrsets, RRType::A(), RRClass::IN(), "beta.com"); addRRset(rrsets, RRType::A(), RRClass::CH(), "alpha.com"); addRRset(rrsets, RRType::A(), RRClass::CH(), "beta.com"); addRRset(rrsets, RRType::NS(), RRClass::IN(), "alpha.com"); addRRset(rrsets, RRType::NS(), RRClass::IN(), "beta.com"); addRRset(rrsets, RRType::NS(), RRClass::CH(), "alpha.com"); addRRset(rrsets, RRType::NS(), RRClass::CH(), "beta.com"); // ... and do the checks. The ASSERT_ form is used to avoid a plethora // of messages if there is an error. And if there is an error, supply // a more informative message. for (int i = 0; i < rrsets.size(); ++i) { // Check that an RRset is not less than itself ostringstream ossi; ossi << "i = (" << rrsets[i]->getType().toText() << ", " << rrsets[i]->getClass().toText() << ", " << rrsets[i]->getName().toText() << ")"; ASSERT_FALSE(rrsets[i]->lthan(*rrsets[i])) << ossi.str(); for (int j = i + 1; j < rrsets.size(); ++j) { // Check it against the remaining RRsets. ostringstream ossj; ossj << ", j = (" << rrsets[j]->getType().toText() << ", " << rrsets[j]->getClass().toText() << ", " << rrsets[j]->getName().toText() << ")"; ASSERT_TRUE(rrsets[i]->lthan(*rrsets[j])) << ossi.str() << ossj.str(); ASSERT_FALSE(rrsets[j]->lthan(*rrsets[i])) << ossi.str() << ossj.str(); } } } void addRdataTestCommon(const RRset& rrset) { EXPECT_EQ(2, rrset.getRdataCount()); RdataIteratorPtr it = rrset.getRdataIterator(); // cursor is set to the 1st EXPECT_FALSE(it->isLast()); EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1"))); it->next(); EXPECT_FALSE(it->isLast()); EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2"))); it->next(); EXPECT_TRUE(it->isLast()); } TEST_F(RRsetTest, addRdata) { addRdataTestCommon(rrset_a); // Reference version of addRdata() doesn't allow to add a different // type of Rdata. EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), std::bad_cast); } TEST_F(RRsetTest, addRdataPtr) { rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(), rrset_a_empty.getClass(), "192.0.2.1")); rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(), rrset_a_empty.getClass(), "192.0.2.2")); addRdataTestCommon(rrset_a); // Pointer version of addRdata() doesn't type check and does allow to //add a different type of Rdata as a result. rrset_a_empty.addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns.example.com")); EXPECT_EQ(3, rrset_a_empty.getRdataCount()); } TEST_F(RRsetTest, iterator) { // Iterator for an empty RRset. RdataIteratorPtr it = rrset_a_empty.getRdataIterator(); EXPECT_TRUE(it->isLast()); // Normal case (already tested, but do it again just in case) rrset_a_empty.addRdata(in::A("192.0.2.1")); rrset_a_empty.addRdata(in::A("192.0.2.2")); addRdataTestCommon(rrset_a_empty); // Rewind test: should be repeat the iteration by calling first(). for (int i = 0; i < 2; ++i) { it = rrset_a_empty.getRdataIterator(); it->first(); EXPECT_FALSE(it->isLast()); it->next(); EXPECT_FALSE(it->isLast()); it->next(); EXPECT_TRUE(it->isLast()); } } TEST_F(RRsetTest, 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); } TEST_F(RRsetTest, toWireBuffer) { rrset_a.toWire(buffer); UnitTestUtil::readWireData("rrset_toWire1", wiredata); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(), buffer.getLength(), &wiredata[0], wiredata.size()); // toWire() cannot be performed for an empty RRset. buffer.clear(); EXPECT_THROW(rrset_a_empty.toWire(buffer), EmptyRRset); } TEST_F(RRsetTest, toWireRenderer) { rrset_ns.addRdata(generic::NS(test_nsname)); rrset_a.toWire(renderer); rrset_ns.toWire(renderer); UnitTestUtil::readWireData("rrset_toWire2", wiredata); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(), renderer.getLength(), &wiredata[0], wiredata.size()); // toWire() cannot be performed for an empty RRset. renderer.clear(); EXPECT_THROW(rrset_a_empty.toWire(renderer), EmptyRRset); } // test operator<<. We simply confirm it appends the result of toText(). TEST_F(RRsetTest, LeftShiftOperator) { ostringstream oss; oss << rrset_a; EXPECT_EQ(rrset_a.toText(), oss.str()); } }