1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <exceptions/exceptions.h>
#include <hooks/parking_lots.h>
#include <boost/weak_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <testutils/gtest_utils.h>
#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace isc;
using namespace isc::hooks;

namespace {

// Defines a pointer to a string. The tests use shared pointers
// as parked objects to ensure key matching works correctly with
// them.  We're doing this because real-world use parked objects
// are typically pointers to packets.
typedef boost::shared_ptr<std::string> StringPtr;

// Test that it is possible to create and retrieve parking lots for
// specified hook points.
TEST(ParkingLotsTest, createGetParkingLot) {
    ParkingLots parking_lots;

    ParkingLotPtr parking_lot0 = parking_lots.getParkingLotPtr(1);
    ParkingLotPtr parking_lot1 = parking_lots.getParkingLotPtr(2);
    ParkingLotPtr parking_lot2 = parking_lots.getParkingLotPtr(1);

    ASSERT_TRUE(parking_lot0);
    ASSERT_TRUE(parking_lot1);
    ASSERT_TRUE(parking_lot2);

    EXPECT_EQ(0, parking_lot0->size());
    EXPECT_EQ(0, parking_lot1->size());
    EXPECT_EQ(0, parking_lot2->size());

    EXPECT_FALSE(parking_lot0 == parking_lot1);
    EXPECT_TRUE(parking_lot0 == parking_lot2);

    ASSERT_NO_THROW(parking_lots.clear());

    ParkingLotPtr parking_lot3 = parking_lots.getParkingLotPtr(1);
    ASSERT_TRUE(parking_lot3);

    EXPECT_FALSE(parking_lot3 == parking_lot0);
}

// Verify that an object can be parked.
TEST(ParkingLotsTest, park) {<--- syntax error
    ParkingLot parking_lot;

    // Create object to park.
    StringPtr parked_object(new std::string("foo"));

    // Verify that we can park an object that has not been parked.
    ASSERT_NO_THROW(parking_lot.park(parked_object, [] {}));

    EXPECT_EQ(1, parking_lot.size());

    // Verify that we cannot park an object that has been parked
    EXPECT_THROW(parking_lot.park(parked_object, [] {}),
                 InvalidOperation);
}

// Verify that an object can be referenced.
TEST(ParkingLotsTest, reference) {
    ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
    ParkingLotHandlePtr parking_lot_handle =
        boost::make_shared<ParkingLotHandle>(parking_lot);

    // Create an object.
    StringPtr parked_object(new std::string("foo"));

    // Cannot reference an object that has not been parked.
    ASSERT_THROW(parking_lot_handle->reference(parked_object),
                 InvalidOperation);

    // Park the object.
    ASSERT_NO_THROW(parking_lot->park(parked_object, [] {}));

    EXPECT_EQ(1, parking_lot->size());

    // Reference the object. Reference count should one.
    int ref_count = 0;
    ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
    ASSERT_EQ(1, ref_count);

    // Reference the object again. Reference count should two.
    ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
    ASSERT_EQ(2, ref_count);

    EXPECT_EQ(1, parking_lot->size());
}

// Test that object can be parked and then unparked.
TEST(ParkingLotsTest, unpark) {
    ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
    ParkingLotHandlePtr parking_lot_handle =
        boost::make_shared<ParkingLotHandle>(parking_lot);

    StringPtr parked_object(new std::string("foo"));

    // Unparking should return false if the object isn't parked.
    EXPECT_FALSE(parking_lot->unpark(parked_object));

    EXPECT_EQ(0, parking_lot->size());

    // This flag will indicate if the callback has been called.
    bool unparked = false;

    ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
        unparked = true;
    }));

    EXPECT_EQ(1, parking_lot->size());

    // Reference the parked object twice because we're going to test that
    // reference counting works fine.
    ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
    ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));

    EXPECT_EQ(1, parking_lot->size());

    // Try to unpark the object. It should decrease the reference count, but not
    // unpark the packet yet.
    EXPECT_TRUE(parking_lot_handle->unpark(parked_object));
    EXPECT_FALSE(unparked);

    EXPECT_EQ(1, parking_lot->size());

    // Try to unpark the object. This time it should be successful, because the
    // reference count goes to 0.
    EXPECT_TRUE(parking_lot_handle->unpark(parked_object));
    EXPECT_TRUE(unparked);

    EXPECT_EQ(0, parking_lot->size());

    // Calling unpark again should return false to indicate that the object is
    // not parked.
    EXPECT_FALSE(parking_lot_handle->unpark(parked_object));

    EXPECT_EQ(0, parking_lot->size());
}

// Test that parked object can be dropped.
TEST(ParkingLotsTest, drop) {
    ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
    ParkingLotHandlePtr parking_lot_handle =
        boost::make_shared<ParkingLotHandle>(parking_lot);

    StringPtr parked_object(new std::string("foo"));

    // This flag will indicate if the callback has been called.
    bool unparked = false;
    ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
        unparked = true;
    }));

    EXPECT_EQ(1, parking_lot->size());

    // Reference object twice to test that dropping the packet ignores
    // reference counting.
    ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
    ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));

    // Drop parked object. The callback should not be invoked.
    EXPECT_TRUE(parking_lot_handle->drop(parked_object));
    EXPECT_FALSE(unparked);

    EXPECT_EQ(0, parking_lot->size());

    // Expect that an attempt to unpark return false, as the object
    // has been dropped.
    EXPECT_FALSE(parking_lot_handle->unpark(parked_object));
}

// Test that parked lots can be cleared.
TEST(ParkingLotsTest, clear) {
    ParkingLotsPtr parking_lots = boost::make_shared<ParkingLots>();
    ParkingLotPtr parking_lot = parking_lots->getParkingLotPtr(1234);
    ASSERT_TRUE(parking_lot);
    ParkingLotHandlePtr parking_lot_handle =
        boost::make_shared<ParkingLotHandle>(parking_lot);

    boost::shared_ptr<std::string> parked_object =
        boost::make_shared<std::string>("foo");
    boost::weak_ptr<std::string> weak_parked_object(parked_object);

    // This flag will indicate if the callback has been called.
    bool unparked = false;
    ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
        unparked = true;
    }));

    // Reference object twice to test that clearing the parking lots
    // ignores reference counting.
    ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
    ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));

    // Drop reference on objects.
    parking_lot.reset();
    parking_lot_handle.reset();
    parked_object.reset();

    // The parked object is still alive.
    EXPECT_FALSE(weak_parked_object.expired());

    // Clear the parking lots.
    ASSERT_NO_THROW(parking_lots->clear());

    // The callback should not be invoked.
    EXPECT_FALSE(unparked);

    // The parked object was destroyed.
    EXPECT_TRUE(weak_parked_object.expired());
}

// Verify that an object can be dereferenced.
TEST(ParkingLotsTest, dereference) {
    ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
    ParkingLotHandlePtr parking_lot_handle =
        boost::make_shared<ParkingLotHandle>(parking_lot);

    // Create an object.
    StringPtr parked_object(new std::string("foo"));

    // Verify that an object that hasn't been parked, cannot be
    // dereferenced.
    ASSERT_THROW(parking_lot_handle->dereference(parked_object),
                 InvalidOperation);

    // Park the object.
    // This flag will indicate if the callback has been called.
    bool unparked = false;
    ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
        unparked = true;
    }));

    EXPECT_EQ(1, parking_lot->size());

    // Reference the parked object twice.
    int ref_count = 0;
    ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
    ASSERT_EQ(1, ref_count);
    ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
    ASSERT_EQ(2, ref_count);

    EXPECT_EQ(1, parking_lot->size());

    // Try to dereference the object. It should decrease the reference count,
    // but not unpark the packet or invoke the callback.
    ASSERT_NO_THROW(ref_count = parking_lot_handle->dereference(parked_object));
    ASSERT_EQ(1, ref_count);
    EXPECT_FALSE(unparked);

    EXPECT_EQ(1, parking_lot->size());

    // Try to dereference the object. It should decrease the reference count,
    // but not unpark the packet or invoke the callback.
    ASSERT_NO_THROW(ref_count = parking_lot_handle->dereference(parked_object));
    ASSERT_EQ(0, ref_count);
    EXPECT_FALSE(unparked);

    EXPECT_EQ(1, parking_lot->size());

    // Try to dereference the object. It should decrement to -1
    // but not unpark the packet or invoke the callback.
    ASSERT_NO_THROW(ref_count = parking_lot_handle->dereference(parked_object));
    EXPECT_EQ(-1, ref_count);
    EXPECT_FALSE(unparked);

    EXPECT_EQ(1, parking_lot->size());

    // Calling unpark should invoke the callback.
    ASSERT_TRUE(parking_lot_handle->unpark(parked_object));
    EXPECT_TRUE(unparked);

    EXPECT_EQ(0, parking_lot->size());
}

// Verify that parked objects are correctly distinguished from
// one another.
TEST(ParkingLotsTest, multipleObjects) {
    ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
    ParkingLotHandlePtr parking_lot_handle =
        boost::make_shared<ParkingLotHandle>(parking_lot);

    // Create an object and park it.
    StringPtr object_one(new std::string("one"));
    int unparked_one = 0;
    ASSERT_NO_THROW(parking_lot->park(object_one, [&unparked_one] {
        ++unparked_one;
    }));

    // Create a second object and park it.
    StringPtr object_two(new std::string("two"));
    int unparked_two = 0;
    ASSERT_NO_THROW(parking_lot->park(object_two, [&unparked_two] {
        ++unparked_two;
    }));

    EXPECT_EQ(2, parking_lot->size());

    // Create a third object but don't park it.
    StringPtr object_three(new std::string("three"));

    // Try to unpark object_three. It should fail, and no callbacks
    // should get invoked.
    EXPECT_FALSE(parking_lot_handle->unpark(object_three));
    EXPECT_EQ(unparked_one, 0);
    EXPECT_EQ(unparked_two, 0);

    EXPECT_EQ(2, parking_lot->size());

    // Unpark object one.  It should succeed and its callback should
    // get invoked.
    EXPECT_TRUE(parking_lot_handle->unpark(object_one));
    EXPECT_EQ(unparked_one, 1);
    EXPECT_EQ(unparked_two, 0);

    EXPECT_EQ(1, parking_lot->size());

    // Unpark object two.  It should succeed and its callback should
    // get invoked.
    EXPECT_TRUE(parking_lot_handle->unpark(object_two));
    EXPECT_EQ(unparked_one, 1);
    EXPECT_EQ(unparked_two, 1);

    EXPECT_EQ(0, parking_lot->size());
}

}