memory_datasrc_unittest.cc 52.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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.

15 16 17 18
#include <sstream>
#include <vector>

#include <boost/bind.hpp>
19
#include <boost/foreach.hpp>
20

21 22
#include <exceptions/exceptions.h>

23
#include <dns/masterload.h>
24
#include <dns/name.h>
25 26
#include <dns/rdata.h>
#include <dns/rdataclass.h>
27
#include <dns/rrclass.h>
chenzhengzhang's avatar
chenzhengzhang committed
28
#include <dns/rrsetlist.h>
Michal Vaner's avatar
Michal Vaner committed
29
#include <dns/rrttl.h>
Michal Vaner's avatar
Michal Vaner committed
30
#include <dns/masterload.h>
31 32

#include <datasrc/memory_datasrc.h>
33 34
#include <datasrc/data_source.h>
#include <datasrc/iterator.h>
35

36 37
#include <testutils/dnsmessage_test.h>

38 39
#include <gtest/gtest.h>

40
using namespace std;
41
using namespace isc::dns;
42
using namespace isc::dns::rdata;
43
using namespace isc::datasrc;
44
using namespace isc::testutils;
45 46

namespace {
47 48 49
// Commonly used result codes (Who should write the prefix all the time)
using result::SUCCESS;
using result::EXIST;
50

51
class InMemoryClientTest : public ::testing::Test {
52
protected:
53
    InMemoryClientTest() : rrclass(RRClass::IN())
54
    {}
55
    RRClass rrclass;
56
    InMemoryClient memory_client;
57 58
};

59
TEST_F(InMemoryClientTest, add_find_Zone) {
60 61
    // test add zone
    // Bogus zone (NULL)
62
    EXPECT_THROW(memory_client.addZone(ZoneFinderPtr()),
63
                 isc::InvalidParameter);
64 65

    // add zones with different names one by one
66
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
67 68
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
                                                       Name("a")))));
69
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
70 71
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
                                                       Name("b")))));
72
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
73 74
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
                                                       Name("c")))));
75
    // add zones with the same name suffix
76
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
77 78
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
                                                       Name("x.d.e.f")))));
79
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
80 81
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
                                                       Name("o.w.y.d.e.f")))));
82
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
83 84
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
                                                       Name("p.w.y.d.e.f")))));
85
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
86 87
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
                                                       Name("q.w.y.d.e.f")))));
88
    // add super zone and its subzone
89
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
90 91
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
                                                       Name("g.h")))));
92
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
93
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
94
                                               Name("i.g.h")))));
95
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
96 97
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
                                                       Name("z.d.e.f")))));
98
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(
99 100
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
                                                       Name("j.z.d.e.f")))));
101 102

    // different zone class isn't allowed.
103
    EXPECT_EQ(result::EXIST, memory_client.addZone(
104 105
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::CH(),
                                                       Name("q.w.y.d.e.f")))));
106 107

    // names are compared in a case insensitive manner.
108
    EXPECT_EQ(result::EXIST, memory_client.addZone(
109 110
                  ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
                                                       Name("Q.W.Y.d.E.f")))));
111 112

    // test find zone
113
    EXPECT_EQ(result::SUCCESS, memory_client.findZone(Name("a")).code);
114
    EXPECT_EQ(Name("a"),
115
              memory_client.findZone(Name("a")).zone_finder->getOrigin());
116 117

    EXPECT_EQ(result::SUCCESS,
118
              memory_client.findZone(Name("j.z.d.e.f")).code);
119
    EXPECT_EQ(Name("j.z.d.e.f"),
120 121
              memory_client.findZone(Name("j.z.d.e.f")).zone_finder->
                  getOrigin());
122 123

    // NOTFOUND
124
    EXPECT_EQ(result::NOTFOUND, memory_client.findZone(Name("d.e.f")).code);
125
    EXPECT_EQ(ConstZoneFinderPtr(),
126
              memory_client.findZone(Name("d.e.f")).zone_finder);
127 128

    EXPECT_EQ(result::NOTFOUND,
129
              memory_client.findZone(Name("w.y.d.e.f")).code);
130
    EXPECT_EQ(ConstZoneFinderPtr(),
131
              memory_client.findZone(Name("w.y.d.e.f")).zone_finder);
132 133 134 135

    // there's no exact match.  the result should be the longest match,
    // and the code should be PARTIALMATCH.
    EXPECT_EQ(result::PARTIALMATCH,
136
              memory_client.findZone(Name("j.g.h")).code);
137
    EXPECT_EQ(Name("g.h"),
138
              memory_client.findZone(Name("g.h")).zone_finder->getOrigin());
139 140

    EXPECT_EQ(result::PARTIALMATCH,
141
              memory_client.findZone(Name("z.i.g.h")).code);
142
    EXPECT_EQ(Name("i.g.h"),
143 144
              memory_client.findZone(Name("z.i.g.h")).zone_finder->
                  getOrigin());
145
}
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160
TEST_F(InMemoryClientTest, iterator) {
    // Just some preparations of data
    boost::shared_ptr<InMemoryZoneFinder>
        zone(new InMemoryZoneFinder(RRClass::IN(), Name("a")));
    RRsetPtr aRRsetA(new RRset(Name("a"), RRClass::IN(), RRType::A(),
                                  RRTTL(300)));
    aRRsetA->addRdata(rdata::in::A("192.0.2.1"));
    RRsetPtr aRRsetAAAA(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
                                  RRTTL(300)));
    aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::1"));
    aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::2"));
    RRsetPtr subRRsetA(new RRset(Name("sub.x.a"), RRClass::IN(), RRType::A(),
                                  RRTTL(300)));
    subRRsetA->addRdata(rdata::in::A("192.0.2.2"));
161
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(zone));
162 163
    // First, the zone is not there, so it should throw
    EXPECT_THROW(memory_client.getIterator(Name("b")), DataSourceError);
164 165
    // This zone is not there either, even when there's a zone containing this
    EXPECT_THROW(memory_client.getIterator(Name("x.a")), DataSourceError);
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
    // Now, an empty zone
    ZoneIteratorPtr iterator(memory_client.getIterator(Name("a")));
    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
    // It throws Unexpected when we are past the end
    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
    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)
    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());
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
TEST_F(InMemoryClientTest, iterator_separate_rrs) {
    // Exactly the same tests as for iterator, but now with separate_rrs = true
    // For the one that returns actual data, the AAAA should now be split up
    boost::shared_ptr<InMemoryZoneFinder>
        zone(new InMemoryZoneFinder(RRClass::IN(), Name("a")));
    RRsetPtr aRRsetA(new RRset(Name("a"), RRClass::IN(), RRType::A(),
                                  RRTTL(300)));
    aRRsetA->addRdata(rdata::in::A("192.0.2.1"));
    RRsetPtr aRRsetAAAA(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
                                  RRTTL(300)));
    aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::1"));
    aRRsetAAAA->addRdata(rdata::in::AAAA("2001:db8::2"));
    RRsetPtr aRRsetAAAA_r1(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
                                  RRTTL(300)));
    aRRsetAAAA_r1->addRdata(rdata::in::AAAA("2001:db8::1"));
    RRsetPtr aRRsetAAAA_r2(new RRset(Name("a"), RRClass::IN(), RRType::AAAA(),
                                  RRTTL(300)));
    aRRsetAAAA_r2->addRdata(rdata::in::AAAA("2001:db8::2"));

    RRsetPtr subRRsetA(new RRset(Name("sub.x.a"), RRClass::IN(), RRType::A(),
                                  RRTTL(300)));
    subRRsetA->addRdata(rdata::in::A("192.0.2.2"));
    EXPECT_EQ(result::SUCCESS, memory_client.addZone(zone));

    // First, the zone is not there, so it should throw
    EXPECT_THROW(memory_client.getIterator(Name("b"), true), DataSourceError);
    // This zone is not there either, even when there's a zone containing this
    EXPECT_THROW(memory_client.getIterator(Name("x.a")), DataSourceError);
    // Now, an empty zone
    ZoneIteratorPtr iterator(memory_client.getIterator(Name("a"), true));
    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
    // It throws Unexpected when we are past the end
    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);

    ASSERT_EQ(result::SUCCESS, zone->add(aRRsetA));
    ASSERT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
    ASSERT_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)
    iterator = memory_client.getIterator(Name("a"), true);
    EXPECT_EQ(aRRsetA->toText(), iterator->getNextRRset()->toText());
    EXPECT_EQ(aRRsetAAAA_r1->toText(), iterator->getNextRRset()->toText());
    EXPECT_EQ(aRRsetAAAA_r2->toText(), iterator->getNextRRset()->toText());
    EXPECT_EQ(subRRsetA->toText(), iterator->getNextRRset()->toText());
    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
}

232 233 234
TEST_F(InMemoryClientTest, getZoneCount) {
    EXPECT_EQ(0, memory_client.getZoneCount());
    memory_client.addZone(
235 236
                  ZoneFinderPtr(new InMemoryZoneFinder(rrclass,
                                                       Name("example.com"))));
237
    EXPECT_EQ(1, memory_client.getZoneCount());
238 239

    // duplicate add.  counter shouldn't change
240
    memory_client.addZone(
241 242
                  ZoneFinderPtr(new InMemoryZoneFinder(rrclass,
                                                       Name("example.com"))));
243
    EXPECT_EQ(1, memory_client.getZoneCount());
244 245

    // add one more
246
    memory_client.addZone(
247 248
                  ZoneFinderPtr(new InMemoryZoneFinder(rrclass,
                                                       Name("example.org"))));
249
    EXPECT_EQ(2, memory_client.getZoneCount());
250
}
251

252
TEST_F(InMemoryClientTest, startUpdateZone) {
253
    EXPECT_THROW(memory_client.getUpdater(Name("example.org"), false),
254
                 isc::NotImplemented);
255 256
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270
// Commonly used RRSIG data
const char* const rrsig_a_txt =
    "example.org. 300 IN RRSIG A 5 3 3600 20000101000000 20000201000000 12345 "
    "example.org. FAKEFAKEFAKE\n";
const char* const rrsig_ns_txt =
    "example.org. 300 IN RRSIG NS 5 3 3600 20000101000000 20000201000000 "
    "54321 example.org. FAKEFAKEFAKEFAKE\n";
// This RRset has two RRSIGs
const char* const rrsig_aaaa_txt =
    "ns.example.org. 300 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
    "12345 example.org. FAKEFAKEFAKE\n"
    "ns.example.org. 300 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
    "54321 example.org. FAKEFAKEFAKEFAKE\n";

271
// A helper callback of masterLoad() used in InMemoryZoneFinderTest.
272 273 274 275 276 277
void
setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
    *(*it) = rrset;
    ++it;
}

278 279
/// \brief Test fixture for the InMemoryZoneFinder class
class InMemoryZoneFinderTest : public ::testing::Test {
280 281 282 283 284 285
    // A straightforward pair of textual RR(set) and a RRsetPtr variable
    // to store the RRset.  Used to build test data below.
    struct RRsetData {
        const char* const text; // textual representation of an RRset
        RRsetPtr* rrset;
    };
286
public:
287
    InMemoryZoneFinderTest() :
288 289
        class_(RRClass::IN()),
        origin_("example.org"),
290
        zone_finder_(class_, origin_)
Michal Vaner's avatar
Michal Vaner committed
291
    {
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
        // Build test RRsets.  Below, we construct an RRset for
        // each textual RR(s) of zone_data, and assign it to the corresponding
        // rr_xxx.
        const RRsetData zone_data[] = {
            {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
            {"example.org. 300 IN A 192.0.2.1", &rr_a_},
            {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
            {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
            {"cname.example.org. 300 IN CNAME canonical.example.org",
             &rr_cname_},
            {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
            {"dname.example.org. 300 IN DNAME target.example.org.",
             &rr_dname_},
            {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
            {"dname.example.org. 300 IN NS ns.dname.example.org.",
             &rr_dname_ns_},
            {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
            {"child.example.org. 300 IN NS ns.child.example.org.",
             &rr_child_ns_},
            {"ns.child.example.org. 300 IN A 192.0.2.153",
             &rr_child_glue_},
            {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
             &rr_grandchild_ns_},
            {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
             &rr_grandchild_glue_},
            {"dname.child.example.org. 300 IN DNAME example.com.",
             &rr_child_dname_},
319
            {"example.com. 300 IN A 192.0.2.10", &rr_out_},
320
            {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
321
            {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
322 323 324 325 326 327
            {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
            {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
             &rr_nested_emptywild_},
            {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
            {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
             &rr_dnamewild_},
328
            {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
329
            {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
330 331
            {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
             &rr_not_wild_another_},
332
            {NULL, NULL}
333 334 335 336 337 338 339 340 341 342
        };

        stringstream zone_data_stream;
        vector<RRsetPtr*> rrsets;
        for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
            zone_data_stream << zone_data[i].text << "\n";
            rrsets.push_back(zone_data[i].rrset);
        }

        masterLoad(zone_data_stream, Name::ROOT_NAME(), class_,
343
                   boost::bind(setRRset, _1, rrsets.begin()));
Michal Vaner's avatar
Michal Vaner committed
344
    }
345
    // Some data to test with
346
    const RRClass class_;
347
    const Name origin_;
348
    // The zone finder to torture by tests
349
    InMemoryZoneFinder zone_finder_;
Michal Vaner's avatar
Michal Vaner committed
350

351 352 353
    // Placeholder for storing RRsets to be checked with rrsetsCheck()
    vector<ConstRRsetPtr> actual_rrsets_;

Michal Vaner's avatar
Michal Vaner committed
354 355 356
    /*
     * Some RRsets to put inside the zone.
     */
357
    RRsetPtr
Michal Vaner's avatar
Michal Vaner committed
358 359 360 361 362 363 364 365 366
        // Out of zone RRset
        rr_out_,
        // NS of example.org
        rr_ns_,
        // A of ns.example.org
        rr_ns_a_,
        // AAAA of ns.example.org
        rr_ns_aaaa_,
        // A of example.org
367
        rr_a_;
368
    RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
369
    RRsetPtr rr_cname_a_; // for mixed CNAME + A case
370
    RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
371 372 373 374 375 376 377 378
    RRsetPtr rr_dname_a_; // for mixed DNAME + A case
    RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
    RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
    RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
    RRsetPtr rr_child_glue_; // glue RR of the child domain
    RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
    RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
    RRsetPtr rr_child_dname_; // A DNAME under NS
379 380 381 382
    RRsetPtr rr_wild_;
    RRsetPtr rr_emptywild_;
    RRsetPtr rr_nested_emptywild_;
    RRsetPtr rr_nswild_, rr_dnamewild_;
383
    RRsetPtr rr_child_wild_;
384
    RRsetPtr rr_under_wild_;
385
    RRsetPtr rr_not_wild_;
386
    RRsetPtr rr_not_wild_another_;
Michal Vaner's avatar
Michal Vaner committed
387 388

    /**
389
     * \brief Test one find query to the zone finder.
Michal Vaner's avatar
Michal Vaner committed
390
     *
391
     * Asks a query to the zone finder and checks it does not throw and returns
Michal Vaner's avatar
Michal Vaner committed
392 393 394 395 396 397
     * expected results. It returns nothing, it just signals failures
     * to GTEST.
     *
     * \param name The name to ask for.
     * \param rrtype The RRType to ask of.
     * \param result The expected code of the result.
Michal Vaner's avatar
Michal Vaner committed
398 399
     * \param check_answer Should a check against equality of the answer be
     *     done?
Michal Vaner's avatar
Michal Vaner committed
400
     * \param answer The expected rrset, if any should be returned.
401 402
     * \param zone_finder Check different InMemoryZoneFinder object than
     *     zone_finder_ (if NULL, uses zone_finder_)
403 404 405 406
     * \param check_wild_answer Checks that the answer has the same RRs, type
     *     class and TTL as the eqxpected answer and that the name corresponds
     *     to the one searched. It is meant for checking answers for wildcard
     *     queries.
Michal Vaner's avatar
Michal Vaner committed
407
     */
408 409
    void findTest(const Name& name, const RRType& rrtype,
                  ZoneFinder::Result result,
410 411
                  bool check_answer = true,
                  const ConstRRsetPtr& answer = ConstRRsetPtr(),
412
                  InMemoryZoneFinder* zone_finder = NULL,
413
                  ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
414
                  bool check_wild_answer = false)
Michal Vaner's avatar
Michal Vaner committed
415
    {
416 417
        if (zone_finder == NULL) {
            zone_finder = &zone_finder_;
Michal Vaner's avatar
Michal Vaner committed
418
        }
Michal Vaner's avatar
Michal Vaner committed
419 420 421
        // The whole block is inside, because we need to check the result and
        // we can't assign to FindResult
        EXPECT_NO_THROW({
422 423
                ZoneFinder::FindResult find_result(zone_finder->find(
                                                       name, rrtype,
424
                                                       options));
425 426 427 428
                // Check it returns correct answers
                EXPECT_EQ(result, find_result.code);
                if (check_answer) {
                    EXPECT_EQ(answer, find_result.rrset);
429
                } else if (check_wild_answer) {
430
                    ASSERT_NE(ConstRRsetPtr(), answer) <<
431
                        "Wrong test, don't check for wild names if you expect "
432 433 434
                        "empty answer";
                    ASSERT_NE(ConstRRsetPtr(), find_result.rrset) <<
                        "No answer found";
435
                    RdataIteratorPtr expectedIt(answer->getRdataIterator());
436 437 438
                    RdataIteratorPtr actualIt(
                        find_result.rrset->getRdataIterator());
                    while (!expectedIt->isLast() && !actualIt->isLast()) {
439
                        EXPECT_EQ(0, expectedIt->getCurrent().compare(
440
                            actualIt->getCurrent())) << "The RRs differ ('" <<
441
                            expectedIt->getCurrent().toText() << "', '" <<
442
                            actualIt->getCurrent().toText() << "')";
443
                        expectedIt->next();
444
                        actualIt->next();
445 446 447
                    }
                    EXPECT_TRUE(expectedIt->isLast()) <<
                        "Result has less RRs than expected";
448
                    EXPECT_TRUE(actualIt->isLast()) <<
449
                        "Result has more RRs than expected";
450 451
                    EXPECT_EQ(answer->getClass(),
                        find_result.rrset->getClass());
452 453 454 455 456
                    EXPECT_EQ(answer->getType(),
                        find_result.rrset->getType());
                    EXPECT_EQ(answer->getTTL(),
                        find_result.rrset->getTTL());
                    EXPECT_EQ(name, find_result.rrset->getName());
457 458
                }
            });
Michal Vaner's avatar
Michal Vaner committed
459
    }
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
    /**
     * \brief Calls the findAll on the finder and checks the result.
     */
    std::vector<ConstRRsetPtr> findAllTest(const Name& name,
                                           ZoneFinder::Result result,
                                           size_t expected_size,
                                           InMemoryZoneFinder* finder = NULL,
                                           const ConstRRsetPtr &rrset_result =
                                           ConstRRsetPtr(),
                                           ZoneFinder::FindOptions options =
                                           ZoneFinder::FIND_DEFAULT)
    {
        if (finder == NULL) {
            finder = &zone_finder_;
        }
        std::vector<ConstRRsetPtr> target;
        ZoneFinder::FindResult findResult(finder->findAll(name, target,
                                                          options));
        EXPECT_EQ(result, findResult.code);
        EXPECT_EQ(rrset_result, findResult.rrset);
        BOOST_FOREACH(const ConstRRsetPtr& rrset, target) {
            EXPECT_EQ(name, rrset->getName());
        }
        EXPECT_EQ(expected_size, target.size());
        return (target);
    }
486
    // Internal part of the cancelWildcard test that is multiple times
487
    void doCancelWildcardTest();
488 489 490 491 492 493 494 495 496 497 498 499

    ConstRRsetPtr textToRRset(const string& text_rrset,
                              const RRClass& rrclass = RRClass::IN()) const
    {
        stringstream ss(text_rrset);
        RRsetPtr rrset;
        vector<RRsetPtr*> rrsets;
        rrsets.push_back(&rrset);
        masterLoad(ss, Name::ROOT_NAME(), rrclass,
                   boost::bind(setRRset, _1, rrsets.begin()));
        return (rrset);
    }
500 501
};

502 503 504 505 506 507 508 509
/**
 * \brief Check that findPreviousName throws as it should now.
 */
TEST_F(InMemoryZoneFinderTest, findPreviousName) {
    EXPECT_THROW(zone_finder_.findPreviousName(Name("www.example.org")),
                 isc::NotImplemented);
}

510
/**
511
 * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
512
 *
513
 * Takes the created zone finder and checks its properties they are the same
514 515
 * as passed parameters.
 */
516
TEST_F(InMemoryZoneFinderTest, constructor) {
517 518
    ASSERT_EQ(class_, zone_finder_.getClass());
    ASSERT_EQ(origin_, zone_finder_.getOrigin());
519
}
Michal Vaner's avatar
Michal Vaner committed
520 521 522 523 524 525
/**
 * \brief Test adding.
 *
 * We test that it throws at the correct moments and the correct exceptions.
 * And we test the return value.
 */
526
TEST_F(InMemoryZoneFinderTest, add) {
Michal Vaner's avatar
Michal Vaner committed
527
    // This one does not belong to this zone
528
    EXPECT_THROW(zone_finder_.add(rr_out_), InMemoryZoneFinder::OutOfZone);
Michal Vaner's avatar
Michal Vaner committed
529
    // Test null pointer
530
    EXPECT_THROW(zone_finder_.add(ConstRRsetPtr()),
531
                 InMemoryZoneFinder::NullRRset);
Michal Vaner's avatar
Michal Vaner committed
532 533

    // Now put all the data we have there. It should throw nothing
534 535 536 537
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
Michal Vaner's avatar
Michal Vaner committed
538 539

    // Try putting there something twice, it should be rejected
540 541
    EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_)));
    EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_finder_.add(rr_ns_a_)));
Michal Vaner's avatar
Michal Vaner committed
542 543
}

544
TEST_F(InMemoryZoneFinderTest, addMultipleCNAMEs) {
545
    rr_cname_->addRdata(generic::CNAME("canonical2.example.org."));
546
    EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError);
547 548
}

549
TEST_F(InMemoryZoneFinderTest, addCNAMEThenOther) {
550
    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
551
    EXPECT_THROW(zone_finder_.add(rr_cname_a_), InMemoryZoneFinder::AddError);
552 553
}

554
TEST_F(InMemoryZoneFinderTest, addOtherThenCNAME) {
555
    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_a_));
556
    EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError);
557 558
}

559
TEST_F(InMemoryZoneFinderTest, findCNAME) {
560
    // install CNAME RR
561
    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
562 563

    // Find A RR of the same.  Should match the CNAME
564 565
    findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
             rr_cname_);
566 567

    // Find the CNAME itself.  Should result in normal SUCCESS
568
    findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
569
             rr_cname_);
570 571
}

572
TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
573 574 575
    // There's nothing special when we find a CNAME under a zone cut
    // (with FIND_GLUE_OK).  The behavior is different from BIND 9,
    // so we test this case explicitly.
576
    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_));
577 578
    ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
        "cname.child.example.org. 300 IN CNAME target.child.example.org.");
579
    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_under_cut_));
580
    findTest(Name("cname.child.example.org"), RRType::AAAA(),
581
             ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL,
582
             ZoneFinder::FIND_GLUE_OK);
583 584
}

585 586 587
// Two DNAMEs at single domain are disallowed by RFC 2672, section 3)
// Having a CNAME there is disallowed too, but it is tested by
// addOtherThenCNAME and addCNAMEThenOther.
588
TEST_F(InMemoryZoneFinderTest, addMultipleDNAMEs) {
589
    rr_dname_->addRdata(generic::DNAME("target2.example.org."));
590
    EXPECT_THROW(zone_finder_.add(rr_dname_), InMemoryZoneFinder::AddError);
591 592 593 594 595 596
}

/*
 * These two tests ensure that we can't have DNAME and NS at the same
 * node with the exception of the apex of zone (forbidden by RFC 2672)
 */
597
TEST_F(InMemoryZoneFinderTest, addDNAMEThenNS) {
598
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_)));
599
    EXPECT_THROW(zone_finder_.add(rr_dname_ns_), InMemoryZoneFinder::AddError);
600 601
}

602
TEST_F(InMemoryZoneFinderTest, addNSThenDNAME) {
603
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_ns_)));
604
    EXPECT_THROW(zone_finder_.add(rr_dname_), InMemoryZoneFinder::AddError);
605 606 607
}

// It is allowed to have NS and DNAME at apex
608
TEST_F(InMemoryZoneFinderTest, DNAMEAndNSAtApex) {
609 610
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
611 612 613

    // The NS should be possible to be found, below should be DNAME, not
    // delegation
614 615
    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
    findTest(rr_child_ns_->getName(), RRType::A(), ZoneFinder::DNAME, true,
616
             rr_dname_apex_);
617 618
}

619
TEST_F(InMemoryZoneFinderTest, NSAndDNAMEAtApex) {
620 621
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_apex_)));
622 623 624 625 626 627
}

// TODO: Test (and implement) adding data under DNAME. That is forbidden by
// 2672 as well.

// Search under a DNAME record. It should return the DNAME
628
TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
629 630 631
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_)));
    findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
             true, rr_dname_);
632 633 634 635
}

// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
// influences only the data below (see RFC 2672, section 3)
636
TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
637 638
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_dname_a_)));
639

640
    const Name dname_name(rr_dname_->getName());
641 642 643 644
    findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
    findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
             rr_dname_);
    findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
645 646
}

647 648
// Try searching something that is both under NS and DNAME, without and with
// GLUE_OK mode (it should stop at the NS and DNAME respectively).
649
TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
650 651
    zone_finder_.add(rr_child_ns_);
    zone_finder_.add(rr_child_dname_);
652 653 654

    Name lowName("below.dname.child.example.org.");

655 656
    findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
    findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
657
             NULL, ZoneFinder::FIND_GLUE_OK);
658 659
}

660
// Test adding child zones and zone cut handling
661
TEST_F(InMemoryZoneFinderTest, delegationNS) {
662
    // add in-zone data
663
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
664 665

    // install a zone cut
666
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
667 668

    // below the zone cut
669 670
    findTest(Name("www.child.example.org"), RRType::A(),
             ZoneFinder::DELEGATION, true, rr_child_ns_);
671 672

    // at the zone cut
673
    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
JINMEI Tatuya's avatar
JINMEI Tatuya committed
674
             true, rr_child_ns_);
675
    findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
JINMEI Tatuya's avatar
JINMEI Tatuya committed
676
             true, rr_child_ns_);
677

678 679
    // finding NS for the apex (origin) node.  This must not be confused
    // with delegation due to the existence of an NS RR.
680
    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
681 682

    // unusual case of "nested delegation": the highest cut should be used.
683
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_ns_)));
684
    findTest(Name("www.grand.child.example.org"), RRType::A(),
685 686
             // note: !rr_grandchild_ns_
             ZoneFinder::DELEGATION, true, rr_child_ns_);
687 688
}

689
TEST_F(InMemoryZoneFinderTest, findAny) {
690 691 692
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
chenzhengzhang's avatar
chenzhengzhang committed
693 694

    // origin
695 696 697 698 699 700
    std::vector<ConstRRsetPtr> rrsets(findAllTest(origin_, ZoneFinder::SUCCESS,
                                                  2));
    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
                                           rr_a_));
    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
                                           rr_ns_));
chenzhengzhang's avatar
chenzhengzhang committed
701 702

    // out zone name
703 704 705 706 707
    findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN, 0);

    rrsets = findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, 1);
    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
                                           rr_child_glue_));
chenzhengzhang's avatar
chenzhengzhang committed
708 709 710 711 712

    // TODO: test NXRRSET case after rbtree non-terminal logic has
    // been implemented

    // add zone cut
713
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
chenzhengzhang's avatar
chenzhengzhang committed
714 715

    // zone cut
716 717
    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION, 0, NULL,
                rr_child_ns_);
chenzhengzhang's avatar
chenzhengzhang committed
718 719

    // glue for this zone cut
720 721
    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION, 0, NULL,
                rr_child_ns_);
chenzhengzhang's avatar
chenzhengzhang committed
722 723
}

724
TEST_F(InMemoryZoneFinderTest, glue) {
725 726
    // install zone data:
    // a zone cut
727
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
728
    // glue for this cut
729
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
730
    // a nested zone cut (unusual)
731
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_ns_)));
732
    // glue under the deeper zone cut
733
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_grandchild_glue_)));
734 735

    // by default glue is hidden due to the zone cut
736 737
    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
             true, rr_child_ns_);
738 739 740


    // If we do it in the "glue OK" mode, we should find the exact match.
741
    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
742
             rr_child_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
743 744

    // glue OK + NXRRSET case
745
    findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
746
             true, ConstRRsetPtr(), NULL, ZoneFinder::FIND_GLUE_OK);
747 748

    // glue OK + NXDOMAIN case
749
    findTest(Name("www.child.example.org"), RRType::A(),
750
             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
751
             ZoneFinder::FIND_GLUE_OK);
752 753

    // nested cut case.  The glue should be found.
754
    findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
755
             ZoneFinder::SUCCESS,
756
             true, rr_grandchild_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
757 758 759 760

    // A non-existent name in nested cut.  This should result in delegation
    // at the highest zone cut.
    findTest(Name("www.grand.child.example.org"), RRType::TXT(),
761
             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
762
             ZoneFinder::FIND_GLUE_OK);
763 764
}

Michal Vaner's avatar
Michal Vaner committed
765 766 767 768 769 770 771
/**
 * \brief Test searching.
 *
 * Check it finds or does not find correctly and does not throw exceptions.
 * \todo This doesn't do any kind of CNAME and so on. If it isn't
 *     directly there, it just tells it doesn't exist.
 */
772
TEST_F(InMemoryZoneFinderTest, find) {
Michal Vaner's avatar
Michal Vaner committed
773 774
    // Fill some data inside
    // Now put all the data we have there. It should throw nothing
775 776 777 778
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_)));
    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
Michal Vaner's avatar
Michal Vaner committed
779 780

    // These two should be successful
781 782 783
    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
    findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
             rr_ns_a_);
Michal Vaner's avatar
Michal Vaner committed
784 785

    // These domain exist but don't have the provided RRType
786 787
    findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET);
    findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET);
Michal Vaner's avatar
Michal Vaner committed
788 789

    // These domains don't exist (and one is out of the zone)
790 791
    findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN);
    findTest(Name("example.net"), RRType::A(), ZoneFinder::NXDOMAIN);
Michal Vaner's avatar
Michal Vaner committed
792 793
}

794
TEST_F(InMemoryZoneFinderTest, emptyNode) {
795 796 797 798 799 800
    /*
     * The backend RBTree for this test should look like as follows:
     *          example.org
     *               |
     *              baz (empty; easy case)
     *            /  |  \
JINMEI Tatuya's avatar
JINMEI Tatuya committed
801
     *          bar  |  x.foo ('foo' part is empty; a bit trickier)
802 803 804 805 806 807 808
     *              bbb
     *             /
     *           aaa
     */

    // Construct the test zone
    const char* const names[] = {
809
        "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
810 811
        "bbb.baz.example.org.", NULL};
    for (int i = 0; names[i] != NULL; ++i) {
812 813
        ConstRRsetPtr rrset = textToRRset(string(names[i]) +
                                          " 300 IN A 192.0.2.1");
JINMEI Tatuya's avatar