zone_table_unittest.cc 8.44 KB
Newer Older
1
// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//
// 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 <exceptions/exceptions.h>

#include <util/memory_segment_local.h>

#include <dns/name.h>
#include <dns/rrclass.h>

22
23
24
#include <datasrc/result.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/zone_table.h>
25
#include <datasrc/memory/segment_object_holder.h>
26
27
28

#include <gtest/gtest.h>

29
30
#include <new>                  // for bad_alloc

31
32
using namespace isc::dns;
using namespace isc::datasrc;
33
using namespace isc::datasrc::memory;
34
using namespace isc::datasrc::memory::detail;
35
36

namespace {
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Memory segment specified for tests.  It normally behaves like a "local"
// memory segment.  If "throw count" is set to non 0 via setThrowCount(),
// it continues the normal behavior up to the specified number of calls to
// allocate(), and throws an exception at the next call.
class TestMemorySegment : public isc::util::MemorySegmentLocal {
public:
    TestMemorySegment() : throw_count_(0) {}
    virtual void* allocate(size_t size) {
        if (throw_count_ > 0) {
            if (--throw_count_ == 0) {
                throw std::bad_alloc();
            }
        }
        return (isc::util::MemorySegmentLocal::allocate(size));
    }
    void setThrowCount(size_t count) { throw_count_ = count; }

private:
    size_t throw_count_;
};

58
59
class ZoneTableTest : public ::testing::Test {
protected:
60
61
    ZoneTableTest() : zclass_(RRClass::IN()),
                      zname1(Name("example.com")),
62
63
                      zname2(Name("example.net")),
                      zname3(Name("example")),
64
                      zone_table(ZoneTable::create(mem_sgmt_, zclass_))
65
66
    {}
    ~ZoneTableTest() {
67
        if (zone_table != NULL) {
68
            ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
69
70
71
        }
    }
    void TearDown() {
72
        ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
73
        zone_table = NULL;
74
        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
75
    }
76
    const RRClass zclass_;
77
    const Name zname1, zname2, zname3;
78
    TestMemorySegment mem_sgmt_;
79
80
81
    ZoneTable* zone_table;
};

82
83
84
85
86
TEST_F(ZoneTableTest, create) {
    // Test about creating a zone table.  Normal case covers through other
    // tests.  We only check exception safety by letting the test memory
    // segment throw.
    mem_sgmt_.setThrowCount(2);
87
    EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
88
89
90
    // This shouldn't cause memory leak (that would be caught in TearDown()).
}

91
TEST_F(ZoneTableTest, addZone) {
92
93
94
95
    // It doesn't accept empty (NULL) zones
    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, zname1, NULL),
                 isc::BadValue);

96
97
    SegmentObjectHolder<ZoneData, RRClass> holder1(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
98
    const ZoneData* data1(holder1.get());
99
    // Normal successful case.
100
101
102
103
    const ZoneTable::AddResult result1(zone_table->addZone(mem_sgmt_, zclass_,
                                                           zname1,
                                                           holder1.release()));
    EXPECT_EQ(result::SUCCESS, result1.code);
104
    EXPECT_EQ(static_cast<const ZoneData*>(NULL), result1.zone_data);
105
    // It got released by it
106
    EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder1.get());
107
108

    // Duplicate add doesn't replace the existing data.
109
110
    SegmentObjectHolder<ZoneData, RRClass> holder2(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
111
112
113
114
115
116
    const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_,
                                                           zname1,
                                                           holder2.release()));
    EXPECT_EQ(result::EXIST, result2.code);
    // The old one gets out
    EXPECT_EQ(data1, result2.zone_data);
117
    // It releases this one even when we replace the old zone
118
    EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder2.get());
119
120
    // We need to release the old one manually
    ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_);
121
122
123
124

    SegmentObjectHolder<ZoneData, RRClass> holder3(
        mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")),
                                    zclass_);
125
    // names are compared in a case insensitive manner.
126
127
128
129
130
    const ZoneTable::AddResult result3(zone_table->addZone(mem_sgmt_, zclass_,
                                                           Name("EXAMPLE.COM"),
                                                           holder3.release()));
    EXPECT_EQ(result::EXIST, result3.code);
    ZoneData::destroy(mem_sgmt_, result3.zone_data, zclass_);
131
    // Add some more different ones.  Should just succeed.
132
133
    SegmentObjectHolder<ZoneData, RRClass> holder4(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
134
    EXPECT_EQ(result::SUCCESS,
135
              zone_table->addZone(mem_sgmt_, zclass_, zname2,
136
                                  holder4.release()).code);
137
138
    SegmentObjectHolder<ZoneData, RRClass> holder5(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
139
    EXPECT_EQ(result::SUCCESS,
140
              zone_table->addZone(mem_sgmt_, zclass_, zname3,
141
                                  holder5.release()).code);
142
143
144
145

    // Have the memory segment throw an exception in extending the internal
    // tree.  It still shouldn't cause memory leak (which would be detected
    // in TearDown()).
146
147
148
149
    SegmentObjectHolder<ZoneData, RRClass> holder6(
        mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_);
    mem_sgmt_.setThrowCount(1);
    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"),
150
                                     holder6.release()),
151
                 std::bad_alloc);
152
153
}

154
TEST_F(ZoneTableTest, findZone) {
155
156
157
158
    SegmentObjectHolder<ZoneData, RRClass> holder1(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
    ZoneData* zone_data = holder1.get();
    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1,
159
                                                   holder1.release()).code);
160
161
    SegmentObjectHolder<ZoneData, RRClass> holder2(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
162
    EXPECT_EQ(result::SUCCESS,
163
              zone_table->addZone(mem_sgmt_, zclass_, zname2,
164
                                  holder2.release()).code);
165
166
    SegmentObjectHolder<ZoneData, RRClass> holder3(
        mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
167
    EXPECT_EQ(result::SUCCESS,
168
              zone_table->addZone(mem_sgmt_, zclass_, zname3,
169
                                  holder3.release()).code);
170

171
172
173
    const ZoneTable::FindResult find_result1 =
        zone_table->findZone(Name("example.com"));
    EXPECT_EQ(result::SUCCESS, find_result1.code);
174
    EXPECT_EQ(zone_data, find_result1.zone_data);
175
176
177

    EXPECT_EQ(result::NOTFOUND,
              zone_table->findZone(Name("example.org")).code);
178
179
    EXPECT_EQ(static_cast<ZoneData*>(NULL),
              zone_table->findZone(Name("example.org")).zone_data);
180
181
182
183
184

    // there's no exact match.  the result should be the longest match,
    // and the code should be PARTIALMATCH.
    EXPECT_EQ(result::PARTIALMATCH,
              zone_table->findZone(Name("www.example.com")).code);
185
    EXPECT_EQ(zone_data,
186
              zone_table->findZone(Name("www.example.com")).zone_data);
187
188
189

    // make sure the partial match is indeed the longest match by adding
    // a zone with a shorter origin and query again.
190
191
    SegmentObjectHolder<ZoneData, RRClass> holder4(
        mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_);
192
    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
193
                                                   Name("com"),
194
                                                   holder4.release()).code);
195
    EXPECT_EQ(zone_data,
196
              zone_table->findZone(Name("www.example.com")).zone_data);
197
198
}
}